From fba86cd69c86cf1f49b99e84b793693344447e42 Mon Sep 17 00:00:00 2001 From: Samal Bishnupriya Date: Wed, 3 Jun 2026 12:27:24 +0530 Subject: [PATCH 1/5] feat: add public shareable link for Grade A freshness reports --- backend/main.py | 4 ++++ backend/public.py | 12 ++++++++++ src/App.tsx | 4 ++++ src/index.css | 4 ++++ src/pages/PublicReport.tsx | 48 ++++++++++++++++++++++++++++++++++++++ src/pages/ScannerPage.tsx | 12 ++++++++++ vercel.json | 1 + 7 files changed, 85 insertions(+) create mode 100644 backend/public.py create mode 100644 src/pages/PublicReport.tsx diff --git a/backend/main.py b/backend/main.py index d6a64c9..5b46406 100644 --- a/backend/main.py +++ b/backend/main.py @@ -6,6 +6,10 @@ from datetime import datetime, timezone from contextlib import asynccontextmanager from typing import Optional +from api.public import router as public_router + +app.include_router(public_router, prefix="/public", tags=["public"]) +# Result: GET /public/report/{scan_id} serves the public report endpoint without auth, used by QR code scans and public links. # Load .env file if present (python-dotenv) try: diff --git a/backend/public.py b/backend/public.py new file mode 100644 index 0000000..faff589 --- /dev/null +++ b/backend/public.py @@ -0,0 +1,12 @@ +# backend/api/public.py +from fastapi import APIRouter, HTTPException +from ..db import supabase # adjust import path to match your project + +router = APIRouter() + +@router.get("/report/{scan_id}") +async def get_public_report(scan_id: str): + result = supabase.table("scans").select("*").eq("id", scan_id).single().execute() + if not result.data: + raise HTTPException(status_code=404, detail="Scan not found") + return result.data \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 14f81c6..0b4417b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,6 +32,10 @@ export default function App() { {/* Catch-all route for broken links/404s */} } /> + import PublicReport from './pages/PublicReport'; + + // Inside : + } /> diff --git a/src/index.css b/src/index.css index de17c37..a081ad2 100644 --- a/src/index.css +++ b/src/index.css @@ -405,4 +405,8 @@ input::placeholder { --color-outline: #6b6b6b; --color-outline-variant: #c9c9c9; --color-tertiary: #121212; +} + +@media print { + nav, .print\:hidden { display: none !important; } } \ No newline at end of file diff --git a/src/pages/PublicReport.tsx b/src/pages/PublicReport.tsx new file mode 100644 index 0000000..edbefe4 --- /dev/null +++ b/src/pages/PublicReport.tsx @@ -0,0 +1,48 @@ +import { useEffect, useState } from 'react'; +import { useParams } from 'react-router-dom'; + +export default function PublicReport() { + const { id } = useParams(); + const [scan, setScan] = useState(null); + const [copied, setCopied] = useState(false); + + useEffect(() => { + fetch(`/api/public/report/${id}`) + .then(r => r.json()) + .then(setScan); + }, [id]); + + const handleShare = () => { + navigator.clipboard.writeText(window.location.href); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + const handlePrint = () => window.print(); + + if (!scan) return
Loading...
; + + return ( +
+

FreshScan AI — Freshness Report

+

{new Date(scan.created_at).toLocaleString()}

+ +
+

{scan.freshness_score}

+

{scan.grade} — {scan.label}

+
+ + {/* Marker breakdown, image, etc. from scan data */} +
{JSON.stringify(scan.markers, null, 2)}
+ +
+ + +
+
+ ); +} \ No newline at end of file diff --git a/src/pages/ScannerPage.tsx b/src/pages/ScannerPage.tsx index f357e5e..a65ee2b 100644 --- a/src/pages/ScannerPage.tsx +++ b/src/pages/ScannerPage.tsx @@ -351,6 +351,18 @@ export default function ScannerPage() { )} + // After scan completes and grade === 'A' + {result.grade === 'A' && ( + + )} Date: Thu, 4 Jun 2026 20:02:21 +0530 Subject: [PATCH 2/5] fix: resolve lint errors in frontend and backend --- FreshScanAi | 1 + backend/main.py | 6 +++++- backend/public.py | 2 +- src/App.tsx | 3 --- src/pages/PublicReport.tsx | 7 ++++++- 5 files changed, 13 insertions(+), 6 deletions(-) create mode 160000 FreshScanAi diff --git a/FreshScanAi b/FreshScanAi new file mode 160000 index 0000000..af3b16a --- /dev/null +++ b/FreshScanAi @@ -0,0 +1 @@ +Subproject commit af3b16aad015e23e0ade62918f973ecea755ed79 diff --git a/backend/main.py b/backend/main.py index 5b46406..e898da0 100644 --- a/backend/main.py +++ b/backend/main.py @@ -9,7 +9,8 @@ from api.public import router as public_router app.include_router(public_router, prefix="/public", tags=["public"]) -# Result: GET /public/report/{scan_id} serves the public report endpoint without auth, used by QR code scans and public links. +#Result: GET /public/report/{scan_id} serves the public report endpoint +#without auth, used by QR code scans and public links. # Load .env file if present (python-dotenv) try: @@ -95,8 +96,11 @@ async def lifespan(app: FastAPI): ) yield +from fastapi import FastAPI +from api.public import router as public_router app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan) +app.include_router(public_router , prefix="/public", tags=["public"]) _cors_origins = ["*"] if CORS_ALLOW_ALL else [ FRONTEND_URL, diff --git a/backend/public.py b/backend/public.py index faff589..2526a38 100644 --- a/backend/public.py +++ b/backend/public.py @@ -9,4 +9,4 @@ async def get_public_report(scan_id: str): result = supabase.table("scans").select("*").eq("id", scan_id).single().execute() if not result.data: raise HTTPException(status_code=404, detail="Scan not found") - return result.data \ No newline at end of file + return result.data diff --git a/src/App.tsx b/src/App.tsx index 0b4417b..f2592bf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -32,9 +32,6 @@ export default function App() { {/* Catch-all route for broken links/404s */} } /> - import PublicReport from './pages/PublicReport'; - - // Inside : } /> diff --git a/src/pages/PublicReport.tsx b/src/pages/PublicReport.tsx index edbefe4..3b2d417 100644 --- a/src/pages/PublicReport.tsx +++ b/src/pages/PublicReport.tsx @@ -3,7 +3,12 @@ import { useParams } from 'react-router-dom'; export default function PublicReport() { const { id } = useParams(); - const [scan, setScan] = useState(null); + interface ScanData { + id: string; + // add other fields your scan object has + [key: string]: unknown; +} +const [scan, setScan] = useState(null); const [copied, setCopied] = useState(false); useEffect(() => { From f965355a0c3eccb0ca66a50adfec5d705e4d343e Mon Sep 17 00:00:00 2001 From: Samal Bishnupriya Date: Fri, 5 Jun 2026 11:08:49 +0530 Subject: [PATCH 3/5] fix: resolve TypeScript and ruff lint errors --- backend/main.py | 1 + src/App.tsx | 1 + src/pages/PublicReport.tsx | 40 +++++++++++++++++++++++++------------- src/pages/ScannerPage.tsx | 24 +++++++++++++++++++++++ 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/backend/main.py b/backend/main.py index e898da0..02bbf30 100644 --- a/backend/main.py +++ b/backend/main.py @@ -6,6 +6,7 @@ from datetime import datetime, timezone from contextlib import asynccontextmanager from typing import Optional +app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan) from api.public import router as public_router app.include_router(public_router, prefix="/public", tags=["public"]) diff --git a/src/App.tsx b/src/App.tsx index f2592bf..a00de5b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,6 +10,7 @@ import MarketMapPage from './pages/MarketMapPage'; import ResultsPage from './pages/ResultsPage'; import PostHogPageView from './components/PostHogPageView'; import NotFound from './pages/NotFound'; +import PublicReport from "./pages/PublicReport"; export default function App() { return ( diff --git a/src/pages/PublicReport.tsx b/src/pages/PublicReport.tsx index 3b2d417..359ddf2 100644 --- a/src/pages/PublicReport.tsx +++ b/src/pages/PublicReport.tsx @@ -1,20 +1,24 @@ import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; -export default function PublicReport() { - const { id } = useParams(); - interface ScanData { +interface ScanData { id: string; - // add other fields your scan object has - [key: string]: unknown; + created_at: string; + freshness_score: number; + grade: string; + label: string; + markers: Record; } -const [scan, setScan] = useState(null); + +export default function PublicReport() { + const { id } = useParams(); + const [scan, setScan] = useState(null); const [copied, setCopied] = useState(false); useEffect(() => { fetch(`/api/public/report/${id}`) .then(r => r.json()) - .then(setScan); + .then((data: ScanData) => setScan(data)); }, [id]); const handleShare = () => { @@ -30,18 +34,28 @@ const [scan, setScan] = useState(null); return (

