feat: auth system, scrip scheduler, UI widgets, and new API routes

- Add session-based authentication (login page, middleware, auth context)
- Add cron-like scrip scheduler for time-based conditions
- Add layout builder, scrip wizard, searchable select components
- Add trend chart widget for dashboards
- Add notifications, attachments, queue-permissions API routes
- Add seed-users script
- Update schema with 10 new migrations (0008-0017)
- Apply redesign: Linear-inspired dark theme, conversation-centric UI
- Gitignore runtime data directory

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Gjermund Høsøien Wiggen
2026-06-15 20:42:17 +02:00
parent 1d4dc38d06
commit 70f0924d4b
59 changed files with 21795 additions and 321 deletions

View File

@@ -0,0 +1,12 @@
CREATE TABLE "transaction_attachments" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"transaction_id" uuid NOT NULL,
"filename" text NOT NULL,
"mime_type" text DEFAULT 'application/octet-stream' NOT NULL,
"size_bytes" integer DEFAULT 0 NOT NULL,
"storage_path" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
ALTER TABLE "transaction_attachments" ADD CONSTRAINT "transaction_attachments_transaction_id_transactions_id_fk" FOREIGN KEY ("transaction_id") REFERENCES "public"."transactions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "transaction_attachments_tx_id_idx" ON "transaction_attachments" USING btree ("transaction_id");

View File

@@ -0,0 +1 @@
ALTER TABLE "transaction_attachments" ALTER COLUMN "transaction_id" DROP NOT NULL;

View File

@@ -0,0 +1,15 @@
CREATE TABLE "ticket_links" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"ticket_id" integer NOT NULL,
"target_ticket_id" integer NOT NULL,
"link_type" text NOT NULL,
"creator_id" uuid NOT NULL,
"created_at" timestamp with time zone DEFAULT now(),
CONSTRAINT "ticket_links_ticket_target_type_unique" UNIQUE("ticket_id","target_ticket_id","link_type")
);
--> statement-breakpoint
ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_target_ticket_id_tickets_id_fk" FOREIGN KEY ("target_ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "ticket_links_ticket_id_idx" ON "ticket_links" USING btree ("ticket_id");--> statement-breakpoint
CREATE INDEX "ticket_links_target_ticket_id_idx" ON "ticket_links" USING btree ("target_ticket_id");

View File

@@ -0,0 +1,2 @@
ALTER TABLE "users" ADD COLUMN "password_hash" text;--> statement-breakpoint
ALTER TABLE "users" ADD COLUMN "role" text DEFAULT 'staff' NOT NULL;

View File

@@ -0,0 +1,12 @@
CREATE TABLE "queue_permissions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"queue_id" uuid NOT NULL,
"team_id" uuid NOT NULL,
"right_name" text NOT NULL,
CONSTRAINT "queue_permissions_queue_team_right_unique" UNIQUE("queue_id","team_id","right_name")
);
--> statement-breakpoint
ALTER TABLE "queue_permissions" ADD CONSTRAINT "queue_permissions_queue_id_queues_id_fk" FOREIGN KEY ("queue_id") REFERENCES "public"."queues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "queue_permissions" ADD CONSTRAINT "queue_permissions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "queue_permissions_queue_id_idx" ON "queue_permissions" USING btree ("queue_id");--> statement-breakpoint
CREATE INDEX "queue_permissions_team_id_idx" ON "queue_permissions" USING btree ("team_id");

View File

@@ -0,0 +1,12 @@
CREATE TABLE "user_permissions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"queue_id" uuid NOT NULL,
"user_id" uuid NOT NULL,
"right_name" text NOT NULL,
CONSTRAINT "user_permissions_queue_user_right_unique" UNIQUE("queue_id","user_id","right_name")
);
--> statement-breakpoint
ALTER TABLE "user_permissions" ADD CONSTRAINT "user_permissions_queue_id_queues_id_fk" FOREIGN KEY ("queue_id") REFERENCES "public"."queues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "user_permissions" ADD CONSTRAINT "user_permissions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "user_permissions_queue_id_idx" ON "user_permissions" USING btree ("queue_id");--> statement-breakpoint
CREATE INDEX "user_permissions_user_id_idx" ON "user_permissions" USING btree ("user_id");

View File

@@ -0,0 +1 @@
ALTER TABLE "transactions" ADD COLUMN "time_worked_minutes" integer DEFAULT 0;

View File

@@ -0,0 +1,15 @@
CREATE TABLE "notifications" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"ticket_id" integer,
"type" text NOT NULL,
"title" text NOT NULL,
"body" text,
"read" boolean DEFAULT false NOT NULL,
"created_at" timestamp with time zone DEFAULT now()
);
--> statement-breakpoint
ALTER TABLE "notifications" ADD CONSTRAINT "notifications_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "notifications" ADD CONSTRAINT "notifications_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "notifications_user_id_idx" ON "notifications" USING btree ("user_id");--> statement-breakpoint
CREATE INDEX "notifications_user_read_idx" ON "notifications" USING btree ("user_id","read");

View File

@@ -0,0 +1,12 @@
CREATE TABLE "api_tokens" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"name" text NOT NULL,
"token_hash" text NOT NULL,
"last_used_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now(),
CONSTRAINT "api_tokens_token_hash_unique" UNIQUE("token_hash")
);
--> statement-breakpoint
ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "api_tokens_user_id_idx" ON "api_tokens" USING btree ("user_id");

View File

@@ -0,0 +1 @@
ALTER TABLE "scrips" ADD COLUMN "applicable_trans_types" text;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -57,6 +57,76 @@
"when": 1781009018666,
"tag": "0007_flimsy_roughhouse",
"breakpoints": true
},
{
"idx": 8,
"version": "7",
"when": 1781039674211,
"tag": "0008_sturdy_prism",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1781039770418,
"tag": "0009_tiny_lady_vermin",
"breakpoints": true
},
{
"idx": 10,
"version": "7",
"when": 1781040536590,
"tag": "0010_misty_morg",
"breakpoints": true
},
{
"idx": 11,
"version": "7",
"when": 1781042321413,
"tag": "0011_breezy_tyrannus",
"breakpoints": true
},
{
"idx": 12,
"version": "7",
"when": 1781043175153,
"tag": "0012_living_photon",
"breakpoints": true
},
{
"idx": 13,
"version": "7",
"when": 1781043729230,
"tag": "0013_bored_silvermane",
"breakpoints": true
},
{
"idx": 14,
"version": "7",
"when": 1781045611610,
"tag": "0014_cloudy_siren",
"breakpoints": true
},
{
"idx": 15,
"version": "7",
"when": 1781078349499,
"tag": "0015_tense_patch",
"breakpoints": true
},
{
"idx": 16,
"version": "7",
"when": 1781078511943,
"tag": "0016_famous_maximus",
"breakpoints": true
},
{
"idx": 17,
"version": "7",
"when": 1781095552496,
"tag": "0017_redundant_the_renegades",
"breakpoints": true
}
]
}