From 43cb62a4d68e251bec7f9d144f92866787c57a66 Mon Sep 17 00:00:00 2001 From: CartSnitch Engineer Bot Date: Wed, 1 Apr 2026 19:36:21 +0000 Subject: [PATCH 1/3] fix(api): remove TimestampMixin from models whose DB tables lack timestamp columns Remove TimestampMixin (created_at/updated_at) from Purchase, PurchaseItem, PriceHistory, Coupon, and ShrinkflationEvent models since their PostgreSQL tables do not have those columns. This was causing 500 errors on /api/v1/purchases and /api/v1/purchases/stats. Co-Authored-By: Paperclip --- api/src/cartsnitch_api/models/coupon.py | 4 ++-- api/src/cartsnitch_api/models/price.py | 4 ++-- api/src/cartsnitch_api/models/purchase.py | 6 +++--- api/src/cartsnitch_api/models/shrinkflation.py | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api/src/cartsnitch_api/models/coupon.py b/api/src/cartsnitch_api/models/coupon.py index df2630a..eb230ea 100644 --- a/api/src/cartsnitch_api/models/coupon.py +++ b/api/src/cartsnitch_api/models/coupon.py @@ -9,14 +9,14 @@ from sqlalchemy import Boolean, Date, DateTime, ForeignKey, Numeric, String from sqlalchemy.orm import Mapped, mapped_column, relationship from cartsnitch_api.constants import DiscountType -from cartsnitch_api.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin +from cartsnitch_api.models.base import Base, UUIDPrimaryKeyMixin if TYPE_CHECKING: from cartsnitch_api.models.product import NormalizedProduct from cartsnitch_api.models.store import Store -class Coupon(UUIDPrimaryKeyMixin, TimestampMixin, Base): +class Coupon(UUIDPrimaryKeyMixin, Base): """A coupon or deal for a product at a store.""" __tablename__ = "coupons" diff --git a/api/src/cartsnitch_api/models/price.py b/api/src/cartsnitch_api/models/price.py index 7da0fa6..47373dd 100644 --- a/api/src/cartsnitch_api/models/price.py +++ b/api/src/cartsnitch_api/models/price.py @@ -9,7 +9,7 @@ from sqlalchemy import Date, ForeignKey, Index, Numeric, String from sqlalchemy.orm import Mapped, mapped_column, relationship from cartsnitch_api.constants import PriceSource -from cartsnitch_api.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin +from cartsnitch_api.models.base import Base, UUIDPrimaryKeyMixin if TYPE_CHECKING: from cartsnitch_api.models.product import NormalizedProduct @@ -17,7 +17,7 @@ if TYPE_CHECKING: from cartsnitch_api.models.store import Store -class PriceHistory(UUIDPrimaryKeyMixin, TimestampMixin, Base): +class PriceHistory(UUIDPrimaryKeyMixin, Base): """A single price observation for a product at a store on a date.""" __tablename__ = "price_history" diff --git a/api/src/cartsnitch_api/models/purchase.py b/api/src/cartsnitch_api/models/purchase.py index 5a56cba..26aa09b 100644 --- a/api/src/cartsnitch_api/models/purchase.py +++ b/api/src/cartsnitch_api/models/purchase.py @@ -18,7 +18,7 @@ from sqlalchemy import ( ) from sqlalchemy.orm import Mapped, mapped_column, relationship -from cartsnitch_api.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin +from cartsnitch_api.models.base import Base, UUIDPrimaryKeyMixin if TYPE_CHECKING: from cartsnitch_api.models.price import PriceHistory @@ -27,7 +27,7 @@ if TYPE_CHECKING: from cartsnitch_api.models.user import User -class Purchase(UUIDPrimaryKeyMixin, TimestampMixin, Base): +class Purchase(UUIDPrimaryKeyMixin, Base): """A single shopping trip / receipt.""" __tablename__ = "purchases" @@ -61,7 +61,7 @@ class Purchase(UUIDPrimaryKeyMixin, TimestampMixin, Base): ) -class PurchaseItem(UUIDPrimaryKeyMixin, TimestampMixin, Base): +class PurchaseItem(UUIDPrimaryKeyMixin, Base): """Individual line item on a receipt.""" __tablename__ = "purchase_items" diff --git a/api/src/cartsnitch_api/models/shrinkflation.py b/api/src/cartsnitch_api/models/shrinkflation.py index 2ce6f9d..35f5d40 100644 --- a/api/src/cartsnitch_api/models/shrinkflation.py +++ b/api/src/cartsnitch_api/models/shrinkflation.py @@ -9,13 +9,13 @@ from sqlalchemy import Date, ForeignKey, Numeric, String from sqlalchemy.orm import Mapped, mapped_column, relationship from cartsnitch_api.constants import SizeUnit -from cartsnitch_api.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin +from cartsnitch_api.models.base import Base, UUIDPrimaryKeyMixin if TYPE_CHECKING: from cartsnitch_api.models.product import NormalizedProduct -class ShrinkflationEvent(UUIDPrimaryKeyMixin, TimestampMixin, Base): +class ShrinkflationEvent(UUIDPrimaryKeyMixin, Base): """Detected shrinkflation event — product size changed while price held or rose.""" __tablename__ = "shrinkflation_events" From e85d757cc6823e79e09490fb06f886a525741426 Mon Sep 17 00:00:00 2001 From: CartSnitch Engineer Bot Date: Wed, 1 Apr 2026 19:37:44 +0000 Subject: [PATCH 2/3] fix(frontend): remove hardcoded mock product IDs from Dashboard price trends Removed usePriceHistory calls with hardcoded string product IDs (prod1, prod10) that caused 422 errors against the UUID-expecting API. The Price Trends section now shows a placeholder until a proper featured-products endpoint is available. Co-Authored-By: Paperclip --- src/pages/Dashboard.tsx | 35 ++++------------------------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index d7c2b9a..9e65c95 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,13 +1,9 @@ -import React, { Suspense } from 'react' +import React from 'react' import { Link } from 'react-router-dom' import { authClient } from '../lib/auth-client.ts' -import { usePurchases, usePriceAlerts, usePriceHistory } from '../hooks/useApi.ts' +import { usePurchases, usePriceAlerts } from '../hooks/useApi.ts' import { StoreIcon } from '../components/StoreIcon.tsx' -const LazySparklineCard = React.lazy(() => - import('../components/SparklineChart.tsx').then((mod) => ({ default: mod.SparklineCard })) -) - export function Dashboard() { const { data: session, isPending } = authClient.useSession() @@ -44,19 +40,11 @@ export function Dashboard() { function AuthenticatedDashboard({ userName }: { userName: string }) { const { data: purchases = [], isLoading: purchasesLoading } = usePurchases() const { data: alerts = [], isLoading: alertsLoading } = usePriceAlerts() - const { data: eggHistory = [] } = usePriceHistory('prod10') - const { data: milkHistory = [] } = usePriceHistory('prod1') const triggeredAlerts = alerts.filter((a) => a.triggered) const watchingAlerts = alerts.filter((a) => !a.triggered) const recentPurchases = purchases.slice(0, 3) - const sparklineData = eggHistory.filter((p) => p.storeId === 'meijer').slice(-8) - const milkSparkline = milkHistory.filter((p) => p.storeId === 'kroger').slice(-8) - - const eggCurrent = sparklineData.length > 0 ? `$${sparklineData[sparklineData.length - 1].price.toFixed(2)}` : '—' - const milkCurrent = milkSparkline.length > 0 ? `$${milkSparkline[milkSparkline.length - 1].price.toFixed(2)}` : '—' - if (purchasesLoading || alertsLoading) { return } @@ -106,11 +94,8 @@ function AuthenticatedDashboard({ userName }: { userName: string }) { {/* Price trend sparklines */}

Price Trends

-
- }> - - - +
+ Connect a store to see price trends
@@ -187,15 +172,3 @@ function DashboardSkeleton() { ) } - -function SparklinePlaceholder() { - return ( -
-
-
-
-
-
-
- ) -} From 63621df0b8af995261474123663a04b5d2dc6e34 Mon Sep 17 00:00:00 2001 From: CartSnitch Engineer Bot Date: Wed, 1 Apr 2026 19:58:41 +0000 Subject: [PATCH 3/3] fix(frontend): remove unused React import from Dashboard.tsx Co-Authored-By: Paperclip --- src/pages/Dashboard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 9e65c95..c3f428a 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,4 +1,3 @@ -import React from 'react' import { Link } from 'react-router-dom' import { authClient } from '../lib/auth-client.ts' import { usePurchases, usePriceAlerts } from '../hooks/useApi.ts'