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
+45 -1
View File
@@ -3,7 +3,7 @@ export interface User {
email: string;
firstName: string;
lastName: string;
role: 'user' | 'admin';
role: 'user' | 'admin' | 'technical' | 'sales';
isActive: boolean;
namespace?: string;
createdAt: string;
@@ -181,3 +181,47 @@ export interface ClusterResources {
nodeCount: number;
appCount: number;
}
// ─── Ticket types ───────────────────────────────────
export type TicketDepartment = 'technical' | 'sales';
export type TicketStatus = 'open' | 'answered' | 'waiting' | 'closed';
export type TicketPriority = 'low' | 'medium' | 'high';
export interface Ticket {
id: string;
subject: string;
department: TicketDepartment;
status: TicketStatus;
priority: TicketPriority;
userId: string;
user?: User;
messages?: TicketMessageType[];
createdAt: string;
updatedAt: string;
closedAt?: string;
}
export interface TicketMessageType {
id: string;
message: string;
ticketId: string;
senderId: string;
senderRole: 'user' | 'admin' | 'technical' | 'sales';
sender?: User;
createdAt: string;
}
export interface CreateTicketDto {
subject: string;
department: TicketDepartment;
priority?: TicketPriority;
message: string;
}
export interface TicketStats {
totalTickets: number;
openTickets: number;
avgResponseTimeMinutes: number;
byDepartment: Record<string, { total: number; open: number; answered: number; closed: number }>;
}