# Development Workflow

[← Back to Index](../CLAUDE.md)

---

## Initial Setup

### Prerequisites

- **Node.js** v22+ (recommended - check package.json engines field)
- **npm** (comes with Node.js)
- **Git**

### Clone and Install

```bash
# Clone repository
git clone https://github.com/chrisvel/tududi.git
cd tududi

# Install all dependencies
# This installs both frontend and backend dependencies (monorepo setup)
npm install
```

### Initialize Database

```bash
# Create database and run all migrations
npm run db:init

# This command:
# 1. Creates /backend/database.sqlite
# 2. Runs all migrations from /backend/migrations/
# 3. Sets up tables and relationships
```

### Create Test User (Optional)

```bash
npm run user:create

# Interactive prompts:
# - Email address
# - Password
# - Timezone (defaults to system timezone)
```

---

## Daily Development

Tududi runs two separate processes during development:

### Two-Server Development

**Terminal 1 - Backend (Express):**
```bash
npm run backend:dev

# Details:
# - Runs: nodemon backend/app.js
# - Server: http://localhost:3002
# - Auto-reloads: Yes (on file changes)
# - API endpoints: /api/v1/*
# - Swagger docs: /api-docs (after login)
```

**Terminal 2 - Frontend (Webpack Dev Server):**
```bash
npm run frontend:dev

# Details:
# - Runs: webpack serve --mode development
# - Server: http://localhost:8080
# - Hot reload: Yes (React Fast Refresh)
# - Proxies /api/* to backend:3002
# - Proxies /locales/* to backend:3002
```

**Or run both simultaneously:**
```bash
npm start

# Runs both backend:dev and frontend:dev in parallel
# Uses 'concurrently' package
# Logs from both processes interleaved
```

### Accessing the Application

Open http://localhost:8080 in your browser.

**Login with:**
- Email: The email you set during db:init or user:create
- Password: The password you set

---

## Environment Variables

Create `/backend/.env` file (not tracked in git):

```bash
# Required
TUDUDI_SESSION_SECRET=your-random-secret-here-use-openssl-rand-hex-64
TUDUDI_USER_EMAIL=admin@example.com
TUDUDI_USER_PASSWORD=your-secure-password

# Optional - Server config
NODE_ENV=development
DB_FILE=database.sqlite
FRONTEND_URL=http://localhost:8080
BACKEND_URL=http://localhost:3002
PORT=3002
HOST=0.0.0.0

# Optional - Email
ENABLE_EMAIL=false
EMAIL_SMTP_HOST=smtp.example.com
EMAIL_SMTP_PORT=587
EMAIL_SMTP_SECURE=false
EMAIL_SMTP_USERNAME=user
EMAIL_SMTP_PASSWORD=pass
EMAIL_FROM_ADDRESS=noreply@example.com
EMAIL_FROM_NAME=Tududi

# Optional - Integrations
DISABLE_TELEGRAM=false
GOOGLE_CLIENT_ID=your-google-oauth-client-id
GOOGLE_CLIENT_SECRET=your-google-oauth-secret
GOOGLE_REDIRECT_URI=http://localhost:8080/auth/google/callback

# Optional - Features
DISABLE_SCHEDULER=false
SWAGGER_ENABLED=true
RATE_LIMITING_ENABLED=true

# Optional - Proxy (if behind reverse proxy)
TUDUDI_TRUST_PROXY=false
TUDUDI_ALLOWED_ORIGINS=http://localhost:8080

# Optional - Registration
REGISTRATION_TOKEN_EXPIRY_HOURS=24
```

**Generate secure session secret:**
```bash
openssl rand -hex 64
```

---

## Adding a New Feature (Complete Example)

**Example: Add "estimated_time" field to tasks**

This walkthrough shows all files to touch when adding a new field to an existing model.

### Step 1: Create Database Migration

```bash
npm run migration:create -- --name add-estimated-time-to-tasks
```

Edit the created file `/backend/migrations/YYYYMMDDHHMMSS-add-estimated-time-to-tasks.js`:

```javascript
'use strict';

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.addColumn('Tasks', 'estimated_time', {
      type: Sequelize.INTEGER, // minutes
      allowNull: true,
      defaultValue: null
    });
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.removeColumn('Tasks', 'estimated_time');
  }
};
```

**Run migration:**
```bash
npm run migration:run
```

### Step 2: Update Sequelize Model

Edit `/backend/models/task.js`:

```javascript
Task.init({
  // ... existing fields ...
  estimated_time: {
    type: DataTypes.INTEGER,
    allowNull: true,
    comment: 'Estimated time in minutes'
  },
  // ... rest of fields ...
}, {
  sequelize,
  modelName: 'Task',
  tableName: 'Tasks'
});
```

### Step 3: Update Serializer (API Response)

Edit `/backend/modules/tasks/core/serializers.js`:

```javascript
function serializeTask(task) {
  return {
    // ... existing fields ...
    estimated_time: task.estimated_time,
    // ... rest of fields ...
  };
}
```

### Step 4: Update Builder (API Input)

Edit `/backend/modules/tasks/core/builders.js`:

```javascript
function buildTaskAttributes(data, userId) {
  const attributes = {
    // ... existing fields ...
    estimated_time: data.estimated_time ? parseInt(data.estimated_time, 10) : null,
    // ... rest of fields ...
  };
  
  return attributes;
}
```

### Step 5: Add Validation (Optional)

If validation needed, edit `/backend/modules/tasks/routes.js`:

