import { pgTable, text, timestamp, boolean, uuid, pgEnum, unique, integer } from 'drizzle-orm/pg-core'; // Enums export const roleEnum = pgEnum('role', ['admin', 'member']); export const projectStatusEnum = pgEnum('project_status', ['active', 'completed', 'on_hold', 'cancelled']); export const todoStatusEnum = pgEnum('todo_status', ['pending', 'in_progress', 'completed', 'cancelled']); export const todoPriorityEnum = pgEnum('todo_priority', ['low', 'medium', 'high', 'urgent']); export const companyStatusEnum = pgEnum('company_status', ['registered', 'lead', 'customer', 'inactive']); // Users table - používatelia systému export const users = pgTable('users', { id: uuid('id').primaryKey().defaultRandom(), username: text('username').notNull().unique(), firstName: text('first_name'), lastName: text('last_name'), password: text('password'), // bcrypt hash (null ak ešte nenastavené) tempPassword: text('temp_password'), // dočasné heslo (bcrypt hash) changedPassword: boolean('changed_password').default(false), role: roleEnum('role').default('member').notNull(), lastLogin: timestamp('last_login'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Email Accounts table - emailové účty (môžu byť zdieľané medzi viacerými používateľmi) export const emailAccounts = pgTable('email_accounts', { id: uuid('id').primaryKey().defaultRandom(), email: text('email').notNull().unique(), // Email adresa emailPassword: text('email_password').notNull(), // Heslo k emailovému účtu (encrypted) jmapAccountId: text('jmap_account_id').notNull(), // JMAP account ID z truemail isActive: boolean('is_active').default(true).notNull(), // či je účet aktívny createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // User Email Accounts - many-to-many medzi users a emailAccounts // Umožňuje zdieľať email účty medzi viacerými používateľmi export const userEmailAccounts = pgTable('user_email_accounts', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), emailAccountId: uuid('email_account_id').references(() => emailAccounts.id, { onDelete: 'cascade' }).notNull(), isPrimary: boolean('is_primary').default(false).notNull(), // primárny email účet pre daného usera addedAt: timestamp('added_at').defaultNow().notNull(), }, (table) => ({ // Jeden user môže mať email account len raz userEmailUnique: unique('user_email_unique').on(table.userId, table.emailAccountId), })); // Audit logs - kompletný audit trail všetkých akcií export const auditLogs = pgTable('audit_logs', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id').references(() => users.id, { onDelete: 'set null' }), action: text('action').notNull(), // 'login', 'password_change', 'email_linked', 'role_change', atď. resource: text('resource').notNull(), // 'user', 'auth', atď. resourceId: text('resource_id'), // ID ovplyvneného zdroja oldValue: text('old_value'), // JSON string starých hodnôt newValue: text('new_value'), // JSON string nových hodnôt ipAddress: text('ip_address'), userAgent: text('user_agent'), success: boolean('success').default(true).notNull(), errorMessage: text('error_message'), createdAt: timestamp('created_at').defaultNow().notNull(), }); // Contacts table - kontakty patriace k emailovému účtu // Kontakty sú zdieľané medzi všetkými používateľmi, ktorí majú prístup k danému email accountu export const contacts = pgTable('contacts', { id: uuid('id').primaryKey().defaultRandom(), emailAccountId: uuid('email_account_id').references(() => emailAccounts.id, { onDelete: 'cascade' }).notNull(), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'set null' }), // kontakt môže byť linknutý k firme email: text('email').notNull(), name: text('name'), notes: text('notes'), addedBy: uuid('added_by').references(() => users.id, { onDelete: 'set null' }), // kto pridal kontakt addedAt: timestamp('added_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }, (table) => ({ // Unique constraint: jeden email môže byť len raz v rámci email accountu accountEmailUnique: unique('account_email_unique').on(table.emailAccountId, table.email), })); // Personal contacts - osobné kontakty používateľa (s voliteľnou väzbou na firmu) export const personalContacts = pgTable('personal_contacts', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'set null' }), // voliteľná väzba na firmu firstName: text('first_name').notNull(), lastName: text('last_name'), phone: text('phone'), // optional email: text('email').notNull(), secondaryEmail: text('secondary_email'), description: text('description'), // popis kontaktu createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }, (table) => ({ personalContactUniqueEmail: unique('personal_contact_user_email').on(table.userId, table.email), })); // Emails table - uložené emaily z JMAP // Emaily patria k email accountu a sú zdieľané medzi všetkými používateľmi s prístupom export const emails = pgTable('emails', { id: uuid('id').primaryKey().defaultRandom(), emailAccountId: uuid('email_account_id').references(() => emailAccounts.id, { onDelete: 'cascade' }).notNull(), contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'set null' }), jmapId: text('jmap_id').unique(), messageId: text('message_id').unique(), threadId: text('thread_id'), inReplyTo: text('in_reply_to'), from: text('from'), to: text('to'), subject: text('subject'), body: text('body'), isRead: boolean('is_read').default(false).notNull(), sentByUserId: uuid('sent_by_user_id').references(() => users.id, { onDelete: 'set null' }), // kto poslal odpoveď (null ak prijatý email) date: timestamp('date'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Companies table - firmy/spoločnosti export const companies = pgTable('companies', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), description: text('description'), address: text('address'), city: text('city'), country: text('country'), phone: text('phone'), email: text('email'), website: text('website'), status: companyStatusEnum('status').default('registered').notNull(), // stav firmy createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Projects table - projekty export const projects = pgTable('projects', { id: uuid('id').primaryKey().defaultRandom(), name: text('name').notNull(), description: text('description'), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }), // projekt môže patriť firme status: projectStatusEnum('status').default('active').notNull(), startDate: timestamp('start_date'), endDate: timestamp('end_date'), createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Company reminders table - pripomienky naviazané na firmu s dátumom export const companyReminders = pgTable('company_remind', { id: uuid('id').primaryKey().defaultRandom(), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }).notNull(), description: text('description').notNull(), dueDate: timestamp('due_date'), // kedy má byť splnená isChecked: boolean('is_checked').default(false).notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Company Users - many-to-many medzi companies a users (tím firmy) export const companyUsers = pgTable('company_users', { id: uuid('id').primaryKey().defaultRandom(), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }).notNull(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), role: text('role'), // napr. 'lead', 'member', 'viewer' - voliteľné addedBy: uuid('added_by').references(() => users.id, { onDelete: 'set null' }), // kto pridal používateľa do firmy addedAt: timestamp('added_at').defaultNow().notNull(), }, (table) => ({ companyUserUnique: unique('company_user_unique').on(table.companyId, table.userId), })); // Project Users - many-to-many medzi projects a users (tím projektu) export const projectUsers = pgTable('project_users', { id: uuid('id').primaryKey().defaultRandom(), projectId: uuid('project_id').references(() => projects.id, { onDelete: 'cascade' }).notNull(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), role: text('role'), // napr. 'lead', 'member', 'viewer' - voliteľné addedBy: uuid('added_by').references(() => users.id, { onDelete: 'set null' }), // kto pridal používateľa do projektu addedAt: timestamp('added_at').defaultNow().notNull(), }, (table) => ({ projectUserUnique: unique('project_user_unique').on(table.projectId, table.userId), })); // Todos table - úlohy/tasky export const todos = pgTable('todos', { id: uuid('id').primaryKey().defaultRandom(), title: text('title').notNull(), description: text('description'), projectId: uuid('project_id').references(() => projects.id, { onDelete: 'cascade' }), // todo môže patriť projektu companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }), // alebo firme status: todoStatusEnum('status').default('pending').notNull(), priority: todoPriorityEnum('priority').default('medium').notNull(), dueDate: timestamp('due_date'), completedAt: timestamp('completed_at'), createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Todo Users - many-to-many medzi todos a users (priradení používatelia) export const todoUsers = pgTable('todo_users', { id: uuid('id').primaryKey().defaultRandom(), todoId: uuid('todo_id').references(() => todos.id, { onDelete: 'cascade' }).notNull(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), assignedBy: uuid('assigned_by').references(() => users.id, { onDelete: 'set null' }), // kto pridal používateľa k todo assignedAt: timestamp('assigned_at').defaultNow().notNull(), }, (table) => ({ todoUserUnique: unique('todo_user_unique').on(table.todoId, table.userId), })); // Notes table - poznámky s voliteľným dátumom a časom splnenia export const notes = pgTable('notes', { id: uuid('id').primaryKey().defaultRandom(), title: text('title'), content: text('content').notNull(), companyId: uuid('company_id').references(() => companies.id, { onDelete: 'cascade' }), // poznámka k firme projectId: uuid('project_id').references(() => projects.id, { onDelete: 'cascade' }), // alebo projektu todoId: uuid('todo_id').references(() => todos.id, { onDelete: 'cascade' }), // alebo todo contactId: uuid('contact_id').references(() => contacts.id, { onDelete: 'cascade' }), // alebo kontaktu dueDate: timestamp('due_date'), // voliteľný dátum a čas splnenia (24h formát) createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Timesheets table - nahrané timesheets od používateľov export const timesheets = pgTable('timesheets', { id: uuid('id').primaryKey().defaultRandom(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), // kto nahral timesheet projectId: uuid('project_id').references(() => projects.id, { onDelete: 'set null' }), // projekt ku ktorému patrí timesheet fileName: text('file_name').notNull(), // originálny názov súboru filePath: text('file_path').notNull(), // cesta k súboru na serveri fileType: text('file_type').notNull(), // 'pdf' alebo 'xlsx' fileSize: integer('file_size').notNull(), // veľkosť súboru v bytoch year: integer('year').notNull(), // rok (napr. 2024) month: integer('month').notNull(), // mesiac (1-12) isGenerated: boolean('is_generated').default(false).notNull(), // či bol súbor vygenerovaný systémom uploadedAt: timestamp('uploaded_at').defaultNow().notNull(), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); // Events table - udalosti v kalendári (meeting/event) export const events = pgTable('events', { id: uuid('id').primaryKey().defaultRandom(), title: text('title').notNull(), description: text('description'), type: text('type').notNull().default('meeting'), // 'meeting' | 'event' start: timestamp('start', { withTimezone: true }).notNull(), end: timestamp('end', { withTimezone: true }).notNull(), createdBy: uuid('created_by').references(() => users.id, { onDelete: 'set null' }), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(), }); // Event Users - many-to-many medzi events a users (kto vidí udalosť) export const eventUsers = pgTable('event_users', { id: uuid('id').primaryKey().defaultRandom(), eventId: uuid('event_id').references(() => events.id, { onDelete: 'cascade' }).notNull(), userId: uuid('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), assignedAt: timestamp('assigned_at').defaultNow().notNull(), }, (table) => ({ eventUserUnique: unique('event_user_unique').on(table.eventId, table.userId), })); // 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(), }); // Messages table - interná komunikácia medzi používateľmi export const messages = pgTable('messages', { id: uuid('id').primaryKey().defaultRandom(), senderId: uuid('sender_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), receiverId: uuid('receiver_id').references(() => users.id, { onDelete: 'cascade' }).notNull(), content: text('content').notNull(), isRead: boolean('is_read').default(false).notNull(), deletedBySender: boolean('deleted_by_sender').default(false).notNull(), // soft delete pre odosielateľa deletedByReceiver: boolean('deleted_by_receiver').default(false).notNull(), // soft delete pre príjemcu createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), });