Skip to content

Latest commit

 

History

History
428 lines (342 loc) · 13.1 KB

File metadata and controls

428 lines (342 loc) · 13.1 KB

ROTA Project Development Rules & Commands

Directory Structure

C:\ROTA\
├── src\
│   ├── StaffRota.Web\          # Backend .NET API
│   ├── StaffRota.Core\         # Core business logic
│   └── StaffRota.Data\         # Data access layer
├── frontend\
│   └── staff-rota-ui\          # React TypeScript frontend
├── create_shift_assignments_table.sql
├── StaffRota.sln
└── rules.md                   # This file

PowerShell Command Rules

Command Chaining

  • DO NOT use && (bash syntax) - it doesn't work in PowerShell
  • USE ; for command chaining in PowerShell
  • Example: cd C:\ROTA\src\StaffRota.Web; dotnet build

Directory Navigation

  • Always use full absolute paths to avoid navigation errors
  • Backend: C:\ROTA\src\StaffRota.Web
  • Frontend: C:\ROTA\frontend\staff-rota-ui

Common Commands

Backend (.NET)

# Navigate to backend
cd C:\ROTA\src\StaffRota.Web

# Build project
dotnet build

# Run backend server (background)
dotnet run

# Stop running process
# Find process: tasklist | findstr "StaffRota"
# Kill process: Stop-Process -Id [PID] -Force

Frontend (React)

# Navigate to frontend
cd C:\ROTA\frontend\staff-rota-ui

# Install dependencies
npm install

# Start development server
npm start

# Build for production
npm run build

Database

# Run SQL scripts from project root
cd C:\ROTA
# Execute SQL file through backend connection

Development Workflow

1. Starting Development Environment

# Terminal 1 - Backend
cd C:\ROTA\src\StaffRota.Web; dotnet run

# Terminal 2 - Frontend  
cd C:\ROTA\frontend\staff-rota-ui; npm start

2. Making Changes

  1. Backend changes: Stop backend → Make changes → dotnet builddotnet run
  2. Frontend changes: Hot reload automatically applies changes
  3. Database changes: Run SQL scripts → Restart backend

3. Troubleshooting Build Issues

  • File locked errors: Kill running processes first
  • Missing dependencies: Run dotnet restore or npm install
  • Port conflicts: Check if services are already running

File Paths Reference

Key Backend Files

  • Main service: C:\ROTA\src\StaffRota.Web\Services\ShiftService.cs
  • Controller: C:\ROTA\src\StaffRota.Web\Controllers\ShiftsController.cs
  • Startup: C:\ROTA\src\StaffRota.Web\Program.cs
  • Entities: C:\ROTA\src\StaffRota.Core\Entities\

Key Frontend Files

  • Main app: C:\ROTA\frontend\staff-rota-ui\src\App.tsx
  • Shift management: C:\ROTA\frontend\staff-rota-ui\src\components\ShiftManagement.tsx
  • Staff assignment dialog: C:\ROTA\frontend\staff-rota-ui\src\components\StaffAssignmentDialog.tsx

Multi-Staff Assignment System

Backend Architecture

  • Entity: ShiftAssignment (ShiftId, UserId, AssignedAt)
  • Repository: ShiftAssignmentRepository for CRUD operations
  • Service: ShiftService.AssignUsersToShiftAsync() with validation
  • API: /api/shifts/{id}/assignments endpoints

Frontend Architecture

  • Component: StaffAssignmentDialog with dual-list interface
  • State: Available staff (left) ↔ Assigned staff (right)
  • Validation: Prevent over-assignment beyond RequiredStaff limit
  • UI: Material-UI with responsive design (90vh height)

PowerShell Specific Notes

Terminal Management

  • Multiple terminals may be needed (backend + frontend)
  • Use Ctrl+C to stop running processes
  • Background processes: Use isBackground: true in terminal commands

Path Handling

  • Use backslashes \ for Windows paths
  • Wrap paths with spaces in quotes
  • Prefer absolute paths over relative navigation

Error Prevention

Common Issues

  1. Build failures: Usually due to running processes locking DLLs
  2. Port conflicts: Backend (5000/5001) or Frontend (3000)
  3. Missing files: Use absolute paths, verify file existence
  4. SQL errors: Ensure database connection and table existence