FreshScan AI — Freshness Report

-

{new Date(scan.created_at).toLocaleString()}

+

+ {new Date(scan.created_at).toLocaleString()} +

-

{scan.freshness_score}

-

{scan.grade} — {scan.label}

+

+ {scan.freshness_score} +

+

+ {scan.grade} — {scan.label} +

- {/* Marker breakdown, image, etc. from scan data */} -
{JSON.stringify(scan.markers, null, 2)}
+
+        {JSON.stringify(scan.markers, null, 2)}
+      
-
)} + {scanComplete && ( +
+ {/* ... existing buttons ... */} + {freshness !== null && ( + + )} +
+)} +These fixes will resolve all TypeScript compilation errors and allow the build to complete successfully. + // After scan completes and grade === 'A' {result.grade === 'A' && ( From adcb9c782e4838d840f3e74e6fc27d49329b261d Mon Sep 17 00:00:00 2001 From: Karan Singh Date: Sat, 6 Jun 2026 08:37:39 +0530 Subject: [PATCH 4/5] fix: resolve failing CI/CD, TS errors, styling violations, and bad fastAPI routing --- backend/main.py | 24 ++++++--- backend/public.py | 12 ----- src/pages/PublicReport.tsx | 100 ++++++++++++++++++++++++++----------- src/pages/ScannerPage.tsx | 53 +++++++------------- 4 files changed, 104 insertions(+), 85 deletions(-) delete mode 100644 backend/public.py diff --git a/backend/main.py b/backend/main.py index 02bbf30..eb1836a 100644 --- a/backend/main.py +++ b/backend/main.py @@ -6,12 +6,7 @@ from datetime import datetime, timezone from contextlib import asynccontextmanager from typing import Optional -app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan) -from api.public import router as public_router -app.include_router(public_router, prefix="/public", tags=["public"]) -#Result: GET /public/report/{scan_id} serves the public report endpoint -#without auth, used by QR code scans and public links. # Load .env file if present (python-dotenv) try: @@ -98,10 +93,8 @@ async def lifespan(app: FastAPI): yield from fastapi import FastAPI -from api.public import router as public_router app = FastAPI(title="FreshScan AI", version="1.1.0", lifespan=lifespan) -app.include_router(public_router , prefix="/public", tags=["public"]) _cors_origins = ["*"] if CORS_ALLOW_ALL else [ FRONTEND_URL, @@ -355,6 +348,23 @@ async def get_me(current_user=Depends(get_current_user)): } +@app.get("/api/v1/public/report/{scan_id}") +async def get_public_report(scan_id: str): + try: + resp = _db().table("scans").select("*").eq("id", scan_id).execute() + if not resp.data: + raise HTTPException(status_code=404, detail="Scan not found") + return {"success": True, "scan": resp.data[0]} + except Exception as exc: + raise HTTPException(status_code=500, detail=str(exc)) + +# ── MAIN ────────────────────────────────────────────────────────────────────── + +if __name__ == "__main__": + import uvicorn + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) + + # ── SCAN ────────────────────────────────────────────────────────────────────── @app.post("/api/v1/scan") diff --git a/backend/public.py b/backend/public.py deleted file mode 100644 index 2526a38..0000000 --- a/backend/public.py +++ /dev/null @@ -1,12 +0,0 @@ -# backend/api/public.py -from fastapi import APIRouter, HTTPException -from ..db import supabase # adjust import path to match your project - -router = APIRouter() - -@router.get("/report/{scan_id}") -async def get_public_report(scan_id: str): - result = supabase.table("scans").select("*").eq("id", scan_id).single().execute() - if not result.data: - raise HTTPException(status_code=404, detail="Scan not found") - return result.data diff --git a/src/pages/PublicReport.tsx b/src/pages/PublicReport.tsx index 359ddf2..eb88a86 100644 --- a/src/pages/PublicReport.tsx +++ b/src/pages/PublicReport.tsx @@ -1,5 +1,6 @@ import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; +import StatusTerminal from '../components/StatusTerminal'; interface ScanData { id: string; @@ -14,11 +15,16 @@ export default function PublicReport() { const { id } = useParams(); const [scan, setScan] = useState(null); const [copied, setCopied] = useState(false); + const [error, setError] = useState(false); useEffect(() => { - fetch(`/api/public/report/${id}`) - .then(r => r.json()) - .then((data: ScanData) => setScan(data)); + fetch(`/api/v1/public/report/${id}`) + .then(r => { + if (!r.ok) throw new Error("Not found"); + return r.json(); + }) + .then((data) => setScan(data.scan)) + .catch(() => setError(true)); }, [id]); const handleShare = () => { @@ -29,38 +35,72 @@ export default function PublicReport() { const handlePrint = () => window.print(); - if (!scan) return
Loading...
; + if (error) { + return ( +
+ +
+ ); + } + + if (!scan) { + return ( +
+ +
+ ); + } return ( -
-

FreshScan AI — Freshness Report

-

- {new Date(scan.created_at).toLocaleString()} -

+
+
+ + {/* Header */} +
+

+ FreshScan Public Report +

+

+ GENERATED: {new Date(scan.created_at).toLocaleString()} | ID: {scan.id} +

+
-
-

- {scan.freshness_score} -

-

- {scan.grade} — {scan.label} -

-
+ {/* Score Card */} +
+

FRESHNESS_SCORE

+

= 85 ? 'text-secondary' : 'text-neon'} print:text-black mb-4`}> + {scan.freshness_score} +

+
+

+ GRADE {scan.grade} — {scan.label} +

+
+
-
-        {JSON.stringify(scan.markers, null, 2)}
-      
+ {/* Data Markers */} +
+

RAW_MARKERS

+
+            {JSON.stringify(scan.markers, null, 2)}
+          
+
-
- - + {/* Controls */} +
+ + +
); diff --git a/src/pages/ScannerPage.tsx b/src/pages/ScannerPage.tsx index 3dd614c..f89e42d 100644 --- a/src/pages/ScannerPage.tsx +++ b/src/pages/ScannerPage.tsx @@ -351,42 +351,23 @@ export default function ScannerPage() {
)} - {scanComplete && ( -
- {/* ... existing buttons ... */} - {freshness !== null && ( - - )} -
-)} -These fixes will resolve all TypeScript compilation errors and allow the build to complete successfully. - - - // After scan completes and grade === 'A' - {result.grade === 'A' && ( - - )} + {scanComplete && freshness !== null && freshness >= 85 && ( +
+ +
+ )} Date: Sat, 6 Jun 2026 08:43:56 +0530 Subject: [PATCH 5/5] fix: resolve invalid vercel.json syntax --- vercel.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/vercel.json b/vercel.json index 0cc8407..b78983b 100644 --- a/vercel.json +++ b/vercel.json @@ -8,7 +8,6 @@ "source": "/ingest/:path*", "destination": "https://us.i.posthog.com/:path*" }, - { "source": "/(.*)", "destination": "/" } - { "source": "/report/:id", "destination": "/index.html" } + { "source": "/(.*)", "destination": "/index.html" } ] }