Add Time Tracking backend API

Implementovaný kompletný backend pre time tracking:
- Nová tabuľka time_entries s foreign keys na users, projects, todos, companies
- Service layer s business logikou pre CRUD operácie
- Controller pre všetky endpointy
- Validačné schémy pomocou Zod
- Routes s autentifikáciou a validáciou
- Endpointy:
  * POST /api/time-tracking/start - Spustenie timeru
  * POST /api/time-tracking/:id/stop - Zastavenie timeru
  * GET /api/time-tracking/running - Získanie bežiaceho záznamu
  * GET /api/time-tracking/month/:year/:month - Mesačné záznamy
  * GET /api/time-tracking/stats/monthly/:year/:month - Mesačné štatistiky
  * PATCH /api/time-tracking/:id - Aktualizácia záznamu
  * DELETE /api/time-tracking/:id - Zmazanie záznamu
- Podpora pre isEdited flag pri editácii
- Kalkulácia duration v minútach

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
richardtekula
2025-11-24 06:41:39 +01:00
parent ca93b6f2d2
commit 540c1719d3
7 changed files with 843 additions and 0 deletions

View File

@@ -190,3 +190,20 @@ export const timesheets = pgTable('timesheets', {
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
// Time Entries table - sledovanie odpracovaného času používateľov
export const timeEntries = pgTable('time_entries', {
id: uuid('id').primaryKey().defaultRandom(),
userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), // kto trackuje čas
projectId: uuid('project_id').references(() => projects.id, { onDelete: 'set null' }), // na akom projekte (voliteľné)
todoId: uuid('todo_id').references(() => todos.id, { onDelete: 'set null' }), // na akom todo (voliteľné)
companyId: uuid('company_id').references(() => companies.id, { onDelete: 'set null' }), // pre akú firmu (voliteľné)
startTime: timestamp('start_time').notNull(), // kedy začal trackovať
endTime: timestamp('end_time'), // kedy skončil (null ak ešte beží)
duration: integer('duration'), // trvanie v minútach
description: text('description'), // popis práce
isRunning: boolean('is_running').default(false).notNull(), // či práve beží
isEdited: boolean('is_edited').default(false).notNull(), // či bol editovaný
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});