Skip to content

Commit 4995339

Browse files
authored
Merge pull request #1 from Paca-AI/feature/implement-customizable-panel
Feature/implement customizable panel
2 parents 5538835 + cfb4d7b commit 4995339

43 files changed

Lines changed: 5528 additions & 1295 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 246 additions & 141 deletions
Large diffs are not rendered by default.

backend/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ module github.com/Paca-AI/first-party/dashboard
22

33
go 1.24
44

5-
require github.com/Paca-AI/plugin-sdk-go v0.2.1
5+
require github.com/Paca-AI/plugin-sdk-go v0.3.0

backend/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
github.com/Paca-AI/plugin-sdk-go v0.2.1 h1:jz8plKkll/3Zlfgp5lotO1APmkk9czCUHkLRANxNIDM=
2-
github.com/Paca-AI/plugin-sdk-go v0.2.1/go.mod h1:5WeC6cSEf2wM1ovICZbDaVky9oi5id/Qpdfc5LDAQnw=
1+
github.com/Paca-AI/plugin-sdk-go v0.3.0 h1:qcakLwUEgjc7Ug4bixv5h47oS+uSfVhFzyhF1VyA3N8=
2+
github.com/Paca-AI/plugin-sdk-go v0.3.0/go.mod h1:5WeC6cSEf2wM1ovICZbDaVky9oi5id/Qpdfc5LDAQnw=
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
-- 0001_create_dashboard_tables.sql
2+
-- Creates the customizable-dashboard tables in the plugin schema.
3+
-- Run with search_path = plugin_data_com_paca_dashboard, public.
4+
--
5+
-- Three dashboard scopes share these two tables:
6+
-- 'project' — exactly one row per project (the project dashboard page).
7+
-- 'admin' — exactly one row total, project_id NULL (the admin dashboard page).
8+
-- 'integration' — any number of rows per project, user-created from the
9+
-- "Add view" popover on Integration pages (backlog/sprint/timeline).
10+
--
11+
-- Partial unique indexes enforce the "exactly one" constraint for the
12+
-- project/admin scopes at the database level rather than relying on
13+
-- application logic alone.
14+
15+
CREATE TABLE IF NOT EXISTS dashboard_views (
16+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
17+
project_id UUID REFERENCES projects(id) ON DELETE CASCADE,
18+
scope TEXT NOT NULL CHECK (scope IN ('project', 'admin', 'integration')),
19+
name TEXT NOT NULL DEFAULT 'Dashboard',
20+
created_by UUID REFERENCES project_members(id) ON DELETE SET NULL,
21+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
22+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
23+
24+
CONSTRAINT dashboard_views_project_scope_requires_project_id
25+
CHECK (
26+
(scope = 'admin' AND project_id IS NULL) OR
27+
(scope IN ('project', 'integration') AND project_id IS NOT NULL)
28+
)
29+
);
30+
31+
-- At most one 'project'-scope dashboard per project.
32+
CREATE UNIQUE INDEX IF NOT EXISTS uq_dashboard_views_one_project_scope
33+
ON dashboard_views (project_id)
34+
WHERE scope = 'project';
35+
36+
-- At most one 'admin'-scope dashboard, instance-wide.
37+
CREATE UNIQUE INDEX IF NOT EXISTS uq_dashboard_views_one_admin_scope
38+
ON dashboard_views ((1))
39+
WHERE scope = 'admin';
40+
41+
CREATE INDEX IF NOT EXISTS idx_dashboard_views_project_scope
42+
ON dashboard_views (project_id, scope);
43+
44+
CREATE TABLE IF NOT EXISTS dashboard_panels (
45+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
46+
dashboard_view_id UUID NOT NULL REFERENCES dashboard_views(id) ON DELETE CASCADE,
47+
type TEXT NOT NULL CHECK (type IN ('chart', 'table', 'text')),
48+
title TEXT NOT NULL DEFAULT '',
49+
-- SQL query text for 'chart'/'table' panels; NULL for 'text' panels.
50+
-- See query_guard.go for the safety model enforced before execution:
51+
-- must be a single read-only SELECT/WITH statement, must reference the
52+
-- {{project_id}} placeholder (for project/integration-scoped panels),
53+
-- and gets a LIMIT appended if it doesn't declare one. Sensitive
54+
-- columns it happens to read (core platform secrets, or another
55+
-- plugin's own declared-sensitive fields) come back masked from the
56+
-- host itself, not from any table restriction here.
57+
query TEXT,
58+
-- Chart rendering hint ('bar' | 'line' | 'donut'); NULL for table/text.
59+
chart_type TEXT,
60+
-- Static markdown body for 'text' panels; NULL for chart/table.
61+
content TEXT,
62+
-- Free-form visualization config (axis/series field mapping, colors,
63+
-- column order for table panels, etc.) — validated shape lives in the
64+
-- frontend; the backend treats it as an opaque JSON blob.
65+
viz_config JSONB NOT NULL DEFAULT '{}',
66+
pos_x INTEGER NOT NULL DEFAULT 0,
67+
pos_y INTEGER NOT NULL DEFAULT 0,
68+
width INTEGER NOT NULL DEFAULT 4,
69+
height INTEGER NOT NULL DEFAULT 3,
70+
created_by UUID REFERENCES project_members(id) ON DELETE SET NULL,
71+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
72+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
73+
);
74+
75+
CREATE INDEX IF NOT EXISTS idx_dashboard_panels_view_id
76+
ON dashboard_panels (dashboard_view_id);
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
-- 0002_dashboard_views_singleton_per_host_view.sql
2+
-- Integration-scope dashboards ("Dashboard" views inside Integration pages)
3+
-- used to be an unlimited, user-managed list per project, with their own
4+
-- tab strip (list/create/rename/delete) inside DashboardIntegrationView.tsx.
5+
--
6+
-- That's being replaced with the same "exactly one dashboard" model already
7+
-- used by the project/admin scopes: each host-side interaction view (a
8+
-- Timeline/Backlog/Sprint page's "Dashboard" view, created via the host's
9+
-- own "Add view" popover) now gets exactly one dashboard, identified by the
10+
-- host's own view row id (host_view_id) rather than a plugin-managed list.
11+
--
12+
-- host_view_id is only ever set for scope='integration' — project/admin
13+
-- scope dashboards have no corresponding host view row (they're reached via
14+
-- a nav item, not a view-picker entry) and stay NULL here, same as
15+
-- project_id already does for the admin scope.
16+
17+
ALTER TABLE dashboard_views
18+
ADD COLUMN IF NOT EXISTS host_view_id UUID;
19+
20+
-- At most one integration-scope dashboard per host view (get-or-create
21+
-- singleton, mirrors uq_dashboard_views_one_project_scope /
22+
-- uq_dashboard_views_one_admin_scope for the other two scopes).
23+
CREATE UNIQUE INDEX IF NOT EXISTS uq_dashboard_views_one_per_host_view
24+
ON dashboard_views (host_view_id)
25+
WHERE scope = 'integration';

0 commit comments

Comments
 (0)