feat: insights

* fix: migration for newly created self-hosting instances

* fix: build script

* wip

* wip

* wip

* fix: tailwind css
This commit is contained in:
Carl-Gerhard Lindesvärd
2025-12-19 09:37:15 +01:00
committed by GitHub
parent 1e4f02fb5e
commit 5f38560373
48 changed files with 4072 additions and 25 deletions

View File

@@ -0,0 +1,57 @@
-- CreateEnum
CREATE TYPE "public"."InsightState" AS ENUM ('active', 'suppressed', 'closed');
-- CreateTable
CREATE TABLE "public"."project_insights" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"projectId" TEXT NOT NULL,
"moduleKey" TEXT NOT NULL,
"dimensionKey" TEXT NOT NULL,
"windowKind" TEXT NOT NULL,
"state" "public"."InsightState" NOT NULL DEFAULT 'active',
"title" TEXT NOT NULL,
"summary" TEXT,
"payload" JSONB,
"currentValue" DOUBLE PRECISION,
"compareValue" DOUBLE PRECISION,
"changePct" DOUBLE PRECISION,
"direction" TEXT,
"impactScore" DOUBLE PRECISION NOT NULL DEFAULT 0,
"severityBand" TEXT,
"version" INTEGER NOT NULL DEFAULT 1,
"threadId" UUID NOT NULL DEFAULT gen_random_uuid(),
"windowStart" TIMESTAMP(3),
"windowEnd" TIMESTAMP(3),
"firstDetectedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"lastUpdatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"lastSeenAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "project_insights_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "public"."insight_events" (
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
"insightId" UUID NOT NULL,
"eventKind" TEXT NOT NULL,
"changeFrom" JSONB,
"changeTo" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "insight_events_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX "project_insights_projectId_impactScore_idx" ON "public"."project_insights"("projectId", "impactScore" DESC);
-- CreateIndex
CREATE INDEX "project_insights_projectId_moduleKey_windowKind_state_idx" ON "public"."project_insights"("projectId", "moduleKey", "windowKind", "state");
-- CreateIndex
CREATE UNIQUE INDEX "project_insights_projectId_moduleKey_dimensionKey_windowKin_key" ON "public"."project_insights"("projectId", "moduleKey", "dimensionKey", "windowKind", "state");
-- CreateIndex
CREATE INDEX "insight_events_insightId_createdAt_idx" ON "public"."insight_events"("insightId", "createdAt");
-- AddForeignKey
ALTER TABLE "public"."insight_events" ADD CONSTRAINT "insight_events_insightId_fkey" FOREIGN KEY ("insightId") REFERENCES "public"."project_insights"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,9 @@
/*
Warnings:
- Made the column `payload` on table `project_insights` required. This step will fail if there are existing NULL values in that column.
*/
-- AlterTable
ALTER TABLE "public"."project_insights" ALTER COLUMN "payload" SET NOT NULL,
ALTER COLUMN "payload" SET DEFAULT '{}';

View File

@@ -0,0 +1,13 @@
/*
Warnings:
- You are about to drop the column `changePct` on the `project_insights` table. All the data in the column will be lost.
- You are about to drop the column `compareValue` on the `project_insights` table. All the data in the column will be lost.
- You are about to drop the column `currentValue` on the `project_insights` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE "public"."project_insights" DROP COLUMN "changePct",
DROP COLUMN "compareValue",
DROP COLUMN "currentValue",
ADD COLUMN "displayName" TEXT NOT NULL DEFAULT '';

View File

@@ -497,3 +497,58 @@ model Import {
@@map("imports")
}
enum InsightState {
active
suppressed
closed
}
model ProjectInsight {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
projectId String
moduleKey String // e.g. "referrers", "entry-pages"
dimensionKey String // e.g. "referrer:instagram", "page:/pricing"
windowKind String // "yesterday" | "rolling_7d" | "rolling_30d"
state InsightState @default(active)
title String
summary String?
displayName String @default("")
/// [IPrismaProjectInsightPayload]
payload Json @default("{}") // Rendered insight payload (typed)
direction String? // "up" | "down" | "flat"
impactScore Float @default(0)
severityBand String? // "low" | "moderate" | "severe"
version Int @default(1)
threadId String @default(dbgenerated("gen_random_uuid()")) @db.Uuid
windowStart DateTime?
windowEnd DateTime?
firstDetectedAt DateTime @default(now())
lastUpdatedAt DateTime @default(now()) @updatedAt
lastSeenAt DateTime @default(now())
events InsightEvent[]
@@unique([projectId, moduleKey, dimensionKey, windowKind, state])
@@index([projectId, impactScore(sort: Desc)])
@@index([projectId, moduleKey, windowKind, state])
@@map("project_insights")
}
model InsightEvent {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
insightId String @db.Uuid
insight ProjectInsight @relation(fields: [insightId], references: [id], onDelete: Cascade)
eventKind String // "created" | "updated" | "severity_up" | "direction_flip" | "closed" | etc
changeFrom Json?
changeTo Json?
createdAt DateTime @default(now())
@@index([insightId, createdAt])
@@map("insight_events")
}