```javascript
router.put('/task/:id', async (req, res, next) => {
  try {
    // Validate estimated_time
    if (req.body.estimated_time !== undefined) {
      const time = parseInt(req.body.estimated_time, 10);
      if (isNaN(time) || time < 0) {
        return res.status(400).json({
          error: 'Estimated time must be a positive number'
        });
      }
    }
    
    // ... rest of route handler ...
  } catch (error) {
    next(error);
  }
});
```

### Step 6: Update Swagger Documentation

Edit `/backend/config/swagger.js`:

```javascript
// Find Task schema
components: {
  schemas: {
    Task: {
      type: 'object',
      properties: {
        // ... existing properties ...
        estimated_time: {
          type: 'integer',
          description: 'Estimated time in minutes',
          nullable: true,
          example: 30
        }
      }
    }
  }
}
```

### Step 7: Update Frontend TypeScript Interface

If TypeScript interface exists, edit `/frontend/entities/Task.ts`:

```typescript
export interface Task {
  // ... existing fields ...
  estimated_time: number | null;
  // ... rest of fields ...
}
```

### Step 8: Update Frontend Component

Edit `/frontend/components/Task/TaskForm.tsx`:

```typescript
// Add input field
<div className="mb-4">
  <label className="block text-sm font-medium text-gray-700 dark:text-gray-300">
    Estimated Time (minutes)
  </label>
  <input
    type="number"
    min="0"
    value={task.estimated_time || ''}
    onChange={(e) => updateTask({
      ...task,
      estimated_time: e.target.value ? parseInt(e.target.value) : null
    })}
    className="mt-1 block w-full rounded-md border-gray-300 shadow-sm"
  />
</div>
```

### Step 9: Write Tests

Add tests in `/backend/tests/integration/tasks/tasks.test.js`:

```javascript
it('should create task with estimated_time', async () => {
  const response = await request(app)
    .post('/api/v1/task')
    .set('Cookie', authCookie)
    .send({
      name: 'Task with estimate',
      estimated_time: 60
    });

  expect(response.status).toBe(201);
  expect(response.body.estimated_time).toBe(60);
});

it('should reject negative estimated_time', async () => {
  const response = await request(app)
    .post('/api/v1/task')
    .set('Cookie', authCookie)
    .send({
      name: 'Invalid task',
      estimated_time: -10
    });

  expect(response.status).toBe(400);
});
```

### Step 10: Run Tests and Checks

```bash
# Run backend tests
npm run backend:test

# Run linting
npm run lint:fix

# Format code
npm run format:fix

# Run all pre-push checks
npm run pre-push
```

### Step 11: Commit Changes

```bash
git add .
git commit -m "Add estimated_time field to tasks

- Add database migration for estimated_time
- Update Task model and serializers
- Update Swagger documentation
- Add validation for positive values
- Add UI field in TaskForm component
- Add integration tests"
```

---

## Database Management

```bash
# Reset database (WIPES ALL DATA!)
npm run db:reset

# Seed development data
npm run db:seed

# Check migration status
npm run db:status

# Create new migration
npm run migration:create -- --name description

# Run pending migrations
npm run migration:run

# Rollback last migration
npm run migration:undo
```

---

## Code Quality

```bash
# Check linting
npm run lint

# Auto-fix linting issues
npm run lint:fix

# Format code with Prettier
npm run format:fix

# Run all pre-push checks
# (lint + format + tests)
npm run pre-push
```

---

## Testing

```bash
# Backend tests
npm test
# or
npm run backend:test

# Frontend tests
npm run frontend:test

# E2E tests
npm run test:ui           # Headless
npm run test:ui:headed    # With browser visible

# Coverage report
npm run test:coverage

# Watch mode (during development)
npm run test:watch
```

---

## Branch Strategy

From CONTRIBUTING.md conventions:

```bash
# Feature branches
git checkout -b feature/description

# Bug fix branches
git checkout -b fix/description

# Refactoring branches
git checkout -b refactor/description

# Documentation branches
git checkout -b docs/description

# Test branches
git checkout -b test/description
```

### Example Workflow

```bash
# Create feature branch from main
git checkout main
git pull origin main
git checkout -b feature/estimated-time

# Make changes, run tests
npm run pre-push

# Commit changes
git add .
git commit -m "Add estimated time feature"

# Before PR: rebase on main
git checkout main
git pull origin main
git checkout feature/estimated-time
git rebase main

# Push and create PR
git push origin feature/estimated-time
```

---

## Build for Production

```bash
# Build frontend
npm run build

# Builds to: /dist/
# - Minified JavaScript
# - Optimized CSS
# - Hashed filenames for cache busting
```

### Docker Production

```bash
# Build Docker image
docker build -t tududi:latest .

# Run container
docker run \
  -e TUDUDI_USER_EMAIL=admin@example.com \
  -e TUDUDI_USER_PASSWORD=secure-password \
  -e TUDUDI_SESSION_SECRET=$(openssl rand -hex 64) \
  -v ~/tududi_db:/app/backend/db \
  -v ~/tududi_uploads:/app/backend/uploads \
  -p 3002:3002 \
  -d tududi:latest
```

---

## Troubleshooting

### Port Already in Use

```bash
# Find process using port 3002
lsof -ti:3002

# Kill process
kill -9 $(lsof -ti:3002)

# Or use different port
PORT=3003 npm run backend:dev
```

### Database Locked

```bash
# Stop all servers
# Delete database file
rm backend/database.sqlite

# Reinitialize
npm run db:init
```

### Module Not Found

```bash
# Clean install
rm -rf node_modules package-lock.json
npm install
```

### Webpack Build Errors

```bash
# Clear webpack cache
rm -rf node_modules/.cache

# Rebuild
npm run frontend:dev
```

---

[← Back to Index](../CLAUDE.md)