Best Practices

  1. Always check if processes are running before building
  2. Use full directory paths in commands
  3. Verify file changes before executing operations
  4. Test both frontend and backend after changes
  5. Keep terminal output for debugging reference

Adding New Functionality - Complete Implementation Pattern

Based on learnings from implementing the Multi-Staff Assignment System

Overview: The 8-Layer Architecture Pattern

When adding complex new features, changes must be coordinated across all layers of the stack. Missing any layer will result in incomplete functionality.

1. Database Layer (Foundation)

Location: C:\ROTA\create_[feature]_table.sql Purpose: Data persistence and relationships

Key Actions:

  • Create new tables with proper foreign keys
  • Add indexes for performance
  • Add unique constraints to prevent duplicates
  • Migrate existing data if needed
  • Critical: Ensure table is actually created in database file

Example - Multi-Staff Assignments:

CREATE TABLE "ShiftAssignments" (
    "Id" INTEGER PRIMARY KEY AUTOINCREMENT,
    "ShiftId" INTEGER NOT NULL,
    "UserId" INTEGER NOT NULL,
    CONSTRAINT "FK_ShiftAssignments_Shifts_ShiftId" 
        FOREIGN KEY ("ShiftId") REFERENCES "Shifts" ("Id") ON DELETE CASCADE
);

2. Core Entities Layer

Location: C:\ROTA\src\StaffRota.Core\Entities\ Purpose: Define data models and business rules

Key Actions:

  • Create new entity classes
  • Add navigation properties for relationships
  • Define enums for status/type fields
  • Add validation attributes

Example:

public class ShiftAssignment
{
    public int Id { get; set; }
    public int ShiftId { get; set; }
    public int UserId { get; set; }
    public virtual Shift Shift { get; set; }
    public virtual User User { get; set; }
}

3. Data Access Layer (Repository Pattern)

Location: C:\ROTA\src\StaffRota.Data\Repositories\ Purpose: Database operations and query logic

Key Actions:

  • Create repository interface (I[Feature]Repository)
  • Implement repository class with CRUD operations
  • Add Entity Framework configuration
  • Register in dependency injection

Critical Methods:

  • GetByIdAsync() - Single record retrieval
  • GetByParentIdAsync() - Related records (e.g., assignments by shift)
  • CreateAsync() / UpdateAsync() / DeleteAsync() - CRUD
  • HasConflictAsync() - Business rule validation

4. Business Logic Layer (Services)

Location: C:\ROTA\src\StaffRota.Web\Services\ Purpose: Business rules, validation, and orchestration

Key Actions:

  • Add methods to existing services OR create new service
  • Implement business validation (staffing limits, conflicts, etc.)
  • Handle transaction logic
  • Return structured results (success/failure with details)

Critical Pattern - Replace vs Add Logic:

// WRONG: Only adds new records
public async Task AssignUsersAsync(int shiftId, List<int> userIds)
{
    foreach(var userId in userIds) {
        await _repository.CreateAsync(new Assignment { ShiftId = shiftId, UserId = userId });
    }
}

// RIGHT: Replaces complete assignment list
public async Task AssignUsersAsync(int shiftId, List<int> userIds) 
{
    // Remove existing
    var existing = await _repository.GetByShiftIdAsync(shiftId);
    foreach(var assignment in existing) {
        await _repository.DeleteAsync(assignment.Id);
    }
    
    // Add new
    foreach(var userId in userIds) {
        await _repository.CreateAsync(new Assignment { ShiftId = shiftId, UserId = userId });
    }
}

5. API Layer (Controllers)

Location: C:\ROTA\src\StaffRota.Web\Controllers\ Purpose: HTTP endpoints and request/response handling

Key Actions:

  • Add new endpoints OR extend existing controllers
  • Add request/response DTOs
  • Implement proper HTTP verbs (GET, POST, PUT, DELETE)
  • Add authorization attributes
  • Critical: Use async mapping methods that load related data

Data Loading Pattern:

// WRONG: Uses static mapping without related data
var shiftDtos = shifts.Select(MapToShiftDto);

// RIGHT: Uses async mapping with related data loading
var shiftDtos = new List<ShiftDto>();
foreach (var shift in shifts) {
    var shiftDto = await MapToShiftDtoAsync(shift); // Loads assignments
    shiftDtos.Add(shiftDto);
}

