feat: ticketing system with technical/sales roles and department routing

Backend:
- Added TECHNICAL and SALES roles to UserRole enum
- Added TicketDepartment, TicketStatus, TicketPriority enums
- Created Ticket and TicketMessage entities with relationships
- Created TicketsModule with full CRUD service and controller
- Ticket routing: users create tickets to technical/sales departments
- Staff reply updates status (answered), user reply sets waiting
- Role-based access: technical staff sees technical tickets, sales sees sales tickets
- Admin sees all tickets with stats (total, open, avg response time)
- Updated access control: technical role has admin-level access (except role change)
- Sales role can view users and handle sales tickets
- Clusters controller: technical role can manage clusters/pools
- Users controller: technical/sales can view users, only admin changes roles

Frontend:
- New user ticket pages: list (with create form) + detail (chat-style messages)
- Staff ticket panel: filtered by department with status filters
- Admin all-tickets page with statistics dashboard and department/status filters
- Updated sidebar: role-based nav items (admin/technical/sales sections)
- Role badges in header for technical (blue) and sales (green)
- Admin users page: new roles in dropdowns, role change restricted to admin only
- Deploy page: technical role gets cluster selection access like admin
This commit is contained in:
keyhan
2026-04-06 12:36:41 +03:30
parent 4fd102468e
commit 3ab647be2e
20 changed files with 1301 additions and 56 deletions
+8 -8
View File
@@ -63,16 +63,16 @@ export class UsersController {
}
@Get()
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'List all users with optional search (Admin only)' })
@Roles(UserRole.ADMIN, UserRole.TECHNICAL, UserRole.SALES)
@ApiOperation({ summary: 'List all users with optional search (Staff/Admin)' })
@ApiQuery({ name: 'search', required: false, description: 'Search by name or email' })
async findAll(@Query('search') search?: string) {
return this.usersService.findAll(search);
}
@Post()
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Create a new user (Admin only)' })
@Roles(UserRole.ADMIN, UserRole.TECHNICAL)
@ApiOperation({ summary: 'Create a new user (Admin/Technical only)' })
async adminCreate(@Body() dto: AdminCreateUserDto) {
return this.usersService.adminCreate(dto);
}
@@ -86,16 +86,16 @@ export class UsersController {
}
@Patch(':id/deactivate')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Deactivate a user (Admin only)' })
@Roles(UserRole.ADMIN, UserRole.TECHNICAL)
@ApiOperation({ summary: 'Deactivate a user (Admin/Technical)' })
async deactivate(@Param('id') id: string) {
await this.usersService.deactivate(id);
return { message: 'User deactivated' };
}
@Patch(':id/activate')
@Roles(UserRole.ADMIN)
@ApiOperation({ summary: 'Activate a user (Admin only)' })
@Roles(UserRole.ADMIN, UserRole.TECHNICAL)
@ApiOperation({ summary: 'Activate a user (Admin/Technical)' })
async activate(@Param('id') id: string) {
await this.usersService.activate(id);
return { message: 'User activated' };