From b8ba53598d4f95dcc45d3fa5e778b7fc9ca23c15 Mon Sep 17 00:00:00 2001 From: cheffe01 <32869042-cheffe01@users.noreply.replit.com> Date: Mon, 25 May 2026 13:28:34 +0000 Subject: [PATCH] Add icon support for tools and improve analytics chart display Update API, database schema, and frontend components to allow optional icons for tools, and adjust analytics chart rendering for better visibility. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 776963d0-f75d-42e2-a57b-cc36bdff8495 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 0ad0b686-eebd-46d2-8314-67e9e874e224 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0683fb79-a27c-485c-9333-5f4b288c4567/776963d0-f75d-42e2-a57b-cc36bdff8495/1p7jhzu Replit-Helium-Checkpoint-Created: true --- artifacts/api-server/src/routes/tools.ts | 2 + .../toolrate/src/components/tool-card.tsx | 24 ++++++++-- artifacts/toolrate/src/pages/analytics.tsx | 46 +++++++++++-------- artifacts/toolrate/src/pages/tool-detail.tsx | 22 +++++++-- artifacts/toolrate/src/pages/tool-new.tsx | 37 +++++++++++++++ .../src/generated/api.schemas.ts | 6 +++ lib/api-spec/openapi.yaml | 8 ++++ lib/api-zod/src/generated/api.ts | 7 +++ lib/api-zod/src/generated/types/tool.ts | 2 + lib/api-zod/src/generated/types/toolInput.ts | 1 + lib/api-zod/src/generated/types/toolUpdate.ts | 1 + .../src/generated/types/toolWithStats.ts | 2 + lib/db/src/schema/tools.ts | 1 + 13 files changed, 132 insertions(+), 27 deletions(-) diff --git a/artifacts/api-server/src/routes/tools.ts b/artifacts/api-server/src/routes/tools.ts index a75d7b1..1b881da 100644 --- a/artifacts/api-server/src/routes/tools.ts +++ b/artifacts/api-server/src/routes/tools.ts @@ -84,6 +84,7 @@ router.post("/tools", requireAuth, async (req, res): Promise => { description: parsed.data.description, category: parsed.data.category, websiteUrl: parsed.data.websiteUrl ?? null, + iconUrl: parsed.data.iconUrl ?? null, features: parsed.data.features ?? [], tags: parsed.data.tags ?? [], }).returning(); @@ -130,6 +131,7 @@ router.patch("/tools/:id", requireAuth, async (req, res): Promise => { if (parsed.data.description !== undefined) updateData.description = parsed.data.description; if (parsed.data.category !== undefined) updateData.category = parsed.data.category; if (parsed.data.websiteUrl !== undefined) updateData.websiteUrl = parsed.data.websiteUrl; + if (parsed.data.iconUrl !== undefined) updateData.iconUrl = parsed.data.iconUrl; if (parsed.data.features !== undefined) updateData.features = parsed.data.features; if (parsed.data.tags !== undefined) updateData.tags = parsed.data.tags; diff --git a/artifacts/toolrate/src/components/tool-card.tsx b/artifacts/toolrate/src/components/tool-card.tsx index 22253fd..65639dd 100644 --- a/artifacts/toolrate/src/components/tool-card.tsx +++ b/artifacts/toolrate/src/components/tool-card.tsx @@ -1,7 +1,7 @@ import { ToolWithStats } from "@workspace/api-client-react"; import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { Star, MessageSquare } from "lucide-react"; +import { Star, MessageSquare, Wrench } from "lucide-react"; import { Link } from "wouter"; export function ToolCard({ tool }: { tool: ToolWithStats }) { @@ -9,9 +9,25 @@ export function ToolCard({ tool }: { tool: ToolWithStats }) { -
- {tool.name} - {tool.category} +
+
+ {tool.iconUrl ? ( + {tool.name} { (e.target as HTMLImageElement).style.display = "none"; }} + /> + ) : ( + + )} +
+
+
+ {tool.name} + {tool.category} +
+
{tool.description} diff --git a/artifacts/toolrate/src/pages/analytics.tsx b/artifacts/toolrate/src/pages/analytics.tsx index 6817d71..b732f97 100644 --- a/artifacts/toolrate/src/pages/analytics.tsx +++ b/artifacts/toolrate/src/pages/analytics.tsx @@ -164,30 +164,36 @@ export default function Analytics() { {loadingDistribution ? ( - + ) : ( -
-
+
+
Usefulness - - - - - - - - +
+ + + + + + + + + +
-
+
Usability - - - - - - - - +
+ + + + + + + + + +
)} diff --git a/artifacts/toolrate/src/pages/tool-detail.tsx b/artifacts/toolrate/src/pages/tool-detail.tsx index 6ae113d..6ce453d 100644 --- a/artifacts/toolrate/src/pages/tool-detail.tsx +++ b/artifacts/toolrate/src/pages/tool-detail.tsx @@ -142,9 +142,25 @@ export default function ToolDetail() {
-
-

{tool.name}

- {tool.category} +
+
+ {tool.iconUrl ? ( + {tool.name} { (e.target as HTMLImageElement).style.display = "none"; }} + /> + ) : ( + + {tool.name.charAt(0).toUpperCase()} + + )} +
+
+

{tool.name}

+ {tool.category} +

diff --git a/artifacts/toolrate/src/pages/tool-new.tsx b/artifacts/toolrate/src/pages/tool-new.tsx index 95ee38c..00f75e0 100644 --- a/artifacts/toolrate/src/pages/tool-new.tsx +++ b/artifacts/toolrate/src/pages/tool-new.tsx @@ -23,6 +23,7 @@ const toolSchema = z.object({ description: z.string().min(10, "Description must be at least 10 characters"), category: z.string().min(2, "Category is required"), websiteUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")), + iconUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")), features: z.array(z.object({ value: z.string() })).optional(), tags: z.array(z.object({ value: z.string() })).optional(), }); @@ -43,6 +44,7 @@ export default function ToolNew() { description: "", category: "", websiteUrl: "", + iconUrl: "", features: [{ value: "" }], tags: [{ value: "" }], }, @@ -62,6 +64,7 @@ export default function ToolNew() { const payload = { ...data, websiteUrl: data.websiteUrl || undefined, + iconUrl: data.iconUrl || undefined, features: data.features?.map((f) => f.value).filter((v) => v.trim() !== ""), tags: data.tags?.map((t) => t.value).filter((v) => v.trim() !== ""), }; @@ -166,6 +169,40 @@ export default function ToolNew() { )} /> + ( + + Icon / Logo URL (Optional) + +

+
+ {field.value ? ( + icon preview { (e.target as HTMLImageElement).style.display = "none"; }} + /> + ) : ( + img + )} +
+ +
+ + + + )} + /> +