6. Frontend Services Layer

Location: C:\ROTA\frontend\staff-rota-ui\src\services\ Purpose: API communication and data transformation

Key Actions:

  • Add methods to existing service OR create new service
  • Handle request/response format differences
  • Add proper error handling
  • Critical: Match backend property naming (PascalCase vs camelCase)

Naming Convention Fix:

// Frontend sends
const requestData = {
    userIds: [1, 2, 3],
    notes: "example"
};

// Backend expects (fix in service)
const requestData = {
    UserIds: assignmentData.userIds,      // Convert case
    Notes: assignmentData.notes || '',
    ShiftId: id,
    SendNotification: true
};

7. Frontend Components Layer

Location: C:\ROTA\frontend\staff-rota-ui\src\components\ Purpose: User interface and user experience

Key Actions:

  • Create new components OR extend existing ones
  • Implement proper state management
  • Add form validation and error handling
  • Critical: Call refresh/reload after data changes

UI Refresh Pattern:

const handleSave = async () => {
    await ServiceClass.saveData(data);
    
    // CRITICAL: Refresh data to show changes
    await loadData();  
    
    onClose(); // Close dialog after refresh
};

8. Integration Layer (Dependency Injection)

Location: C:\ROTA\src\StaffRota.Web\Program.cs Purpose: Wire up all components

Key Actions:

  • Register new repositories in DI container
  • Register new services in DI container
  • Ensure proper lifetime management (Scoped for Entity Framework)

Example:

builder.Services.AddScoped<IShiftAssignmentRepository, ShiftAssignmentRepository>();

Implementation Checklist Template

When adding new functionality, verify each layer:

□ Database Layer

  • SQL script created and tested
  • Tables/indexes actually exist in database file
  • Foreign key relationships established
  • Migration of existing data (if needed)

□ Entity Layer

  • Entity classes created with proper properties
  • Navigation properties for relationships
  • Enums defined for status fields

□ Repository Layer

  • Interface created with all needed methods
  • Implementation with proper async/await
  • Entity Framework configuration
  • Dependency injection registration

□ Service Layer

  • Business logic methods implemented
  • Validation rules enforced
  • Transaction handling
  • Error handling with structured results

□ API Layer

  • Endpoints created with proper HTTP verbs
  • Request/response DTOs defined
  • Authorization attributes added
  • ASYNC mapping methods for related data

□ Frontend Service Layer

  • API client methods created
  • Request/response transformation
  • Property name case conversion (camelCase ↔ PascalCase)
  • Error handling

□ Frontend Component Layer

  • UI components created/updated
  • State management implemented
  • Form validation added
  • Data refresh after mutations

□ Integration Layer

  • Dependency injection configured
  • Services registered with proper lifetimes

Common Pitfalls and Solutions

1. "API returns 200 but no data changes"

  • Cause: Using static mapping methods that don't load related data
  • Solution: Use async mapping methods with data loading

2. "Frontend shows old data after save"

  • Cause: Missing data refresh after mutations
  • Solution: Call loadData() after save operations

3. "Assignment adds instead of replaces"

  • Cause: Business logic only handles additions
  • Solution: Implement "clear existing + add new" pattern

4. "Missing table errors"

  • Cause: SQL script not properly executed
  • Solution: Verify table exists in actual database file

5. "Case sensitivity API errors"

  • Cause: Frontend camelCase vs Backend PascalCase
  • Solution: Transform property names in frontend service layer

6. "Date range filtering excludes records"

  • Cause: Default API filters too restrictive
  • Solution: Use wide date ranges or remove default filters

Testing Strategy

For each new feature, test the complete flow:

  1. Create new record → Verify in database
  2. Read records → Verify UI displays correctly
  3. Update existing record → Verify changes persist
  4. Delete record → Verify removal
  5. Validation → Test business rules and limits
  6. Edge cases → Empty states, conflicts, errors

Debugging Approach

When functionality doesn't work:

  1. Check database - Is data actually saved?
  2. Check API responses - Use browser dev tools network tab
  3. Check property names - Frontend vs backend naming
  4. Check data loading - Are related records included?
  5. Check refresh logic - Does UI update after changes?

This pattern applies to any complex feature: recurring shifts, department management, notifications, etc.