-- Phase 2: topic-based wantlists + items -- A "list" is a topic (clothes, gear, …). An "item" is a thing the user covets, -- usually backed by a pasted product URL. Price/metadata columns are filled by the -- Phase 3 refetch worker (generic Shopify .json adapter, etc.) and stay NULL until then. CREATE TABLE lists ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, name TEXT NOT NULL, emoji TEXT, -- optional decorative glyph description TEXT, position INTEGER NOT NULL DEFAULT 0, -- manual ordering created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_lists_user ON lists(user_id); CREATE TYPE item_status AS ENUM ('coveted', 'acquired', 'renounced'); CREATE TABLE items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), list_id UUID NOT NULL REFERENCES lists(id) ON DELETE CASCADE, title TEXT NOT NULL, url TEXT, -- pasted product URL (Phase 3 tracks this) note TEXT, status item_status NOT NULL DEFAULT 'coveted', target_price NUMERIC(12, 2), -- alert threshold the user sets position INTEGER NOT NULL DEFAULT 0, -- Filled by the Phase 3 fetcher; NULL until first successful fetch. title_fetched TEXT, current_price NUMERIC(12, 2), currency TEXT, -- ISO 4217, e.g. 'EUR' image_url TEXT, in_stock BOOLEAN, source TEXT, -- adapter that produced the data, e.g. 'shopify' fetched_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_items_list ON items(list_id); CREATE INDEX idx_items_url ON items(url) WHERE url IS NOT NULL; CREATE TRIGGER trg_lists_updated BEFORE UPDATE ON lists FOR EACH ROW EXECUTE FUNCTION set_updated_at(); CREATE TRIGGER trg_items_updated BEFORE UPDATE ON items FOR EACH ROW EXECUTE FUNCTION set_updated_at();