feat: add vendor trust score endpoint and leaderboard UI (closes #45)#52
feat: add vendor trust score endpoint and leaderboard UI (closes #45)#52Aharshi3614 wants to merge 1 commit into
Conversation
|
@Aharshi3614 is attempting to deploy a commit to the karan3431's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
🎉 Thank you for your Pull Request! We're thrilled to have your contribution to FreshScan AI. Before we review, please make sure you have:
A maintainer will review your code as soon as possible! |
There was a problem hiding this comment.
6 issues found across 5 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/migrations/add_vendor_trust_score.sql">
<violation number="1" location="backend/migrations/add_vendor_trust_score.sql:12">
P1: `avg_freshness_score NUMERIC(4,2)` cannot store the value `100.00`, which is possible when a vendor's average freshness reaches the maximum of the 0–100 percentage scale. This will cause numeric overflow errors at runtime.</violation>
<violation number="2" location="backend/migrations/add_vendor_trust_score.sql:12">
P2: `ADD COLUMN IF NOT EXISTS` silently skips the entire column definition when a column already exists, masking missing or incorrect type, defaults, and CHECK constraints. The `avg_freshness_score` column already exists in `master_schema.sql` as `INTEGER DEFAULT 0`, so this migration silently fails to change it to `NUMERIC(4,2) DEFAULT 0.0`. Similarly, `trust_badge` and `trend` CHECK constraints and defaults would be skipped if those columns were ever added manually. This creates schema drift across environments.</violation>
</file>
<file name="backend/vendors.py">
<violation number="1" location="backend/vendors.py:35">
P2: Trend calculation incorrectly ignores valid `freshness_index = 0` values, which can skew vendor trend results.</violation>
<violation number="2" location="backend/vendors.py:57">
P1: Missing range validation on `limit` query parameter in public `/leaderboard` endpoint</violation>
<violation number="3" location="backend/vendors.py:99">
P1: State-changing POST `/recalculate` endpoint is unauthenticated, allowing any caller to mutate vendor trust scores, badges, and trends. Other write endpoints in the app require `Depends(get_current_user)` but this endpoint lacks auth entirely.</violation>
</file>
<file name="src/pages/Leaderboard.tsx">
<violation number="1" location="src/pages/Leaderboard.tsx:35">
P1: This fetch bypasses the configured API base URL, so leaderboard requests can fail in deployed environments where backend and frontend are on different origins.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| CHECK (trend IN ('up', 'down', 'stable')) | ||
| DEFAULT 'stable', | ||
| ADD COLUMN IF NOT EXISTS total_scans INTEGER DEFAULT 0, | ||
| ADD COLUMN IF NOT EXISTS avg_freshness_score NUMERIC(4,2) DEFAULT 0.0; |
There was a problem hiding this comment.
P1: avg_freshness_score NUMERIC(4,2) cannot store the value 100.00, which is possible when a vendor's average freshness reaches the maximum of the 0–100 percentage scale. This will cause numeric overflow errors at runtime.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/migrations/add_vendor_trust_score.sql, line 12:
<comment>`avg_freshness_score NUMERIC(4,2)` cannot store the value `100.00`, which is possible when a vendor's average freshness reaches the maximum of the 0–100 percentage scale. This will cause numeric overflow errors at runtime.</comment>
<file context>
@@ -0,0 +1,16 @@
+ CHECK (trend IN ('up', 'down', 'stable'))
+ DEFAULT 'stable',
+ ADD COLUMN IF NOT EXISTS total_scans INTEGER DEFAULT 0,
+ ADD COLUMN IF NOT EXISTS avg_freshness_score NUMERIC(4,2) DEFAULT 0.0;
+
+-- Index for fast leaderboard sorting
</file context>
| """ | ||
|
|
||
| @router.get("/leaderboard") | ||
| async def get_leaderboard(limit: int = 20): |
There was a problem hiding this comment.
P1: Missing range validation on limit query parameter in public /leaderboard endpoint
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/vendors.py, line 57:
<comment>Missing range validation on `limit` query parameter in public `/leaderboard` endpoint</comment>
<file context>
@@ -0,0 +1,143 @@
+ """
+
+ @router.get("/leaderboard")
+ async def get_leaderboard(limit: int = 20):
+ """Public leaderboard — no auth required."""
+ try:
</file context>
| except Exception as exc: | ||
| raise HTTPException(status_code=500, detail=str(exc)) | ||
|
|
||
| @router.post("/{vendor_id}/recalculate") |
There was a problem hiding this comment.
P1: State-changing POST /recalculate endpoint is unauthenticated, allowing any caller to mutate vendor trust scores, badges, and trends. Other write endpoints in the app require Depends(get_current_user) but this endpoint lacks auth entirely.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/vendors.py, line 99:
<comment>State-changing POST `/recalculate` endpoint is unauthenticated, allowing any caller to mutate vendor trust scores, badges, and trends. Other write endpoints in the app require `Depends(get_current_user)` but this endpoint lacks auth entirely.</comment>
<file context>
@@ -0,0 +1,143 @@
+ except Exception as exc:
+ raise HTTPException(status_code=500, detail=str(exc))
+
+ @router.post("/{vendor_id}/recalculate")
+ async def recalculate_trust_score(vendor_id: str):
+ """
</file context>
| const [error, setError] = useState<string | null>(null); | ||
|
|
||
| useEffect(() => { | ||
| fetch("/api/v1/vendors/leaderboard") |
There was a problem hiding this comment.
P1: This fetch bypasses the configured API base URL, so leaderboard requests can fail in deployed environments where backend and frontend are on different origins.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/pages/Leaderboard.tsx, line 35:
<comment>This fetch bypasses the configured API base URL, so leaderboard requests can fail in deployed environments where backend and frontend are on different origins.</comment>
<file context>
@@ -0,0 +1,120 @@
+ const [error, setError] = useState<string | null>(null);
+
+ useEffect(() => {
+ fetch("/api/v1/vendors/leaderboard")
+ .then((r) => {
+ if (!r.ok) throw new Error("Failed to fetch leaderboard.");
</file context>
| CHECK (trend IN ('up', 'down', 'stable')) | ||
| DEFAULT 'stable', | ||
| ADD COLUMN IF NOT EXISTS total_scans INTEGER DEFAULT 0, | ||
| ADD COLUMN IF NOT EXISTS avg_freshness_score NUMERIC(4,2) DEFAULT 0.0; |
There was a problem hiding this comment.
P2: ADD COLUMN IF NOT EXISTS silently skips the entire column definition when a column already exists, masking missing or incorrect type, defaults, and CHECK constraints. The avg_freshness_score column already exists in master_schema.sql as INTEGER DEFAULT 0, so this migration silently fails to change it to NUMERIC(4,2) DEFAULT 0.0. Similarly, trust_badge and trend CHECK constraints and defaults would be skipped if those columns were ever added manually. This creates schema drift across environments.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/migrations/add_vendor_trust_score.sql, line 12:
<comment>`ADD COLUMN IF NOT EXISTS` silently skips the entire column definition when a column already exists, masking missing or incorrect type, defaults, and CHECK constraints. The `avg_freshness_score` column already exists in `master_schema.sql` as `INTEGER DEFAULT 0`, so this migration silently fails to change it to `NUMERIC(4,2) DEFAULT 0.0`. Similarly, `trust_badge` and `trend` CHECK constraints and defaults would be skipped if those columns were ever added manually. This creates schema drift across environments.</comment>
<file context>
@@ -0,0 +1,16 @@
+ CHECK (trend IN ('up', 'down', 'stable'))
+ DEFAULT 'stable',
+ ADD COLUMN IF NOT EXISTS total_scans INTEGER DEFAULT 0,
+ ADD COLUMN IF NOT EXISTS avg_freshness_score NUMERIC(4,2) DEFAULT 0.0;
+
+-- Index for fast leaderboard sorting
</file context>
| .lt("timestamp", week_ago).execute() | ||
|
|
||
| def avg(rows): | ||
| vals = [r["freshness_index"] for r in rows if r.get("freshness_index")] |
There was a problem hiding this comment.
P2: Trend calculation incorrectly ignores valid freshness_index = 0 values, which can skew vendor trend results.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/vendors.py, line 35:
<comment>Trend calculation incorrectly ignores valid `freshness_index = 0` values, which can skew vendor trend results.</comment>
<file context>
@@ -0,0 +1,143 @@
+ .lt("timestamp", week_ago).execute()
+
+ def avg(rows):
+ vals = [r["freshness_index"] for r in rows if r.get("freshness_index")]
+ return sum(vals) / len(vals) if vals else None
+
</file context>
jpdevhub
left a comment
There was a problem hiding this comment.
Awesome work on the backend logic, Aharshi! The endpoints and data aggregations are extremely solid. However, the frontend (Leaderboard.tsx) needs some heavy styling adjustments to fit our project's custom design system before we can merge.
| const BADGE: Record<Badge, { emoji: string; label: string; color: string }> = { | ||
| gold: { emoji: "🥇", label: "Gold", color: "#f59e0b" }, | ||
| silver: { emoji: "🥈", label: "Silver", color: "#9ca3af" }, | ||
| bronze: { emoji: "🥉", label: "Bronze", color: "#f97316" }, | ||
| unranked: { emoji: "⚪", label: "Unranked", color: "#d1d5db" }, | ||
| }; | ||
|
|
There was a problem hiding this comment.
NO EMOJIS: This project strictly avoids emojis to maintain a professional, academic/cyberpunk aesthetic. Please remove the 🥇🥈🥉⚪ emojis. Instead, use mono-spaced text labels like [ GOLD ] or use custom SVG icons.
|
|
||
| const TREND: Record<Trend, { icon: string; color: string; label: string }> = { | ||
| up: { icon: "↑", color: "#22c55e", label: "Improving" }, |
There was a problem hiding this comment.
Hardcoded Colors: You used classes like bg-white, text-gray-800, and text-blue-600. These completely break our Dark/Light theme toggle. You must use our CSS variables. For example, replace bg-white with bg-surface-low or use the existing component. Replace text colors with text-on-surface or text-neon.
| const trend = TREND[vendor.trend ?? "stable"]; | ||
| return ( | ||
| <div | ||
| key={vendor.id} |
There was a problem hiding this comment.
Brutalist Aesthetic: Our app avoids soft, rounded UI elements. Please remove rounded-xl and shadow-sm and stick to the sharp borders (border-outline-variant/30) used in the rest of the application.
| <div className="max-w-2xl mx-auto px-4 py-10"> | ||
| <h1 className="text-3xl font-bold mb-1">🐟 Vendor Trust Leaderboard</h1> | ||
| <p className="text-gray-500 mb-8 text-sm"> | ||
| Rankings based on anonymous freshness scans across markets. | ||
| </p> |
There was a problem hiding this comment.
Please utilize our custom fonts. Headers should use font-[family-name:var(--font-display)] and metrics/small text should use font-[family-name:var(--font-mono)] tracking-widest to match the terminal-like appearance.
Description
Adds vendor trust scoring and a public leaderboard.
Changes
Closes #45
Summary by cubic
Adds vendor trust scoring and a public leaderboard to rank vendors by average freshness. Addresses #45 by exposing read endpoints, a recalc flow, and a new /leaderboard page.
New Features
vendorsrouter with:/api/v1/vendors/leaderboard(public)/api/v1/vendors/{vendor_id}/trust-score(public)/api/v1/vendors/{vendor_id}/recalculate/leaderboardshowing badge, score, scans, and trend.Migration
trust_badge,trend,total_scans,avg_freshness_scoretovendors.avg_freshness_scorefor faster sorting.POST /api/v1/vendors/{vendor_id}/recalculatefor vendors with scans.Written for commit 9e2ee09. Summary will update on new commits.