Správa používateľov + notifikačný systém

- Pridaná kompletná správa používateľov (CRUD, reset hesla, zmena roly) pre ROOT/ADMIN
- Backend: POST /users endpoint, createUser controller, validácia
- Frontend: UserManagement, UserForm, PasswordResetModal komponenty
- Settings prístupné pre ROOT aj ADMIN (AdminRoute)
- Notifikačný systém s snooze funkcionalitou
- Aktualizácia HELPDESK_INIT_V2.md dokumentácie

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 15:30:27 +01:00
parent cbdd952bc1
commit 2ca0c4f4d8
36 changed files with 3116 additions and 522 deletions

View File

@@ -28,7 +28,8 @@
### **Pridané:**
- ✅ Configuration-driven architecture
- ✅ ROOT Settings panel
- ✅ ROOT/ADMIN Settings panel
- ✅ User Management (CRUD, reset hesla, zmena roly)
- ✅ External DB import pre zákazníkov
- ✅ Dynamic workflow rules
- ✅ Multi-entity tagging system
@@ -173,6 +174,10 @@ model User {
reminders Reminder[]
activityLogs ActivityLog[]
// Comments & Notifications
comments Comment[]
notifications Notification[]
// Equipment
createdEquipment Equipment[] @relation("EquipmentCreator")
performedRevisions Revision[]
@@ -511,11 +516,12 @@ model Task {
createdById String
createdBy User @relation("TaskCreator", fields: [createdById], references: [id])
assignees TaskAssignee[]
reminders Reminder[]
comments Comment[]
tags TaskTag[]
assignees TaskAssignee[]
reminders Reminder[]
comments Comment[]
tags TaskTag[]
notifications Notification[]
@@index([projectId])
@@index([parentId])
@@index([statusId])
@@ -572,18 +578,51 @@ model Comment {
id String @id @default(cuid())
taskId String
userId String
task Task @relation(fields: [taskId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id])
content String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([taskId])
@@index([createdAt])
}
// ==================== NOTIFICATIONS ====================
model Notification {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
type String // TASK_ASSIGNED, TASK_STATUS_CHANGED, TASK_COMMENT, etc.
title String
message String // Prázdne pre TASK_COMMENT - text sa načíta z Comment tabuľky
// Odkazy na entity
taskId String?
task Task? @relation(fields: [taskId], references: [id], onDelete: Cascade)
rmaId String?
rma RMA? @relation(fields: [rmaId], references: [id], onDelete: Cascade)
// Dodatočné dáta (JSON) - napr. commentId, actorName, oldStatus, newStatus
data Json?
isRead Boolean @default(false)
readAt DateTime?
snoozedUntil DateTime? // Odloženie notifikácie
createdAt DateTime @default(now())
@@index([userId, isRead])
@@index([userId, createdAt])
@@index([taskId])
@@index([rmaId])
}
// ==================== EQUIPMENT MANAGEMENT ====================
model Equipment {
@@ -760,7 +799,8 @@ model RMA {
statusHistory RMAStatusHistory[]
comments RMAComment[]
tags RMATag[]
notifications Notification[]
@@index([rmaNumber])
@@index([customerId])
@@index([statusId])
@@ -910,15 +950,16 @@ POST /api/auth/logout
GET /api/auth/me
```
### Users
### Users (ROOT/ADMIN)
```
GET /api/users // Stránkovaný zoznam (admin only)
GET /api/users/simple // Jednoduchý zoznam pre selecty (server-side search: ?search=meno)
POST /api/users // Vytvorenie používateľa (admin only)
GET /api/users/:id
PUT /api/users/:id
DELETE /api/users/:id
PATCH /api/users/:id/role
PUT /api/users/:id // Úprava + reset hesla
DELETE /api/users/:id // Soft delete (deaktivácia)
PATCH /api/users/:id/role // Zmena roly
```
### Projects
@@ -1004,7 +1045,7 @@ GET /api/rma/:id/pdf // Generate PDF
GET /api/rma/generate-number // Next RMA number
```
### **🆕 Settings (ROOT only)**
### **🆕 Settings (ROOT/ADMIN)**
```
// Equipment Types
@@ -1064,6 +1105,17 @@ PUT /api/settings/roles/:id
DELETE /api/settings/roles/:id
```
### **🆕 Notifications**
```
GET /api/notifications // Zoznam notifikácií (limit, offset, unreadOnly)
GET /api/notifications/unread-count // Počet neprečítaných
POST /api/notifications/:id/read // Označiť ako prečítané
POST /api/notifications/mark-all-read // Označiť všetky ako prečítané
POST /api/notifications/:id/snooze // Odložiť notifikáciu (minutes)
DELETE /api/notifications/:id // Vymazať notifikáciu
```
### Dashboard
```
@@ -1100,6 +1152,9 @@ src/
│ │ ├── Sidebar.tsx
│ │ └── MainLayout.tsx
│ │
│ ├── notifications/ # NEW (Fáza 2)
│ │ └── NotificationCenter.tsx # Zvonček s dropdown v header
│ │
│ ├── dashboard/
│ │ ├── DashboardView.tsx
│ │ ├── TodaysTasks.tsx
@@ -1153,6 +1208,9 @@ src/
│ │
│ ├── settings/ # NEW
│ │ ├── SettingsDashboard.tsx
│ │ ├── UserManagement.tsx # Správa používateľov (ROOT/ADMIN)
│ │ ├── UserForm.tsx # Formulár vytvorenie/editácia
│ │ ├── PasswordResetModal.tsx # Reset hesla
│ │ ├── EquipmentTypesSettings.tsx
│ │ ├── RevisionTypesSettings.tsx
│ │ ├── RMAStatusSettings.tsx
@@ -1266,9 +1324,9 @@ cd backend && npx prisma db seed
---
### **FÁZA 2: Core Features + Workflow** 🔥 (4-5 týždňov)
### **FÁZA 2: Core Features + Workflow** 🔥 (4-5 týždňov) - *PREBIEHAJÚCA*
**Cieľ:** Swimlanes, revízie, RMA workflow, reminders
**Cieľ:** Swimlanes, revízie, RMA workflow, reminders, notifikácie
**Backend:**
- [ ] **Revision system**
@@ -1284,8 +1342,15 @@ cd backend && npx prisma db seed
- [ ] Dashboard aggregations
- [ ] Email service (Postfix self-hosted)
- [ ] WebSocket (Socket.IO)
- [ ] File upload handling
- [ ] **Task notifications** (databázové - viditeľné na všetkých zariadeniach)
- [x] File upload handling
- [x] **Notification system**
- [x] Notification model (Prisma)
- [x] notification.service.ts - CRUD, enrichment komentárov
- [x] notifyTaskComment - ukladá len commentId (žiadna duplicita)
- [x] notifyTaskStatusChange - ukladá oldStatus, newStatus, actorName
- [x] notifyTaskAssignment
- [x] Snooze funkcionalita s konfigurovateľnými možnosťami
- [x] SystemSetting NOTIFICATION_SNOOZE_OPTIONS
**Frontend:**
- [ ] **Swimlanes Board** (dnd-kit)
@@ -1300,7 +1365,7 @@ cd backend && npx prisma db seed
- [ ] **RMA Workflow**
- [ ] Status change UI
- [ ] Approval buttons (admin)
- [ ] File attachments
- [x] File attachments
- [ ] Comments
- [ ] PDF export
- [ ] **Inline Quick Actions**
@@ -1308,21 +1373,29 @@ cd backend && npx prisma db seed
- [ ] Reminder management UI
- [ ] Filters & tags
- [ ] Real-time updates (WebSocket)
- [ ] **Notifikácie o nových komentároch/zmenách** (všetky zariadenia)
- [x] **Notification UI**
- [x] NotificationCenter komponent (zvonček v header)
- [x] Dashboard - prehľadné zobrazenie notifikácií
- [x] Typ notifikácie + relatívny čas
- [x] Názov úlohy + projekt
- [x] Detail zmeny/komentára + autor
- [x] markAsRead pri akcii (komentár/zmena stavu)
- [x] Snooze dropdown s konfigurovateľnými možnosťami
- [x] useSnoozeOptions hook (načíta z SystemSettings)
**Deliverable:**
```
✅ Všetko z Fázy 1 +
Swimlanes board
Revízny systém funguje
RMA workflow s approval
Email notifikácie
Live updates (WebSocket)
Swimlanes board
Revízny systém funguje
RMA workflow s approval
Email notifikácie
Live updates (WebSocket)
✅ File uploads
✅ Task notifikácie (databázové, všetky zariadenia)
```
**Čas:** 4-5 týždňov
**Čas:** 4-5 týždňov
**Náklady:** €15-25/mesiac
---
@@ -2059,7 +2132,120 @@ async function getUpcomingRevisions(days: number = 30) {
---
### **5. External DB Import (Customer)**
### **5. Notification Service (Fáza 2)**
```typescript
// services/notification.service.ts
export enum NotificationType {
TASK_ASSIGNED = 'TASK_ASSIGNED',
TASK_STATUS_CHANGED = 'TASK_STATUS_CHANGED',
TASK_COMMENT = 'TASK_COMMENT',
TASK_DEADLINE_APPROACHING = 'TASK_DEADLINE_APPROACHING',
TASK_UPDATED = 'TASK_UPDATED',
RMA_ASSIGNED = 'RMA_ASSIGNED',
RMA_STATUS_CHANGED = 'RMA_STATUS_CHANGED',
RMA_COMMENT = 'RMA_COMMENT',
}
export const notificationService = {
// Vytvorenie notifikácie pre viacerých používateľov
async createForUsers(userIds: string[], data: {
type: NotificationType;
title: string;
message: string;
taskId?: string;
rmaId?: string;
data?: object; // Dodatočné dáta (commentId, actorName, ...)
}) {
if (userIds.length === 0) return [];
return prisma.notification.createMany({
data: userIds.map((userId) => ({
userId,
type: data.type,
title: data.title,
message: data.message,
taskId: data.taskId,
rmaId: data.rmaId,
data: data.data || undefined,
})),
});
},
// Notifikácia o novom komentári - NEUKLADÁ text, len commentId
async notifyTaskComment(taskId: string, commentId: string, commentByUserId: string, commentByUserName: string) {
const task = await prisma.task.findUnique({
where: { id: taskId },
include: {
assignees: { select: { userId: true } },
createdBy: { select: { id: true } },
},
});
if (!task) return;
const userIds = new Set<string>();
task.assignees.forEach((a) => userIds.add(a.userId));
userIds.add(task.createdById);
userIds.delete(commentByUserId); // Nenotifikovať autora
if (userIds.size === 0) return;
await this.createForUsers(Array.from(userIds), {
type: NotificationType.TASK_COMMENT,
title: 'Nový komentár',
message: '', // Text sa načíta z Comment tabuľky (žiadna duplicita)
taskId: task.id,
data: { commentId, actorName: commentByUserName },
});
},
// Pri načítaní notifikácií - enrichment TASK_COMMENT
async getForUser(userId: string, options?: { limit?: number; offset?: number }) {
const rawNotifications = await prisma.notification.findMany({
where: { userId },
orderBy: { createdAt: 'desc' },
take: options?.limit || 50,
skip: options?.offset || 0,
include: {
task: { select: { id: true, title: true, project: true } },
},
});
// Pre TASK_COMMENT načítaj text komentára z Comment tabuľky
const notifications = await Promise.all(
rawNotifications.map(async (notification) => {
if (notification.type === 'TASK_COMMENT' && notification.taskId) {
const data = notification.data as { commentId?: string } | null;
if (data?.commentId) {
const comment = await prisma.comment.findUnique({
where: { id: data.commentId },
select: { content: true },
});
if (comment) {
const shortComment = comment.content.length > 100
? comment.content.substring(0, 100) + '...'
: comment.content;
return { ...notification, message: shortComment };
}
}
}
return notification;
})
);
return notifications;
},
};
```
---
### **6. External DB Import (Customer)**
```typescript
// services/import.service.ts
@@ -2266,6 +2452,7 @@ helpdesk-system/
│ │ │ ├── useTasks.ts
│ │ │ ├── useEquipment.ts # NEW
│ │ │ ├── useRMA.ts # NEW
│ │ │ ├── useSnoozeOptions.ts # NEW (Fáza 2) - konfigurovateľné snooze možnosti
│ │ │ └── useKeyboard.ts
│ │ │
│ │ ├── services/
@@ -2276,13 +2463,15 @@ helpdesk-system/
│ │ │ ├── customers.api.ts # NEW
│ │ │ ├── equipment.api.ts # NEW
│ │ │ ├── rma.api.ts # NEW
│ │ │ ── settings.api.ts # NEW
│ │ │ ── settings.api.ts # NEW
│ │ │ └── notification.api.ts # NEW (Fáza 2)
│ │ │
│ │ ├── store/
│ │ │ ├── authStore.ts
│ │ │ ├── configStore.ts # NEW
│ │ │ ├── projectsStore.ts
│ │ │ ── tasksStore.ts
│ │ │ ── tasksStore.ts
│ │ │ └── notificationStore.ts # NEW (Fáza 2)
│ │ │
│ │ ├── types/
│ │ ├── styles/
@@ -2528,6 +2717,7 @@ CELKOM: ~€10-15/mesiac
---
*Dokument vytvorený: 02.02.2026*
*Verzia: 2.0.0*
*Dokument vytvorený: 02.02.2026*
*Posledná aktualizácia: 19.02.2026*
*Verzia: 2.2.0*
*Autor: Claude (Anthropic) + Používateľ*