diff --git a/sql/Initial_triggers_and_functions.sql b/sql/Initial_triggers_and_functions.sql index cbb9f5e..614268c 100644 --- a/sql/Initial_triggers_and_functions.sql +++ b/sql/Initial_triggers_and_functions.sql @@ -67,6 +67,9 @@ CREATE TRIGGER on_categories_updated BEFORE UPDATE ON public.categories FOR EACH DROP TRIGGER IF EXISTS on_flyers_updated ON public.flyers; CREATE TRIGGER on_flyers_updated BEFORE UPDATE ON public.flyers FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); +DROP TRIGGER IF EXISTS on_flyer_items_updated ON public.flyer_items; +CREATE TRIGGER on_flyer_items_updated BEFORE UPDATE ON public.flyer_items FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); + DROP TRIGGER IF EXISTS on_master_grocery_items_updated ON public.master_grocery_items; CREATE TRIGGER on_master_grocery_items_updated BEFORE UPDATE ON public.master_grocery_items FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at(); diff --git a/sql/initial_schema.sql b/sql/initial_schema.sql index ae792de..788626e 100644 --- a/sql/initial_schema.sql +++ b/sql/initial_schema.sql @@ -79,6 +79,7 @@ CREATE TABLE IF NOT EXISTS public.flyers ( valid_from DATE, valid_to DATE, store_address TEXT, + uploaded_by UUID REFERENCES public.users(user_id) ON DELETE SET NULL, created_at TIMESTAMPTZ DEFAULT now() NOT NULL, updated_at TIMESTAMPTZ DEFAULT now() NOT NULL ); @@ -91,6 +92,7 @@ COMMENT ON COLUMN public.flyers.store_id IS 'Foreign key linking this flyer to a COMMENT ON COLUMN public.flyers.valid_from IS 'The start date of the sale period for this flyer, extracted by the AI.'; COMMENT ON COLUMN public.flyers.valid_to IS 'The end date of the sale period for this flyer, extracted by the AI.'; COMMENT ON COLUMN public.flyers.store_address IS 'The physical store address if it was successfully extracted from the flyer image.'; +COMMENT ON COLUMN public.flyers.uploaded_by IS 'The user who uploaded the flyer. Can be null for anonymous or system uploads.'; -- 7. The 'master_grocery_items' table. This is the master dictionary. CREATE TABLE IF NOT EXISTS public.master_grocery_items ( @@ -134,7 +136,8 @@ CREATE TABLE IF NOT EXISTS public.flyer_items ( click_count INTEGER DEFAULT 0 NOT NULL, master_item_id BIGINT REFERENCES public.master_grocery_items(master_grocery_item_id), product_id BIGINT, - created_at TIMESTAMPTZ DEFAULT now() NOT NULL + created_at TIMESTAMPTZ DEFAULT now() NOT NULL, + updated_at TIMESTAMPTZ DEFAULT now() NOT NULL ); COMMENT ON TABLE public.flyer_items IS 'Stores individual items extracted from a specific flyer.'; COMMENT ON COLUMN public.flyer_items.flyer_id IS 'Foreign key linking this item to its parent flyer in the `flyers` table.'; @@ -359,7 +362,7 @@ CREATE INDEX IF NOT EXISTS idx_user_submitted_prices_master_item_id ON public.us CREATE TABLE IF NOT EXISTS public.unmatched_flyer_items ( unmatched_flyer_item_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, flyer_item_id BIGINT NOT NULL REFERENCES public.flyer_items(flyer_item_id) ON DELETE CASCADE, - status TEXT DEFAULT 'pending' NOT NULL CHECK (status IN ('pending', 'reviewed', 'ignored')), + status TEXT DEFAULT 'pending' NOT NULL CHECK (status IN ('pending', 'resolved', 'ignored')), created_at TIMESTAMPTZ DEFAULT now() NOT NULL, reviewed_at TIMESTAMPTZ, UNIQUE(flyer_item_id), @@ -428,8 +431,8 @@ CREATE TABLE IF NOT EXISTS public.recipes ( protein_grams NUMERIC, fat_grams NUMERIC, carb_grams NUMERIC, - avg_rating NUMERIC(2,1) DEFAULT 0.0, - status TEXT DEFAULT 'private' NOT NULL CHECK (status IN ('private', 'pending_review', 'public')), + avg_rating NUMERIC(2,1) DEFAULT 0.0 NOT NULL, + status TEXT DEFAULT 'private' NOT NULL CHECK (status IN ('private', 'pending_review', 'public', 'rejected')), rating_count INTEGER DEFAULT 0 NOT NULL, created_at TIMESTAMPTZ DEFAULT now() NOT NULL, updated_at TIMESTAMPTZ DEFAULT now() NOT NULL diff --git a/src/types.ts b/src/types.ts index f4f0dca..f89bac4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -15,7 +15,7 @@ export interface Flyer { valid_from?: string | null; valid_to?: string | null; store_address?: string | null; - uploaded_by?: string | null; // UUID of the user who uploaded it + uploaded_by?: string | null; // UUID of the user who uploaded it, can be null for anonymous uploads store?: Store; } @@ -25,9 +25,9 @@ export interface UnitPrice { } export interface FlyerItem { - flyer_item_id?: number; - flyer_id?: number; - created_at?: string; + flyer_item_id: number; + flyer_id: number; + created_at: string; item: string; price_display: string; price_in_cents: number | null; @@ -41,6 +41,7 @@ export interface FlyerItem { product_id?: number | null; view_count: number; click_count: number; + updated_at: string; } export interface MasterGroceryItem { @@ -387,18 +388,6 @@ export interface UserFollow { created_at: string; } - -export interface UnmatchedFlyerItem { - unmatched_flyer_item_id: number; - status: 'pending' | 'resolved' | 'ignored'; - created_at: string; - flyer_item_id: number; - flyer_item_name: string; - price_display: string; - flyer_id: number; - store_name: string; -} - /** * Defines a flexible but type-safe structure for the `details` object in an activity log. * It allows for arbitrary string keys but restricts values to primitives, @@ -414,11 +403,9 @@ export interface ActivityLogItem { activity_log_id: number; user_id?: string | null; // UUID action: string; // A key for the event type, e.g., 'user_registered' - display_text: string; // A pre-formatted message for direct display in the UI + display_text: string; // A pre-formatted, user-facing message for direct display in the UI icon?: string | null; // An optional icon name for the UI - details: ActivityLogDetails | null; // Structured data for analytics, i18n, etc. - activity_type: string; // Add missing property - entity_id?: string | null; // Add missing property + details?: ActivityLogDetails | null; // Structured data for analytics, i18n, etc. created_at: string; } @@ -652,7 +639,7 @@ export interface MenuPlanShoppingListItem { */ export interface UnmatchedFlyerItem { unmatched_flyer_item_id: number; - status: 'pending' | 'resolved' | 'ignored'; + status: 'pending' | 'resolved' | 'ignored'; // 'resolved' is used instead of 'reviewed' from the DB for clarity created_at: string; // Date string flyer_item_id: number; flyer_item_name: string; diff --git a/tsconfig.json b/tsconfig.json index de153f4..8b7aa27 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -26,6 +26,10 @@ ] }, "allowImportingTsExtensions": true, - "noEmit": true - } + "noEmit": true, + "strict": true, // Enforcing strict mode is a best practice for new projects. + "forceConsistentCasingInFileNames": true // Helps prevent casing-related import errors. + }, + "include": ["src", ".vitepress", "vite.config.ts", "vitest.config.ts"], + "exclude": ["node_modules", "dist", "coverage"] } \ No newline at end of file