- Add client/src/themes/ with tokens, built-in skins (iistwin, twenty, omegaprom) - Add ThemeProvider with data-skin/data-theme and runtime CSS for custom skins - Add npm run skins:generate script for build-time CSS generation - Add organization_skins table, migration and CRUD API - Add activeSkinId to organizations and expose it in /api/auth/me - Add skin switcher in /profile and admin skin editor in /settings/appearance - Adapt index.css hardcoded colors to skin tokens - Update AGENTS.md and Obsidian notes
28 lines
1.1 KiB
SQL
28 lines
1.1 KiB
SQL
-- Organization skin storage and active skin selector
|
|
CREATE TABLE IF NOT EXISTS organization_skins (
|
|
id SERIAL PRIMARY KEY,
|
|
organization_id INTEGER NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
|
|
skin_id VARCHAR(50) NOT NULL,
|
|
name VARCHAR(100) NOT NULL,
|
|
description TEXT,
|
|
tokens JSONB NOT NULL,
|
|
is_editable BOOLEAN DEFAULT true,
|
|
is_active BOOLEAN DEFAULT false,
|
|
created_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
UNIQUE(organization_id, skin_id)
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS organization_skins_org_idx ON organization_skins(organization_id);
|
|
|
|
ALTER TABLE organizations ADD COLUMN IF NOT EXISTS active_skin_id VARCHAR(50) DEFAULT 'iistwin';
|
|
|
|
-- Tenant isolation
|
|
ALTER TABLE organization_skins ENABLE ROW LEVEL SECURITY;
|
|
DROP POLICY IF EXISTS tenant_iso ON organization_skins;
|
|
CREATE POLICY tenant_iso ON organization_skins USING (
|
|
current_setting('app.is_superadmin', true) = 'true'
|
|
OR organization_id = NULLIF(current_setting('app.current_org_id', true), '')::int
|
|
);
|