-- Sādhana streak rewards. -- -- `sadhna_rewards` is the catalog of gift items (one row per reward), -- seeded/synced at BFF startup from the embedded rewards.json so the -- name / meaning / image_url stay in lockstep with the source of truth. -- -- `sadhna_reward_grants` records which reward a user earned at which -- streak milestone (a multiple of 7 days). The UNIQUE (user_id, milestone) -- makes granting idempotent — a milestone hands out exactly one gift no -- matter how often the dashboard is recomputed. `celebrated_at` is NULL -- until the client has shown the confetti celebration, so a freshly earned -- gift surfaces exactly once. CREATE TABLE IF NOT EXISTS sadhna_rewards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), key TEXT NOT NULL UNIQUE, name TEXT NOT NULL, meaning TEXT NOT NULL DEFAULT '', image_url TEXT NOT NULL DEFAULT '', active BOOLEAN NOT NULL DEFAULT TRUE, sort INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE TABLE IF NOT EXISTS sadhna_reward_grants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, reward_id UUID NOT NULL REFERENCES sadhna_rewards(id) ON DELETE CASCADE, milestone INTEGER NOT NULL, granted_at TIMESTAMPTZ NOT NULL DEFAULT now(), celebrated_at TIMESTAMPTZ, UNIQUE (user_id, milestone) ); CREATE INDEX IF NOT EXISTS sadhna_reward_grants_user_idx ON sadhna_reward_grants (user_id, granted_at DESC);