refactor: remove canvas, automations, and custom statuses; simplify notification and status model

This commit is contained in:
2026-08-22 18:00:41 +00:00
parent 7b2cdc3bae
commit ffc50091b1
69 changed files with 834 additions and 6438 deletions
-121
View File
@@ -1,121 +0,0 @@
-- Custom workflow statuses: replace the fixed task_status enum
-- (todo/in_progress/done/cancelled) with per-project status_definitions rows.
--
-- Idempotent — safe to run on every deploy BEFORE `drizzle-kit push`. The push
-- then removes the legacy `tasks.status` column and the `task_status` enum type,
-- which is why the backfill must run first:
--
-- 1. Create the status_category enum + status_definitions table if missing.
-- 2. Seed the four default statuses (todo/in_progress/done/cancelled) for
-- every project that has none yet.
-- 3. Backfill tasks.status_id by joining on the legacy `status` column when it
-- still exists; otherwise fall back to each project's default status.
--
-- Fresh installs (no projects table yet) skip the data steps; the API seeds
-- default statuses when a project is created.
-- ─── 1. status_category enum (Postgres has no CREATE TYPE IF NOT EXISTS) ──────
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'status_category') THEN
CREATE TYPE "status_category" AS ENUM ('todo', 'in_progress', 'done', 'cancelled');
END IF;
END $$;
-- ─── 2. status_definitions table ───────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS "status_definitions" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"project_id" uuid NOT NULL,
"key" text NOT NULL,
"label" text NOT NULL,
"category" "status_category" DEFAULT 'todo' NOT NULL,
"color" text,
"sort_order" integer DEFAULT 0,
"is_default" boolean DEFAULT false,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'status_definitions_project_id_projects_id_fk') THEN
ALTER TABLE "status_definitions" ADD CONSTRAINT "status_definitions_project_id_projects_id_fk"
FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;
END IF;
END $$;
CREATE UNIQUE INDEX IF NOT EXISTS "status_definitions_project_key_idx" ON "status_definitions" USING btree ("project_id","key");
CREATE INDEX IF NOT EXISTS "status_definitions_project_id_idx" ON "status_definitions" USING btree ("project_id");
-- ─── 3. tasks.status_id column ──────────────────────────────────────────────────
ALTER TABLE "tasks" ADD COLUMN IF NOT EXISTS "status_id" uuid;
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'tasks_status_id_status_definitions_id_fk') THEN
ALTER TABLE "tasks" ADD CONSTRAINT "tasks_status_id_status_definitions_id_fk"
FOREIGN KEY ("status_id") REFERENCES "public"."status_definitions"("id") ON DELETE set null ON UPDATE no action;
END IF;
END $$;
CREATE INDEX IF NOT EXISTS "tasks_status_id_idx" ON "tasks" USING btree ("status_id");
-- ─── 4. Seed default statuses per project ───────────────────────────────────────
DO $$
BEGIN
IF to_regclass('public.projects') IS NOT NULL AND to_regclass('public.status_definitions') IS NOT NULL THEN
EXECUTE '
INSERT INTO "status_definitions" ("project_id", "key", "label", "category", "color", "sort_order", "is_default")
SELECT p."id", d."key", d."label", d."category", d."color", d."sort_order", d."is_default"
FROM "projects" p
CROSS JOIN (VALUES
(''todo'', ''Todo'', ''todo'', ''#94a3b8'', 0, true),
(''in_progress'', ''In Progress'', ''in_progress'', ''#3b82f6'', 1, false),
(''done'', ''Done'', ''done'', ''#22c55e'', 2, false),
(''cancelled'', ''Cancelled'', ''cancelled'', ''#ef4444'', 3, false)
) AS d("key", "label", "category", "color", "sort_order", "is_default")
WHERE NOT EXISTS (
SELECT 1 FROM "status_definitions" sd WHERE sd."project_id" = p."id"
)';
END IF;
END $$;
-- ─── 5. Backfill tasks.status_id ────────────────────────────────────────────────
-- Legacy status column still present → map each task to the status with the
-- same key in its project (preserves the old todo/in_progress/done/cancelled).
DO $$
BEGIN
IF EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'tasks' AND column_name = 'status'
) AND EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'tasks' AND column_name = 'status_id'
) THEN
EXECUTE '
UPDATE "tasks" t
SET "status_id" = sd."id"
FROM "status_definitions" sd
WHERE t."project_id" = sd."project_id"
AND t."status"::text = sd."key"
AND t."status_id" IS NULL';
END IF;
END $$;
-- Legacy column already gone (or never existed) → assign each null-status task
-- its project''s default status so nothing is left un-categorized.
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_schema = 'public' AND table_name = 'tasks' AND column_name = 'status'
) THEN
EXECUTE '
UPDATE "tasks" t
SET "status_id" = sd."id"
FROM "status_definitions" sd
WHERE sd."project_id" = t."project_id"
AND sd."is_default" = true
AND t."status_id" IS NULL';
END IF;
END $$;
+4
View File
@@ -0,0 +1,4 @@
DROP TABLE IF EXISTS "canvas_connections" CASCADE;--> statement-breakpoint
DROP TABLE IF EXISTS "canvas_cards" CASCADE;--> statement-breakpoint
DROP TABLE IF EXISTS "canvases" CASCADE;--> statement-breakpoint
DROP TABLE IF EXISTS "reports" CASCADE;
-14
View File
@@ -1,14 +0,0 @@
CREATE TABLE "automation_rules" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"project_id" uuid NOT NULL,
"name" text NOT NULL,
"active" boolean DEFAULT true,
"trigger" jsonb NOT NULL,
"conditions" jsonb DEFAULT '[]'::jsonb,
"actions" jsonb NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "automation_rules" ADD CONSTRAINT "automation_rules_project_id_projects_id_fk" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
CREATE INDEX "automation_rules_project_id_idx" ON "automation_rules" USING btree ("project_id");
-20
View File
@@ -1,20 +0,0 @@
CREATE TABLE "notifications" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"user_id" uuid NOT NULL,
"workspace_id" uuid,
"type" text NOT NULL,
"title" text NOT NULL,
"body" text,
"entity_type" text,
"entity_id" uuid,
"read_at" timestamp with time zone,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"deleted_at" timestamp with time zone
);
--> 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_workspace_id_domains_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."domains"("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_at");--> statement-breakpoint
CREATE INDEX "notifications_workspace_id_idx" ON "notifications" USING btree ("workspace_id");--> statement-breakpoint
CREATE INDEX "notifications_entity_idx" ON "notifications" USING btree ("entity_type","entity_id");
+2 -16
View File
@@ -54,22 +54,8 @@
{
"idx": 7,
"version": "7",
"when": 1786800000000,
"tag": "0007_custom_task_statuses",
"breakpoints": true
},
{
"idx": 8,
"version": "7",
"when": 1786900000000,
"tag": "0008_automation_rules",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1786920000000,
"tag": "0009_notifications",
"when": 1786392464858,
"tag": "0007_drop_canvas_reports",
"breakpoints": true
}
]