Rebuild admin pages around DataTable/AdminModal; restructure categories/groups and v2 groups API
Remove the generic $lib/svelte-admin CRUD layer and AdminTable.svelte; each admin route now owns its own form/modal logic. Add DataTable.svelte, superform-errors.ts, and profile-specific modal/field components. Flatten proclamation categories/groups and drop subscription levels; add the v2 groups API; update v1/v2 endpoints, serializers, auth, and regenerated Prisma client with the new migrations. Document the overhaul plan in docs/ADMIN_OVERHAUL.md.
This commit is contained in:
+115
@@ -0,0 +1,115 @@
|
||||
-- Flatten ProclamationCategory hierarchy into a flat list and introduce
|
||||
-- ProclamationGroup (formerly child sub-categories).
|
||||
--
|
||||
-- This migration is intentionally NON-DESTRUCTIVE:
|
||||
-- * ProclamationGroup / Proclamation.groupId are added additively.
|
||||
-- * Former child categories are ARCHIVED to ProclamationCategoryLegacy
|
||||
-- before the hierarchy (parentId) is removed, so no category data is lost.
|
||||
-- * Subscription-level resources (SubscriptionLevel,
|
||||
-- ProclamationCategorySubscriptionLevel, Proclamation.subscriptionLevelId,
|
||||
-- LicenseKey.level) are LEFT IN PLACE as unmanaged legacy data. They are no
|
||||
-- longer modeled by the Prisma schema and are ignored by the application,
|
||||
-- but their data is preserved and no DROP is attempted on tables the deploy
|
||||
-- role may not own.
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ProclamationGroup" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT "ProclamationGroup_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ProclamationGroup_name_key" ON "ProclamationGroup"("name");
|
||||
|
||||
-- Seed groups from every distinct former sub-category name
|
||||
INSERT INTO "ProclamationGroup" ("name", "sortOrder")
|
||||
SELECT "name",
|
||||
ROW_NUMBER() OVER (ORDER BY "name") - 1
|
||||
FROM (
|
||||
SELECT DISTINCT "name" FROM "ProclamationCategory" WHERE "parentId" IS NOT NULL
|
||||
) AS d;
|
||||
|
||||
-- AddColumn (nullable during backfill)
|
||||
ALTER TABLE "Proclamation" ADD COLUMN "groupId" INTEGER;
|
||||
|
||||
-- Backfill: former child categories become the proclamation's group; the
|
||||
-- parent becomes its flat category.
|
||||
UPDATE "Proclamation" AS p
|
||||
SET "groupId" = g."id",
|
||||
"categoryId" = c."parentId"
|
||||
FROM "ProclamationCategory" AS c
|
||||
JOIN "ProclamationGroup" AS g ON g."name" = c."name"
|
||||
WHERE p."categoryId" = c."id" AND c."parentId" IS NOT NULL;
|
||||
|
||||
-- Backfill: proclamations that had no group (e.g. 'Custom (Client Specific)')
|
||||
-- default to the General group, if it exists.
|
||||
UPDATE "Proclamation"
|
||||
SET "groupId" = (SELECT "id" FROM "ProclamationGroup" WHERE "name" = 'General')
|
||||
WHERE "groupId" IS NULL;
|
||||
|
||||
-- Safety net: any proclamation still unmapped takes the first group.
|
||||
UPDATE "Proclamation"
|
||||
SET "groupId" = (SELECT "id" FROM "ProclamationGroup" ORDER BY "sortOrder" LIMIT 1)
|
||||
WHERE "groupId" IS NULL;
|
||||
|
||||
ALTER TABLE "Proclamation" ALTER COLUMN "groupId" SET NOT NULL;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Proclamation" ADD CONSTRAINT "Proclamation_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "ProclamationGroup"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- Archive former child categories before removing them, so their data is
|
||||
-- preserved and recoverable. The legacy table also records each child's
|
||||
-- parent for audit/recovery.
|
||||
CREATE TABLE "ProclamationCategoryLegacy" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"parentId" INTEGER,
|
||||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT "ProclamationCategoryLegacy_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
INSERT INTO "ProclamationCategoryLegacy" ("id", "name", "parentId", "sortOrder")
|
||||
SELECT "id", "name", "parentId", "sortOrder"
|
||||
FROM "ProclamationCategory"
|
||||
WHERE "parentId" IS NOT NULL;
|
||||
|
||||
-- Archive category->subscription mappings for the child categories being
|
||||
-- removed, so the cascade delete below does not destroy this (deprecated)
|
||||
-- mapping data. Requires read access to the join table, but never ownership.
|
||||
CREATE TABLE "ProclamationCategorySubscriptionLevelLegacy" (
|
||||
"categoryId" INTEGER NOT NULL,
|
||||
"subscriptionLevelId" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "ProclamationCategorySubscriptionLevelLegacy_pkey" PRIMARY KEY ("categoryId", "subscriptionLevelId")
|
||||
);
|
||||
|
||||
INSERT INTO "ProclamationCategorySubscriptionLevelLegacy" ("categoryId", "subscriptionLevelId")
|
||||
SELECT j."categoryId", j."subscriptionLevelId"
|
||||
FROM "ProclamationCategorySubscriptionLevel" AS j
|
||||
JOIN "ProclamationCategory" AS c ON c."id" = j."categoryId" AND c."parentId" IS NOT NULL;
|
||||
|
||||
-- Remove former child categories (their data is archived above; the
|
||||
-- subscription-level join rows for them are archived above and the
|
||||
-- SubscriptionLevel table itself is left untouched).
|
||||
DELETE FROM "ProclamationCategory" WHERE "parentId" IS NOT NULL;
|
||||
|
||||
-- Drop self-relation
|
||||
ALTER TABLE "ProclamationCategory" DROP CONSTRAINT "ProclamationCategory_parentId_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "ProclamationCategory_parentId_name_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ProclamationCategory" DROP COLUMN "parentId";
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ProclamationCategory_name_key" ON "ProclamationCategory"("name");
|
||||
|
||||
-- Subscription-level resources are intentionally left in place (unmanaged).
|
||||
-- No DROP TABLE / DROP COLUMN / DROP CONSTRAINT is performed here, so this
|
||||
-- migration never requires ownership of pre-existing subscription tables and
|
||||
-- never destroys subscription data.
|
||||
@@ -0,0 +1,28 @@
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "LicenseKey" DROP CONSTRAINT "LicenseKey_userId_fkey";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "Profile_userId_key";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "LicenseKey" ADD COLUMN "email" TEXT,
|
||||
ALTER COLUMN "userId" DROP NOT NULL;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Profile" ADD COLUMN "email" TEXT,
|
||||
ALTER COLUMN "userId" DROP NOT NULL;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LicenseKey_userId_idx" ON "LicenseKey"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "LicenseKey_email_idx" ON "LicenseKey"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Profile_userId_idx" ON "Profile"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Profile_email_idx" ON "Profile"("email");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "LicenseKey" ADD CONSTRAINT "LicenseKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
Reference in New Issue
Block a user