Yaltopia-Tickets-Backend/example-new-role-implementation.md
debudebuye 98d4bb52c3 Initial commit: Receipt Verification API with universal adapter pattern
- JWT authentication with Supabase integration
- Role-based access control (Admin, Owner, Staff, Auditor)
- Universal database adapter (Prisma/Supabase/MongoDB support)
- User management with hierarchical permissions
- Redis caching service (configured but optional)
- Comprehensive API documentation
- Production-ready NestJS architecture
- Migration scripts for provider switching
- Swagger/OpenAPI documentation
2025-12-21 22:05:22 +03:00

3.3 KiB

Adding a MANAGER Role - Complete Example

1. Update Types

// src/shared/types/index.ts
export enum UserRole {
  SYSTEM_ADMIN = 'SYSTEM_ADMIN',
  BUSINESS_OWNER = 'BUSINESS_OWNER',
  MANAGER = 'MANAGER',           // ← NEW
  STAFF = 'STAFF',
  AUDITOR = 'AUDITOR',
}

2. Update Role Constants

// src/shared/constants/roles.ts
export const ROLE_HIERARCHY = {
  [UserRole.SYSTEM_ADMIN]: 4,
  [UserRole.BUSINESS_OWNER]: 3,
  [UserRole.MANAGER]: 2.5,       // ← NEW (between business owner and staff)
  [UserRole.STAFF]: 2,
  [UserRole.AUDITOR]: 1,
};

export const ROLE_PERMISSIONS = {
  // ... existing roles
  [UserRole.MANAGER]: [
    'manage_team_receipts',
    'view_team_reports',
    'verify_receipts',
    'manage_staff_schedules',    // ← NEW PERMISSIONS
  ],
};

3. Update Database Schema

// prisma/schema.prisma
enum UserRole {
  SYSTEM_ADMIN
  BUSINESS_OWNER
  MANAGER        // ← ADD
  STAFF
  AUDITOR
}

4. Add Manager-Specific Endpoints

// src/features/users/users.controller.ts
@Get('my-team')
@Roles(UserRole.MANAGER, UserRole.BUSINESS_OWNER, UserRole.SYSTEM_ADMIN)
@ApiOperation({ summary: 'Get team members (Manager access)' })
async getMyTeam(@CurrentUser() user: any) {
  return this.usersService.getTeamByManager(user.id);
}

@Post('assign-staff')
@Roles(UserRole.MANAGER, UserRole.BUSINESS_OWNER)
@ApiOperation({ summary: 'Assign staff to manager' })
async assignStaff(@Body() assignDto: AssignStaffDto) {
  return this.usersService.assignStaffToManager(assignDto);
}

5. Update Service Logic

// src/features/users/users.service.ts
async getUsers(currentUserId: string, currentUserRole: UserRole) {
  // ... existing logic
  
  if (currentUserRole === UserRole.MANAGER) {
    // Managers see their assigned team
    const teamMembers = await this.supabaseDb.getUsersByManager(currentUserId);
    const currentUser = await this.supabaseDb.findUserById(currentUserId);
    return currentUser ? [currentUser, ...teamMembers] : teamMembers;
  }
  
  // ... rest of logic
}

async getTeamByManager(managerId: string): Promise<User[]> {
  return this.supabaseDb.findUsers({ 
    manager_id: managerId, 
    is_active: true 
  });
}

6. Database Migration

-- Add new role to enum
ALTER TYPE "UserRole" ADD VALUE 'MANAGER';

-- Add manager_id column to users table (optional)
ALTER TABLE users ADD COLUMN manager_id UUID REFERENCES users(id);

-- Create index for manager relationships
CREATE INDEX idx_users_manager_id ON users(manager_id);

7. Update DTOs

// src/features/users/dto/user.dto.ts
export class CreateStaffDto {
  @ApiProperty({
    description: 'Staff role',
    enum: [UserRole.STAFF, UserRole.AUDITOR, UserRole.MANAGER], // ← ADD MANAGER
    example: UserRole.STAFF,
  })
  @IsEnum([UserRole.STAFF, UserRole.AUDITOR, UserRole.MANAGER])
  role: UserRole.STAFF | UserRole.AUDITOR | UserRole.MANAGER;
  
  @ApiPropertyOptional({
    description: 'Manager ID (for staff assignments)',
  })
  @IsOptional()
  @IsUUID()
  managerId?: string;
}

Result: Fully Functional Manager Role

  • Hierarchical permissions (can access staff endpoints)
  • Manager-specific endpoints
  • Team management capabilities
  • Database relationships
  • Type safety maintained
  • API documentation updated