feat: AI Kurzy module, project/service documents, services SQL import

- Add AI Kurzy module with courses, participants, and registrations management
- Add project documents and service documents features
- Add service folders for document organization
- Add SQL import queries for services from firmy.slovensko.ai
- Update todo notifications and group messaging
- Various API improvements and bug fixes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
richardtekula
2026-01-21 11:32:49 +01:00
parent d9f16ad0a6
commit 4089bb4be2
37 changed files with 7514 additions and 35 deletions

View File

@@ -0,0 +1,193 @@
CREATE TYPE "public"."forma_kurzu_enum" AS ENUM('prezencne', 'online', 'hybridne');--> statement-breakpoint
CREATE TYPE "public"."stav_registracie_enum" AS ENUM('potencialny', 'registrovany', 'potvrdeny', 'absolvoval', 'zruseny');--> statement-breakpoint
CREATE TYPE "public"."typ_prilohy_enum" AS ENUM('certifikat', 'faktura', 'prihlaska', 'doklad_o_platbe', 'ine');--> statement-breakpoint
CREATE TABLE "chat_group_members" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"group_id" uuid NOT NULL,
"user_id" uuid NOT NULL,
"joined_at" timestamp DEFAULT now() NOT NULL,
"last_read_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "chat_group_member_unique" UNIQUE("group_id","user_id")
);
--> statement-breakpoint
CREATE TABLE "chat_groups" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"created_by_id" uuid,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "company_documents" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"company_id" uuid NOT NULL,
"file_name" text NOT NULL,
"original_name" text NOT NULL,
"file_path" text NOT NULL,
"file_type" text NOT NULL,
"file_size" integer NOT NULL,
"description" text,
"uploaded_by" uuid,
"uploaded_at" timestamp DEFAULT now() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "email_signatures" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"full_name" text,
"position" text,
"phone" text,
"email" text,
"company_name" text,
"website" text,
"is_enabled" boolean DEFAULT true NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "email_signatures_user_id_unique" UNIQUE("user_id")
);
--> statement-breakpoint
CREATE TABLE "group_messages" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"group_id" uuid NOT NULL,
"sender_id" uuid,
"content" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "kurzy" (
"id" serial PRIMARY KEY NOT NULL,
"nazov" varchar(255) NOT NULL,
"typ_kurzu" varchar(100) NOT NULL,
"popis" text,
"cena" numeric(10, 2) NOT NULL,
"max_kapacita" integer,
"aktivny" boolean DEFAULT true NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "prilohy" (
"id" serial PRIMARY KEY NOT NULL,
"registracia_id" integer NOT NULL,
"nazov_suboru" varchar(255) NOT NULL,
"typ_prilohy" "typ_prilohy_enum" DEFAULT 'ine' NOT NULL,
"cesta_k_suboru" varchar(500) NOT NULL,
"mime_type" varchar(100),
"velkost_suboru" bigint,
"popis" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "project_documents" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"project_id" uuid NOT NULL,
"file_name" text NOT NULL,
"original_name" text NOT NULL,
"file_path" text NOT NULL,
"file_type" text NOT NULL,
"file_size" integer NOT NULL,
"description" text,
"uploaded_by" uuid,
"uploaded_at" timestamp DEFAULT now() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "push_subscriptions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"endpoint" text NOT NULL,
"p256dh" text NOT NULL,
"auth" text NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
CONSTRAINT "push_subscription_endpoint_unique" UNIQUE("user_id","endpoint")
);
--> statement-breakpoint
CREATE TABLE "registracie" (
"id" serial PRIMARY KEY NOT NULL,
"kurz_id" integer NOT NULL,
"ucastnik_id" integer NOT NULL,
"datum_od" date,
"datum_do" date,
"forma_kurzu" "forma_kurzu_enum" DEFAULT 'prezencne' NOT NULL,
"pocet_ucastnikov" integer DEFAULT 1 NOT NULL,
"faktura_cislo" varchar(100),
"faktura_vystavena" boolean DEFAULT false NOT NULL,
"zaplatene" boolean DEFAULT false NOT NULL,
"stav" "stav_registracie_enum" DEFAULT 'registrovany' NOT NULL,
"poznamka" text,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "service_documents" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"folder_id" uuid NOT NULL,
"file_name" text NOT NULL,
"original_name" text NOT NULL,
"file_path" text NOT NULL,
"file_type" text NOT NULL,
"file_size" integer NOT NULL,
"description" text,
"uploaded_by" uuid,
"uploaded_at" timestamp DEFAULT now() NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "service_folders" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"created_by" uuid,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "services" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"name" text NOT NULL,
"price" text NOT NULL,
"description" text,
"created_by" uuid,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "ucastnici" (
"id" serial PRIMARY KEY NOT NULL,
"titul" varchar(50),
"meno" varchar(100) NOT NULL,
"priezvisko" varchar(100) NOT NULL,
"email" varchar(255) NOT NULL,
"telefon" varchar(50),
"firma" varchar(255),
"mesto" varchar(100),
"ulica" varchar(255),
"psc" varchar(10),
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "ucastnici_email_unique" UNIQUE("email")
);
--> statement-breakpoint
ALTER TABLE "personal_contacts" ALTER COLUMN "phone" DROP NOT NULL;--> statement-breakpoint
ALTER TABLE "todos" ADD COLUMN "completed_notified_at" timestamp;--> statement-breakpoint
ALTER TABLE "chat_group_members" ADD CONSTRAINT "chat_group_members_group_id_chat_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."chat_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "chat_group_members" ADD CONSTRAINT "chat_group_members_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "chat_groups" ADD CONSTRAINT "chat_groups_created_by_id_users_id_fk" FOREIGN KEY ("created_by_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "company_documents" ADD CONSTRAINT "company_documents_company_id_companies_id_fk" FOREIGN KEY ("company_id") REFERENCES "public"."companies"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "company_documents" ADD CONSTRAINT "company_documents_uploaded_by_users_id_fk" FOREIGN KEY ("uploaded_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "email_signatures" ADD CONSTRAINT "email_signatures_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "group_messages" ADD CONSTRAINT "group_messages_group_id_chat_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."chat_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "group_messages" ADD CONSTRAINT "group_messages_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "prilohy" ADD CONSTRAINT "prilohy_registracia_id_registracie_id_fk" FOREIGN KEY ("registracia_id") REFERENCES "public"."registracie"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "project_documents" ADD CONSTRAINT "project_documents_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "project_documents" ADD CONSTRAINT "project_documents_uploaded_by_users_id_fk" FOREIGN KEY ("uploaded_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "push_subscriptions" ADD CONSTRAINT "push_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "registracie" ADD CONSTRAINT "registracie_kurz_id_kurzy_id_fk" FOREIGN KEY ("kurz_id") REFERENCES "public"."kurzy"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "registracie" ADD CONSTRAINT "registracie_ucastnik_id_ucastnici_id_fk" FOREIGN KEY ("ucastnik_id") REFERENCES "public"."ucastnici"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "service_documents" ADD CONSTRAINT "service_documents_folder_id_service_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "public"."service_folders"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "service_documents" ADD CONSTRAINT "service_documents_uploaded_by_users_id_fk" FOREIGN KEY ("uploaded_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "service_folders" ADD CONSTRAINT "service_folders_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "services" ADD CONSTRAINT "services_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
CREATE UNIQUE INDEX "registracie_kurz_ucastnik_idx" ON "registracie" USING btree ("kurz_id","ucastnik_id");--> statement-breakpoint
CREATE UNIQUE INDEX "ucastnici_email_idx" ON "ucastnici" USING btree ("email");

View File

@@ -0,0 +1,8 @@
-- Add lastReadAt column to chat_group_members for tracking unread group messages
ALTER TABLE chat_group_members
ADD COLUMN IF NOT EXISTS last_read_at TIMESTAMP DEFAULT NOW() NOT NULL;
-- Update existing records to have current timestamp as lastReadAt
UPDATE chat_group_members
SET last_read_at = NOW()
WHERE last_read_at IS NULL;

View File

@@ -0,0 +1,17 @@
-- Create project_documents table
CREATE TABLE IF NOT EXISTS project_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
file_name TEXT NOT NULL,
original_name TEXT NOT NULL,
file_path TEXT NOT NULL,
file_type TEXT NOT NULL,
file_size INTEGER NOT NULL,
description TEXT,
uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL,
uploaded_at TIMESTAMP DEFAULT NOW() NOT NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL
);
-- Create index for faster lookups
CREATE INDEX IF NOT EXISTS idx_project_documents_project_id ON project_documents(project_id);

View File

@@ -0,0 +1,26 @@
-- Create service_folders table
CREATE TABLE IF NOT EXISTS service_folders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_by UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL,
updated_at TIMESTAMP DEFAULT NOW() NOT NULL
);
-- Create service_documents table
CREATE TABLE IF NOT EXISTS service_documents (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
folder_id UUID NOT NULL REFERENCES service_folders(id) ON DELETE CASCADE,
file_name TEXT NOT NULL,
original_name TEXT NOT NULL,
file_path TEXT NOT NULL,
file_type TEXT NOT NULL,
file_size INTEGER NOT NULL,
description TEXT,
uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL,
uploaded_at TIMESTAMP DEFAULT NOW() NOT NULL,
created_at TIMESTAMP DEFAULT NOW() NOT NULL
);
-- Create indexes for faster lookups
CREATE INDEX IF NOT EXISTS idx_service_documents_folder_id ON service_documents(folder_id);

View File

@@ -0,0 +1,88 @@
-- Create enums for AI Courses
DO $$ BEGIN
CREATE TYPE forma_kurzu_enum AS ENUM ('prezencne', 'online', 'hybridne');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
CREATE TYPE stav_registracie_enum AS ENUM ('potencialny', 'registrovany', 'potvrdeny', 'absolvoval', 'zruseny');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
DO $$ BEGIN
CREATE TYPE typ_prilohy_enum AS ENUM ('certifikat', 'faktura', 'prihlaska', 'doklad_o_platbe', 'ine');
EXCEPTION
WHEN duplicate_object THEN null;
END $$;
-- Create kurzy table (AI courses)
CREATE TABLE IF NOT EXISTS kurzy (
id SERIAL PRIMARY KEY,
nazov VARCHAR(255) NOT NULL,
typ_kurzu VARCHAR(100) NOT NULL,
popis TEXT,
cena NUMERIC(10, 2) NOT NULL,
datum_od DATE NOT NULL,
datum_do DATE NOT NULL,
max_kapacita INTEGER,
aktivny BOOLEAN DEFAULT TRUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
-- Create ucastnici table (participants)
CREATE TABLE IF NOT EXISTS ucastnici (
id SERIAL PRIMARY KEY,
titul VARCHAR(50),
meno VARCHAR(100) NOT NULL,
priezvisko VARCHAR(100) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
telefon VARCHAR(50),
firma VARCHAR(255),
mesto VARCHAR(100),
ulica VARCHAR(255),
psc VARCHAR(10),
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS ucastnici_email_idx ON ucastnici(email);
-- Create registracie table (registrations)
CREATE TABLE IF NOT EXISTS registracie (
id SERIAL PRIMARY KEY,
kurz_id INTEGER NOT NULL REFERENCES kurzy(id) ON DELETE CASCADE,
ucastnik_id INTEGER NOT NULL REFERENCES ucastnici(id) ON DELETE CASCADE,
forma_kurzu forma_kurzu_enum DEFAULT 'prezencne' NOT NULL,
pocet_ucastnikov INTEGER DEFAULT 1 NOT NULL,
faktura_cislo VARCHAR(100),
faktura_vystavena BOOLEAN DEFAULT FALSE NOT NULL,
zaplatene BOOLEAN DEFAULT FALSE NOT NULL,
stav stav_registracie_enum DEFAULT 'registrovany' NOT NULL,
poznamka TEXT,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS registracie_kurz_ucastnik_idx ON registracie(kurz_id, ucastnik_id);
-- Create prilohy table (attachments)
CREATE TABLE IF NOT EXISTS prilohy (
id SERIAL PRIMARY KEY,
registracia_id INTEGER NOT NULL REFERENCES registracie(id) ON DELETE CASCADE,
nazov_suboru VARCHAR(255) NOT NULL,
typ_prilohy typ_prilohy_enum DEFAULT 'ine' NOT NULL,
cesta_k_suboru VARCHAR(500) NOT NULL,
mime_type VARCHAR(100),
velkost_suboru BIGINT,
popis TEXT,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL
);
-- Create indexes for faster lookups
CREATE INDEX IF NOT EXISTS idx_registracie_kurz_id ON registracie(kurz_id);
CREATE INDEX IF NOT EXISTS idx_registracie_ucastnik_id ON registracie(ucastnik_id);
CREATE INDEX IF NOT EXISTS idx_prilohy_registracia_id ON prilohy(registracia_id);

View File

@@ -0,0 +1,3 @@
-- Add completed_notified_at column to todos table
-- This tracks when the creator was notified about a todo completion
ALTER TABLE todos ADD COLUMN IF NOT EXISTS completed_notified_at TIMESTAMP;

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,13 @@
"when": 1768469306890,
"tag": "0000_fat_unus",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1768990516243,
"tag": "0001_living_natasha_romanoff",
"breakpoints": true
}
]
}