-- t-paliad-144 Phase A1: Custom Views — paliad.user_views.
--
-- Design: docs/design-data-display-model-2026-05-06.md (noether,
-- m-locked 2026-05-07).
--
-- Stores per-user saved view definitions. A view is a `(filter_spec,
-- render_spec, sidebar metadata)` tuple. RLS scopes every operation
-- to the calling user — there is no cross-user visibility in v1.
--
-- System defaults (dashboard / agenda / events / inbox) stay code-
-- resident in internal/services/system_views.go. They never appear
-- as rows in this table; the slugs are reserved and rejected at write
-- time by the application layer.
--
-- Sections:
--   1. CREATE paliad.user_views (with RLS).
--   2. Indexes.

-- ============================================================================
-- 1. paliad.user_views
-- ============================================================================

CREATE TABLE paliad.user_views (
    id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id       uuid NOT NULL REFERENCES paliad.users(id) ON DELETE CASCADE,

    -- Stable user-facing identifier. Goes into the URL.
    -- Application-layer validator enforces ^[a-z0-9][a-z0-9-]{0,62}$ +
    -- a reserved-list rejection (dashboard, agenda, events, inbox, …).
    slug          text NOT NULL,

    -- Display name. Free-form; user picks the language they think in.
    -- Rendered verbatim in the sidebar; no fallback or translation.
    name          text NOT NULL,

    -- One of a fixed set of icon keys (see Sidebar.tsx icon registry).
    -- NULL → default icon (folder). Validator caps length to keep the
    -- column sane even if the registry is bypassed.
    icon          text,

    -- Filter spec — see internal/services/filter_spec.go FilterSpec.
    -- Validated on write; jsonb here for forward-compat without
    -- migrations as new dimensions land.
    filter_spec   jsonb NOT NULL,

    -- Render spec — see internal/services/render_spec.go RenderSpec.
    render_spec   jsonb NOT NULL,

    -- Sidebar ordering. Lower-first. New views land at MAX+1 server-side
    -- so they sort to the bottom; the editor lets users drag-reorder.
    sort_order    integer NOT NULL DEFAULT 0,

    -- Show a row-count badge on the sidebar entry. Costs one COUNT(*)
    -- per refresh; opt-in (default false) so casual users don't pay.
    show_count    boolean NOT NULL DEFAULT false,

    -- Most-recently-used landing on /views (Q10). Updated by a fire-
    -- and-forget PATCH on every view-load.
    last_used_at  timestamptz,

    created_at    timestamptz NOT NULL DEFAULT now(),
    updated_at    timestamptz NOT NULL DEFAULT now(),

    UNIQUE (user_id, slug)
);

CREATE INDEX user_views_owner_idx
    ON paliad.user_views (user_id, sort_order);

ALTER TABLE paliad.user_views ENABLE ROW LEVEL SECURITY;

-- Owner-only access. No global_admin override: views are personal
-- working state, not auditable infrastructure.
CREATE POLICY user_views_owner_all
    ON paliad.user_views FOR ALL
    USING (user_id = auth.uid())
    WITH CHECK (user_id = auth.uid());
