add many to one in todo, fix bugs, notification about todos
This commit is contained in:
369
DOKUMENTACIA.md
369
DOKUMENTACIA.md
@@ -561,10 +561,26 @@ getMonthlyStats(userId, year, month)
|
|||||||
- averagePerDay
|
- averagePerDay
|
||||||
- byProject (čas per projekt)
|
- byProject (čas per projekt)
|
||||||
- byCompany (čas per firma)
|
- byCompany (čas per firma)
|
||||||
|
|
||||||
|
generateMonthlyTimesheet(userId, year, month)
|
||||||
|
→ Generate Excel (XLSX) report z time entries
|
||||||
|
→ Fetch user info: username, firstName, lastName
|
||||||
|
→ Fetch completed entries for month (LEFT JOIN projects, todos, companies)
|
||||||
|
→ Filter: iba entries s endTime a duration
|
||||||
|
→ Počíta: totalMinutes, dailyTotals (per day)
|
||||||
|
→ Vytvára Excel workbook cez ExcelJS:
|
||||||
|
- Header: Timesheet, Name, Period, Generated date
|
||||||
|
- Table: Date, Project, Todo, Company, Description, Start, End, Duration
|
||||||
|
- Summary: Daily totals + Overall total
|
||||||
|
→ Save to: uploads/timesheets/{userId}/{year}/{month}/timesheet-{period}-{timestamp}.xlsx
|
||||||
|
→ INSERT INTO timesheets (isGenerated = true)
|
||||||
|
→ Vráti: { timesheet, filePath, entriesCount, totalMinutes, totalHours }
|
||||||
```
|
```
|
||||||
|
|
||||||
**Volá:**
|
**Volá:**
|
||||||
- Databázu (timeEntries, projects, todos, companies)
|
- Databázu (timeEntries, projects, todos, companies, users, timesheets)
|
||||||
|
- ExcelJS library (workbook generation)
|
||||||
|
- File system (fs/promises) - save XLSX file
|
||||||
- `utils/errors.NotFoundError`
|
- `utils/errors.NotFoundError`
|
||||||
- `utils/errors.BadRequestError`
|
- `utils/errors.BadRequestError`
|
||||||
|
|
||||||
@@ -609,10 +625,233 @@ logUserCreation(adminId, newUserId, username, role, ip, userAgent)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 13. timesheet.controller.js
|
||||||
|
**Účel:** File upload a správa timesheetov (PDF/Excel)
|
||||||
|
|
||||||
|
**Databáza:** `timesheets`, `users`
|
||||||
|
|
||||||
|
**Metódy:**
|
||||||
|
```javascript
|
||||||
|
uploadTimesheet(req, res)
|
||||||
|
→ Validácia file type (PDF, Excel)
|
||||||
|
→ Max 10MB limit
|
||||||
|
→ Save to: uploads/timesheets/{userId}/{year}/{month}/
|
||||||
|
→ INSERT INTO timesheets
|
||||||
|
→ File stored on disk (not in DB)
|
||||||
|
|
||||||
|
getMyTimesheets(userId, filters)
|
||||||
|
→ Filter: year, month (optional)
|
||||||
|
→ SELECT * FROM timesheets WHERE userId
|
||||||
|
→ ORDER BY uploadedAt DESC
|
||||||
|
|
||||||
|
getAllTimesheets(filters)
|
||||||
|
→ Admin only!
|
||||||
|
→ Filter: userId, year, month (all optional)
|
||||||
|
→ LEFT JOIN users (get username, name)
|
||||||
|
→ Vráti timesheets všetkých userov
|
||||||
|
|
||||||
|
downloadTimesheet(timesheetId, userId, userRole)
|
||||||
|
→ Check permissions: owner alebo admin
|
||||||
|
→ Validate file exists on disk
|
||||||
|
→ res.download(filePath, fileName)
|
||||||
|
|
||||||
|
deleteTimesheet(timesheetId, userId, userRole)
|
||||||
|
→ Check permissions: owner alebo admin
|
||||||
|
→ Delete file from filesystem (fs.unlink)
|
||||||
|
→ DELETE FROM timesheets
|
||||||
|
→ Continue even if file deletion fails
|
||||||
|
```
|
||||||
|
|
||||||
|
**Volá:**
|
||||||
|
- Databázu (timesheets, users)
|
||||||
|
- File system operations (fs/promises)
|
||||||
|
- `utils/errors.NotFoundError, ForbiddenError, BadRequestError`
|
||||||
|
|
||||||
|
**File Storage Pattern:**
|
||||||
|
```
|
||||||
|
uploads/timesheets/
|
||||||
|
└── {userId}/
|
||||||
|
└── {year}/
|
||||||
|
└── {month}/
|
||||||
|
└── filename-timestamp-random.pdf
|
||||||
|
```
|
||||||
|
|
||||||
|
**POZNÁMKA:** Timesheet service NEEXISTUJE - všetka logika je priamo v controlleri!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VALIDATORS
|
||||||
|
|
||||||
|
### 1. auth.validators.js
|
||||||
|
**Účel:** Zod schemas pre autentifikáciu a user management
|
||||||
|
|
||||||
|
**Schemas:**
|
||||||
|
```javascript
|
||||||
|
loginSchema
|
||||||
|
→ username: 3-50 chars, required
|
||||||
|
→ password: min 1 char, required
|
||||||
|
|
||||||
|
setPasswordSchema
|
||||||
|
→ newPassword: min 8 chars, obsahuje a-z, A-Z, 0-9, špeciálny znak
|
||||||
|
→ confirmPassword: musí sa zhodovať
|
||||||
|
→ .refine() custom validation pre password match
|
||||||
|
|
||||||
|
linkEmailSchema
|
||||||
|
→ email: valid email format, max 255 chars
|
||||||
|
→ emailPassword: min 1 char
|
||||||
|
|
||||||
|
createUserSchema (admin only)
|
||||||
|
→ username: 3-50 chars, iba [a-zA-Z0-9_-]
|
||||||
|
→ email: optional (ak sa zadá, môže sa linknúť JMAP)
|
||||||
|
→ emailPassword: optional (pre automatické linkovanie)
|
||||||
|
→ firstName, lastName: optional, max 100 chars
|
||||||
|
|
||||||
|
updateUserSchema
|
||||||
|
→ firstName, lastName, email: all optional
|
||||||
|
|
||||||
|
changeRoleSchema
|
||||||
|
→ userId: UUID
|
||||||
|
→ role: enum ['admin', 'member']
|
||||||
|
```
|
||||||
|
|
||||||
|
**Použitie:**
|
||||||
|
- Všetky `/api/auth/*` routes
|
||||||
|
- Admin user management routes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. crm.validators.js
|
||||||
|
**Účel:** Zod schemas pre Company, Project, Todo, Note, Time Tracking
|
||||||
|
|
||||||
|
**Company Schemas:**
|
||||||
|
```javascript
|
||||||
|
createCompanySchema
|
||||||
|
→ name: required, max 255 chars
|
||||||
|
→ description: optional, max 1000 chars
|
||||||
|
→ address, city, country: optional
|
||||||
|
→ phone: optional, max 50 chars
|
||||||
|
→ email: optional, valid email OR empty string
|
||||||
|
→ website: optional, valid URL OR empty string
|
||||||
|
|
||||||
|
updateCompanySchema
|
||||||
|
→ Všetky fields optional
|
||||||
|
```
|
||||||
|
|
||||||
|
**Project Schemas:**
|
||||||
|
```javascript
|
||||||
|
createProjectSchema
|
||||||
|
→ name: required, max 255 chars
|
||||||
|
→ companyId: optional UUID OR empty string
|
||||||
|
→ status: enum ['active', 'completed', 'on_hold', 'cancelled']
|
||||||
|
→ startDate, endDate: optional strings OR empty
|
||||||
|
|
||||||
|
updateProjectSchema
|
||||||
|
→ Všetky fields optional
|
||||||
|
→ NULL support: .or(z.null())
|
||||||
|
```
|
||||||
|
|
||||||
|
**Todo Schemas:**
|
||||||
|
```javascript
|
||||||
|
createTodoSchema
|
||||||
|
→ title: required, max 255 chars
|
||||||
|
→ projectId, companyId, assignedTo: optional UUID OR empty
|
||||||
|
→ status: enum ['pending', 'in_progress', 'completed', 'cancelled']
|
||||||
|
→ priority: enum ['low', 'medium', 'high', 'urgent']
|
||||||
|
→ dueDate: optional string OR empty
|
||||||
|
|
||||||
|
updateTodoSchema
|
||||||
|
→ Všetky fields optional + NULL support
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note Schemas:**
|
||||||
|
```javascript
|
||||||
|
createNoteSchema
|
||||||
|
→ content: required, max 5000 chars
|
||||||
|
→ title: optional, max 255 chars
|
||||||
|
→ companyId, projectId, todoId, contactId: optional UUID OR empty
|
||||||
|
→ reminderDate: optional string OR empty
|
||||||
|
|
||||||
|
updateNoteSchema
|
||||||
|
→ Všetky fields optional + NULL support
|
||||||
|
```
|
||||||
|
|
||||||
|
**Time Tracking Schemas:**
|
||||||
|
```javascript
|
||||||
|
startTimeEntrySchema
|
||||||
|
→ projectId, todoId, companyId: optional UUID (preprocessed to null if empty)
|
||||||
|
→ description: optional, max 1000 chars, trimmed, null if empty
|
||||||
|
|
||||||
|
stopTimeEntrySchema
|
||||||
|
→ Same as start (používa sa pre update pri stop)
|
||||||
|
|
||||||
|
updateTimeEntrySchema
|
||||||
|
→ startTime, endTime: optional ISO strings
|
||||||
|
→ projectId, todoId, companyId, description: optional with preprocessing
|
||||||
|
```
|
||||||
|
|
||||||
|
**Helper Functions:**
|
||||||
|
```javascript
|
||||||
|
optionalUuid(message)
|
||||||
|
→ Preprocess: undefined, null, '' → null
|
||||||
|
→ Validate: UUID format
|
||||||
|
→ Used for optional foreign keys
|
||||||
|
|
||||||
|
optionalDescription
|
||||||
|
→ Preprocess: trim whitespace, '' → null
|
||||||
|
→ Validate: max 1000 chars
|
||||||
|
→ Nullable
|
||||||
|
```
|
||||||
|
|
||||||
|
**Pattern - Empty String Handling:**
|
||||||
|
```javascript
|
||||||
|
// Frontend môže poslať empty string namiesto null
|
||||||
|
.or(z.literal('')) // Accept empty string
|
||||||
|
.or(z.literal('').or(z.null())) // Update: accept empty OR null
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. email-account.validators.js
|
||||||
|
**Účel:** Zod schemas pre email account management
|
||||||
|
|
||||||
|
**Schemas:**
|
||||||
|
```javascript
|
||||||
|
createEmailAccountSchema
|
||||||
|
→ email: required, valid format, max 255 chars
|
||||||
|
→ emailPassword: required, min 1 char
|
||||||
|
|
||||||
|
updateEmailAccountSchema
|
||||||
|
→ emailPassword: optional, min 1 char
|
||||||
|
→ isActive: optional boolean
|
||||||
|
|
||||||
|
setPrimaryAccountSchema
|
||||||
|
→ accountId: UUID
|
||||||
|
→ POZNÁMKA: NEVYUŽÍVA SA (endpoint má accountId v path params)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Použitie:**
|
||||||
|
- `/api/email-accounts/*` routes
|
||||||
|
- JMAP credential validation flow
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## CONTROLLERS
|
## CONTROLLERS
|
||||||
|
|
||||||
**Účel:** Spracovanie HTTP requestov, volanie services, vracanie responses
|
**Účel:** Spracovanie HTTP requestov, volanie services, vracanie responses
|
||||||
|
|
||||||
|
### Zoznam Controllerov:
|
||||||
|
1. **admin.controller.js** - User management (admin only)
|
||||||
|
2. **auth.controller.js** - Autentifikácia a onboarding
|
||||||
|
3. **company.controller.js** - Firmy CRUD + nested notes
|
||||||
|
4. **contact.controller.js** - Email kontakty
|
||||||
|
5. **crm-email.controller.js** - Email management (read status, search)
|
||||||
|
6. **email-account.controller.js** - JMAP účty
|
||||||
|
7. **note.controller.js** - Standalone poznámky (nevyužité)
|
||||||
|
8. **project.controller.js** - Projekty CRUD + nested notes + team management
|
||||||
|
9. **todo.controller.js** - Úlohy CRUD
|
||||||
|
10. **time-tracking.controller.js** - Sledovanie času
|
||||||
|
11. **timesheet.controller.js** - Upload a download timesheetov (bez service!)
|
||||||
|
|
||||||
### Štruktúra každého controllera:
|
### Štruktúra každého controllera:
|
||||||
```javascript
|
```javascript
|
||||||
export const methodName = async (req, res) => {
|
export const methodName = async (req, res) => {
|
||||||
@@ -655,6 +894,19 @@ export const methodName = async (req, res) => {
|
|||||||
|
|
||||||
**Účel:** Definícia endpointov, middleware, validácia
|
**Účel:** Definícia endpointov, middleware, validácia
|
||||||
|
|
||||||
|
### Zoznam Route Files:
|
||||||
|
1. **admin.routes.js** - User management (Auth + Admin role)
|
||||||
|
2. **auth.routes.js** - Login, set password, link email (Mixed public/protected)
|
||||||
|
3. **company.routes.js** - Firmy + nested notes (Auth only)
|
||||||
|
4. **contact.routes.js** - Kontakty (Auth only)
|
||||||
|
5. **crm-email.routes.js** - Emaily (Auth only)
|
||||||
|
6. **email-account.routes.js** - JMAP účty (Auth only)
|
||||||
|
7. **note.routes.js** - Standalone poznámky (Auth only, nevyužité)
|
||||||
|
8. **project.routes.js** - Projekty + notes + team (Auth only)
|
||||||
|
9. **todo.routes.js** - Úlohy (Auth only)
|
||||||
|
10. **time-tracking.routes.js** - Time tracking (Auth only)
|
||||||
|
11. **timesheet.routes.js** - Timesheets upload/download (Auth, admin for /all)
|
||||||
|
|
||||||
### Štruktúra route file:
|
### Štruktúra route file:
|
||||||
```javascript
|
```javascript
|
||||||
import express from 'express'
|
import express from 'express'
|
||||||
@@ -697,6 +949,12 @@ export default router
|
|||||||
|
|
||||||
## UTILS
|
## UTILS
|
||||||
|
|
||||||
|
### Zoznam Utility Files:
|
||||||
|
1. **errors.js** - Custom error classes + formatting
|
||||||
|
2. **jwt.js** - JWT token generation and validation
|
||||||
|
3. **logger.js** - Colored console logging
|
||||||
|
4. **password.js** - Password hashing, encryption, generation
|
||||||
|
|
||||||
### 1. errors.js
|
### 1. errors.js
|
||||||
**Účel:** Custom error classy a formatting
|
**Účel:** Custom error classy a formatting
|
||||||
|
|
||||||
@@ -1500,14 +1758,40 @@ Order: DESC by startTime
|
|||||||
```
|
```
|
||||||
Účel: Mesačný prehľad time entries
|
Účel: Mesačný prehľad time entries
|
||||||
Params: year (YYYY), month (1-12)
|
Params: year (YYYY), month (1-12)
|
||||||
|
Query: userId (optional, admin only – ak je zadaný, načítava sa daný používateľ)
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
Response: Array of entries pre daný mesiac
|
Response: Array of entries pre daný mesiac
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### POST /api/time-tracking/month/:year/:month/generate
|
||||||
|
```
|
||||||
|
Účel: Vygenerovať mesačný timesheet (Excel XLSX)
|
||||||
|
Params: year (YYYY), month (1-12)
|
||||||
|
Query: userId (optional, admin only - generate pre iného usera)
|
||||||
|
Auth: Áno
|
||||||
|
Body: {} (bez payloadu; posiela sa prázdny objekt)
|
||||||
|
Response: {
|
||||||
|
timesheet: { id, fileName, filePath, ... },
|
||||||
|
filePath,
|
||||||
|
entriesCount,
|
||||||
|
totalMinutes,
|
||||||
|
totalHours
|
||||||
|
}
|
||||||
|
Efekt:
|
||||||
|
- Vytvorí Excel súbor s time entries pre daný mesiac
|
||||||
|
- Obsahuje: denné záznamy, projekty, todos, descrip, duration
|
||||||
|
- Summary: daily totals + overall total
|
||||||
|
- Uloží do: uploads/timesheets/{userId}/{year}/{month}/
|
||||||
|
- INSERT INTO timesheets (isGenerated = true)
|
||||||
|
Volá: time-tracking.service.generateMonthlyTimesheet()
|
||||||
|
Admin feature: Admin môže generovať timesheet pre iného usera (query param userId)
|
||||||
|
```
|
||||||
|
|
||||||
#### GET /api/time-tracking/stats/monthly/:year/:month
|
#### GET /api/time-tracking/stats/monthly/:year/:month
|
||||||
```
|
```
|
||||||
Účel: Mesačné štatistiky
|
Účel: Mesačné štatistiky
|
||||||
Params: year (YYYY), month (1-12)
|
Params: year (YYYY), month (1-12)
|
||||||
|
Query: userId (optional, admin only – ak je zadaný, načítava sa daný používateľ)
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
Response: {
|
Response: {
|
||||||
totalMinutes,
|
totalMinutes,
|
||||||
@@ -1566,39 +1850,70 @@ Validation:
|
|||||||
|
|
||||||
#### POST /api/timesheets/upload
|
#### POST /api/timesheets/upload
|
||||||
```
|
```
|
||||||
Účel: Upload timesheet file
|
Účel: Upload timesheet file (manuálne nahraný PDF/Excel)
|
||||||
Content-Type: multipart/form-data
|
Content-Type: multipart/form-data
|
||||||
Form: file (PDF/Excel), year (YYYY), month (1-12)
|
Form: file (PDF/Excel), year (YYYY), month (1-12)
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
Validation: Max 10MB
|
Validation:
|
||||||
|
- Max 10MB
|
||||||
|
- Allowed types: PDF, Excel (xlsx, xls)
|
||||||
|
Efekt:
|
||||||
|
- Uloží file do: uploads/timesheets/{userId}/{year}/{month}/
|
||||||
|
- Generate unique filename: {name}-{timestamp}-{random}.ext
|
||||||
|
- INSERT INTO timesheets (isGenerated = false)
|
||||||
|
Response: { timesheet object }
|
||||||
```
|
```
|
||||||
|
|
||||||
#### GET /api/timesheets/my
|
#### GET /api/timesheets/my?year=YYYY&month=M
|
||||||
```
|
```
|
||||||
Účel: Moje timesheets
|
Účel: Moje timesheets (uploaded + generated)
|
||||||
|
Query: year, month (both optional)
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
|
Response: { timesheets: [...], count }
|
||||||
|
Order: DESC by uploadedAt
|
||||||
```
|
```
|
||||||
|
|
||||||
#### GET /api/timesheets/all
|
#### GET /api/timesheets/all?userId=uuid&year=YYYY&month=M
|
||||||
```
|
```
|
||||||
Účel: Všetky timesheets (admin)
|
Účel: Všetky timesheets všetkých userov (admin)
|
||||||
|
Query: userId, year, month (all optional)
|
||||||
Auth: Áno (admin only)
|
Auth: Áno (admin only)
|
||||||
|
Response: { timesheets: [...with user info...], count }
|
||||||
|
Includes: userId, username, firstName, lastName (LEFT JOIN users)
|
||||||
|
Order: DESC by uploadedAt
|
||||||
```
|
```
|
||||||
|
|
||||||
#### GET /api/timesheets/:timesheetId/download
|
#### GET /api/timesheets/:timesheetId/download
|
||||||
```
|
```
|
||||||
Účel: Stiahnuť timesheet file
|
Účel: Stiahnuť timesheet file
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
Response: File download
|
Permissions: Owner OR admin
|
||||||
|
Response: File download (res.download)
|
||||||
|
Errors:
|
||||||
|
- 404: Timesheet nenájdený alebo súbor neexistuje
|
||||||
|
- 403: Nemáte oprávnenie (nie vlastník ani admin)
|
||||||
```
|
```
|
||||||
|
|
||||||
#### DELETE /api/timesheets/:timesheetId
|
#### DELETE /api/timesheets/:timesheetId
|
||||||
```
|
```
|
||||||
Účel: Zmazať timesheet
|
Účel: Zmazať timesheet
|
||||||
Auth: Áno
|
Auth: Áno
|
||||||
Efekt: Delete file + DB record
|
Permissions: Owner OR admin
|
||||||
|
Efekt:
|
||||||
|
- Delete file from filesystem (fs.unlink)
|
||||||
|
- DELETE FROM timesheets
|
||||||
|
- Continue even if file deletion fails (log error)
|
||||||
|
Errors:
|
||||||
|
- 404: Timesheet nenájdený
|
||||||
|
- 403: Nemáte oprávnenie
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**POZNÁMKA:**
|
||||||
|
- Timesheets môžu byť **uploaded** (manuálne PDF/Excel) alebo **generated** (auto Excel z time entries)
|
||||||
|
- Field `isGenerated` rozlišuje typ: `true` = auto-generated, `false` = manually uploaded
|
||||||
|
- Obe typy sa ukladajú do rovnakej tabuľky `timesheets` a rovnakého adresára
|
||||||
|
- Generated timesheets sa vytvárajú cez `POST /api/time-tracking/month/:year/:month/generate`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## VZŤAHY MEDZI SLUŽBAMI
|
## VZŤAHY MEDZI SLUŽBAMI
|
||||||
@@ -1981,4 +2296,38 @@ console.log('[DEBUG] JMAP validation:', valid);
|
|||||||
**Vytvorené:** 2025-11-21
|
**Vytvorené:** 2025-11-21
|
||||||
**Posledná aktualizácia:** 2025-11-24
|
**Posledná aktualizácia:** 2025-11-24
|
||||||
**Autor:** CRM Server Team
|
**Autor:** CRM Server Team
|
||||||
**Kontakt:** crm-server documentation
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CHANGELOG
|
||||||
|
|
||||||
|
### 2025-11-24 - Additions
|
||||||
|
**Pridané sekcie:**
|
||||||
|
1. **VALIDATORS** - Kompletná dokumentácia všetkých Zod schemas
|
||||||
|
- auth.validators.js (login, password, user creation)
|
||||||
|
- crm.validators.js (company, project, todo, note, time tracking)
|
||||||
|
- email-account.validators.js (JMAP accounts)
|
||||||
|
|
||||||
|
2. **SERVICES** - Doplnené chýbajúce metódy
|
||||||
|
- time-tracking.service.generateMonthlyTimesheet() - Excel XLSX generation
|
||||||
|
|
||||||
|
3. **CONTROLLERS** - Pridaný chýbajúci controller
|
||||||
|
- timesheet.controller.js - File upload/download (bez service layer)
|
||||||
|
|
||||||
|
4. **ROUTES** - Kompletný zoznam všetkých route files
|
||||||
|
- 11 route files s uvedením middleware requirements
|
||||||
|
|
||||||
|
5. **API ROUTES** - Doplnené chýbajúce endpointy
|
||||||
|
- POST /api/time-tracking/month/:year/:month/generate - Generate Excel timesheet
|
||||||
|
- GET /api/timesheets/my - Detail s filters (year, month)
|
||||||
|
- GET /api/timesheets/all - Admin endpoint s filters
|
||||||
|
- DELETE /api/timesheets/:timesheetId - Permission checks
|
||||||
|
|
||||||
|
6. **UTILS** - Zoznam všetkých utility files (boli už zdokumentované)
|
||||||
|
|
||||||
|
**Upresnenia:**
|
||||||
|
- Timesheet service NEEXISTUJE - logika priamo v controlleri
|
||||||
|
- isGenerated flag rozlišuje uploaded vs generated timesheets
|
||||||
|
- Admin môže generovať timesheet pre iného usera (query param userId)
|
||||||
|
- Empty string handling vo validátoroch: `.or(z.literal(''))` pattern
|
||||||
|
- Optional UUID preprocessing v time tracking schemas
|
||||||
|
|||||||
@@ -123,10 +123,8 @@ export const getAllUsers = async (req, res) => {
|
|||||||
|
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
|
||||||
users: allUsers,
|
|
||||||
count: allUsers.length,
|
count: allUsers.length,
|
||||||
},
|
data: allUsers,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
const errorResponse = formatErrorResponse(error, process.env.NODE_ENV === 'development');
|
||||||
|
|||||||
@@ -113,10 +113,12 @@ export const getAllTimeEntries = async (req, res) => {
|
|||||||
export const getMonthlyTimeEntries = async (req, res) => {
|
export const getMonthlyTimeEntries = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
|
const userRole = req.user.role;
|
||||||
|
const targetUserId = userRole === 'admin' && req.query.userId ? req.query.userId : userId;
|
||||||
const { year, month } = req.params;
|
const { year, month } = req.params;
|
||||||
|
|
||||||
const entries = await timeTrackingService.getMonthlyTimeEntries(
|
const entries = await timeTrackingService.getMonthlyTimeEntries(
|
||||||
userId,
|
targetUserId,
|
||||||
parseInt(year),
|
parseInt(year),
|
||||||
parseInt(month)
|
parseInt(month)
|
||||||
);
|
);
|
||||||
@@ -139,10 +141,12 @@ export const getMonthlyTimeEntries = async (req, res) => {
|
|||||||
export const generateMonthlyTimesheet = async (req, res) => {
|
export const generateMonthlyTimesheet = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
|
const userRole = req.user.role;
|
||||||
|
const targetUserId = userRole === 'admin' && req.query.userId ? req.query.userId : userId;
|
||||||
const { year, month } = req.params;
|
const { year, month } = req.params;
|
||||||
|
|
||||||
const result = await timeTrackingService.generateMonthlyTimesheet(
|
const result = await timeTrackingService.generateMonthlyTimesheet(
|
||||||
userId,
|
targetUserId,
|
||||||
parseInt(year),
|
parseInt(year),
|
||||||
parseInt(month)
|
parseInt(month)
|
||||||
);
|
);
|
||||||
@@ -253,10 +257,12 @@ export const deleteTimeEntry = async (req, res) => {
|
|||||||
export const getMonthlyStats = async (req, res) => {
|
export const getMonthlyStats = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const userId = req.userId;
|
const userId = req.userId;
|
||||||
|
const userRole = req.user.role;
|
||||||
|
const targetUserId = userRole === 'admin' && req.query.userId ? req.query.userId : userId;
|
||||||
const { year, month } = req.params;
|
const { year, month } = req.params;
|
||||||
|
|
||||||
const stats = await timeTrackingService.getMonthlyStats(
|
const stats = await timeTrackingService.getMonthlyStats(
|
||||||
userId,
|
targetUserId,
|
||||||
parseInt(year),
|
parseInt(year),
|
||||||
parseInt(month)
|
parseInt(month)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,9 +9,16 @@ import { z } from 'zod';
|
|||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Všetky admin routes vyžadujú autentifikáciu a admin rolu
|
* Routes accessible to all authenticated users
|
||||||
*/
|
*/
|
||||||
router.use(authenticate);
|
router.use(authenticate);
|
||||||
|
|
||||||
|
// Zoznam všetkých userov (dostupné pre všetkých autentifikovaných používateľov)
|
||||||
|
router.get('/users', adminController.getAllUsers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Admin-only routes
|
||||||
|
*/
|
||||||
router.use(requireAdmin);
|
router.use(requireAdmin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,9 +28,6 @@ router.use(requireAdmin);
|
|||||||
// Vytvorenie nového usera
|
// Vytvorenie nového usera
|
||||||
router.post('/users', validateBody(createUserSchema), adminController.createUser);
|
router.post('/users', validateBody(createUserSchema), adminController.createUser);
|
||||||
|
|
||||||
// Zoznam všetkých userov
|
|
||||||
router.get('/users', adminController.getAllUsers);
|
|
||||||
|
|
||||||
// Získanie konkrétneho usera
|
// Získanie konkrétneho usera
|
||||||
router.get(
|
router.get(
|
||||||
'/users/:userId',
|
'/users/:userId',
|
||||||
|
|||||||
Reference in New Issue
Block a user