db to user_id
All checks were successful
Deploy to Web Server flyer-crawler.projectium.com / deploy (push) Successful in 1m3s

This commit is contained in:
2025-11-24 14:14:54 -08:00
parent ba0abded2b
commit ff2a82f06d
4 changed files with 24 additions and 27 deletions

View File

@@ -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; 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(); 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; 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(); CREATE TRIGGER on_master_grocery_items_updated BEFORE UPDATE ON public.master_grocery_items FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();

View File

@@ -79,6 +79,7 @@ CREATE TABLE IF NOT EXISTS public.flyers (
valid_from DATE, valid_from DATE,
valid_to DATE, valid_to DATE,
store_address TEXT, store_address TEXT,
uploaded_by UUID REFERENCES public.users(user_id) ON DELETE SET NULL,
created_at TIMESTAMPTZ DEFAULT now() NOT NULL, created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
updated_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_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.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.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. -- 7. The 'master_grocery_items' table. This is the master dictionary.
CREATE TABLE IF NOT EXISTS public.master_grocery_items ( 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, click_count INTEGER DEFAULT 0 NOT NULL,
master_item_id BIGINT REFERENCES public.master_grocery_items(master_grocery_item_id), master_item_id BIGINT REFERENCES public.master_grocery_items(master_grocery_item_id),
product_id BIGINT, 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 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.'; 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 ( CREATE TABLE IF NOT EXISTS public.unmatched_flyer_items (
unmatched_flyer_item_id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, 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, 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, created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
reviewed_at TIMESTAMPTZ, reviewed_at TIMESTAMPTZ,
UNIQUE(flyer_item_id), UNIQUE(flyer_item_id),
@@ -428,8 +431,8 @@ CREATE TABLE IF NOT EXISTS public.recipes (
protein_grams NUMERIC, protein_grams NUMERIC,
fat_grams NUMERIC, fat_grams NUMERIC,
carb_grams NUMERIC, carb_grams NUMERIC,
avg_rating NUMERIC(2,1) DEFAULT 0.0, avg_rating NUMERIC(2,1) DEFAULT 0.0 NOT NULL,
status TEXT DEFAULT 'private' NOT NULL CHECK (status IN ('private', 'pending_review', 'public')), status TEXT DEFAULT 'private' NOT NULL CHECK (status IN ('private', 'pending_review', 'public', 'rejected')),
rating_count INTEGER DEFAULT 0 NOT NULL, rating_count INTEGER DEFAULT 0 NOT NULL,
created_at TIMESTAMPTZ DEFAULT now() NOT NULL, created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL updated_at TIMESTAMPTZ DEFAULT now() NOT NULL

View File

@@ -15,7 +15,7 @@ export interface Flyer {
valid_from?: string | null; valid_from?: string | null;
valid_to?: string | null; valid_to?: string | null;
store_address?: 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; store?: Store;
} }
@@ -25,9 +25,9 @@ export interface UnitPrice {
} }
export interface FlyerItem { export interface FlyerItem {
flyer_item_id?: number; flyer_item_id: number;
flyer_id?: number; flyer_id: number;
created_at?: string; created_at: string;
item: string; item: string;
price_display: string; price_display: string;
price_in_cents: number | null; price_in_cents: number | null;
@@ -41,6 +41,7 @@ export interface FlyerItem {
product_id?: number | null; product_id?: number | null;
view_count: number; view_count: number;
click_count: number; click_count: number;
updated_at: string;
} }
export interface MasterGroceryItem { export interface MasterGroceryItem {
@@ -387,18 +388,6 @@ export interface UserFollow {
created_at: string; 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. * 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, * It allows for arbitrary string keys but restricts values to primitives,
@@ -414,11 +403,9 @@ export interface ActivityLogItem {
activity_log_id: number; activity_log_id: number;
user_id?: string | null; // UUID user_id?: string | null; // UUID
action: string; // A key for the event type, e.g., 'user_registered' 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 icon?: string | null; // An optional icon name for the UI
details: ActivityLogDetails | null; // Structured data for analytics, i18n, etc. details?: ActivityLogDetails | null; // Structured data for analytics, i18n, etc.
activity_type: string; // Add missing property
entity_id?: string | null; // Add missing property
created_at: string; created_at: string;
} }
@@ -652,7 +639,7 @@ export interface MenuPlanShoppingListItem {
*/ */
export interface UnmatchedFlyerItem { export interface UnmatchedFlyerItem {
unmatched_flyer_item_id: number; 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 created_at: string; // Date string
flyer_item_id: number; flyer_item_id: number;
flyer_item_name: string; flyer_item_name: string;

View File

@@ -26,6 +26,10 @@
] ]
}, },
"allowImportingTsExtensions": true, "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"]
} }