From 8676e3e118e30d40516479dd52eefacc8e4815d8 Mon Sep 17 00:00:00 2001 From: rebelchris Date: Mon, 2 Feb 2026 12:47:30 +0000 Subject: [PATCH 1/3] feat(tools): add frontend mock pages for AI tools directory Add a new frontend-only mock implementation for AI tools visualization: - Create mock data structure with hierarchical AI tools (Anthropic/Claude/Opus-4.5, OpenAI/GPT/GPT-4o, Google/Gemini/Ultra) - Add tools index page displaying all top-level AI tool categories - Add dynamic route [...slug] for hierarchical tool navigation - Create ToolCard, ToolHeader, ToolBreadcrumb components for navigation - Create ToolSentimentChart component with Recharts for sentiment visualization - Create ToolRelatedContent and ToolDiscussion components for engagement Each tool displays sentiment over time, related posts, upvoting capability, and discussion features. State is managed locally (not persisted) for this mock visualization. Co-Authored-By: Claude Opus 4.5 --- .../src/components/tools/ToolBreadcrumb.tsx | 73 ++++ .../shared/src/components/tools/ToolCard.tsx | 56 +++ .../src/components/tools/ToolDiscussion.tsx | 110 +++++ .../src/components/tools/ToolHeader.tsx | 59 +++ .../components/tools/ToolRelatedContent.tsx | 49 +++ .../components/tools/ToolSentimentChart.tsx | 113 +++++ packages/shared/src/lib/toolsMockData.ts | 388 ++++++++++++++++++ packages/webapp/pages/tools/[...slug].tsx | 128 ++++++ packages/webapp/pages/tools/index.tsx | 54 +++ 9 files changed, 1030 insertions(+) create mode 100644 packages/shared/src/components/tools/ToolBreadcrumb.tsx create mode 100644 packages/shared/src/components/tools/ToolCard.tsx create mode 100644 packages/shared/src/components/tools/ToolDiscussion.tsx create mode 100644 packages/shared/src/components/tools/ToolHeader.tsx create mode 100644 packages/shared/src/components/tools/ToolRelatedContent.tsx create mode 100644 packages/shared/src/components/tools/ToolSentimentChart.tsx create mode 100644 packages/shared/src/lib/toolsMockData.ts create mode 100644 packages/webapp/pages/tools/[...slug].tsx create mode 100644 packages/webapp/pages/tools/index.tsx diff --git a/packages/shared/src/components/tools/ToolBreadcrumb.tsx b/packages/shared/src/components/tools/ToolBreadcrumb.tsx new file mode 100644 index 0000000000..ef66d86330 --- /dev/null +++ b/packages/shared/src/components/tools/ToolBreadcrumb.tsx @@ -0,0 +1,73 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import classNames from 'classnames'; +import Link from '../utilities/Link'; +import type { Tool } from '../../lib/toolsMockData'; +import { AiIcon, HomeIcon } from '../icons'; +import { Button, ButtonSize, ButtonVariant } from '../buttons/Button'; + +export interface ToolBreadcrumbProps { + toolPath: Tool[]; + className?: string; +} + +export const ToolBreadcrumb = ({ + toolPath, + className, +}: ToolBreadcrumbProps): ReactElement => { + return ( + + ); +}; diff --git a/packages/shared/src/components/tools/ToolCard.tsx b/packages/shared/src/components/tools/ToolCard.tsx new file mode 100644 index 0000000000..4417e810fe --- /dev/null +++ b/packages/shared/src/components/tools/ToolCard.tsx @@ -0,0 +1,56 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import classNames from 'classnames'; +import Link from '../utilities/Link'; +import type { Tool } from '../../lib/toolsMockData'; +import { Image } from '../image/Image'; +import { UpvoteIcon } from '../icons'; +import { largeNumberFormat } from '../../lib'; + +export interface ToolCardProps { + tool: Tool; + className?: string; +} + +export const ToolCard = ({ tool, className }: ToolCardProps): ReactElement => { + const hasChildren = tool.children.length > 0; + + return ( + + +
+ {`${tool.name} +
+

+ {tool.name} +

+
+ + + {largeNumberFormat(tool.upvotes)} + + {hasChildren && ( + <> + ยท + {tool.children.length} tools + + )} +
+
+
+

+ {tool.description} +

+
+ + ); +}; diff --git a/packages/shared/src/components/tools/ToolDiscussion.tsx b/packages/shared/src/components/tools/ToolDiscussion.tsx new file mode 100644 index 0000000000..c7389f1fa5 --- /dev/null +++ b/packages/shared/src/components/tools/ToolDiscussion.tsx @@ -0,0 +1,110 @@ +import type { ReactElement } from 'react'; +import React from 'react'; +import classNames from 'classnames'; +import type { MockComment } from '../../lib/toolsMockData'; +import { Image } from '../image/Image'; +import { + Button, + ButtonSize, + ButtonVariant, + ButtonColor, +} from '../buttons/Button'; +import { UpvoteIcon } from '../icons'; +import { largeNumberFormat } from '../../lib'; + +export interface ToolDiscussionProps { + comments: MockComment[]; + upvotedComments: Set; + onUpvoteComment: (commentId: string) => void; + className?: string; +} + +const formatTimestamp = (timestamp: string): string => { + const date = new Date(timestamp); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); + + if (diffDays === 0) { + return 'Today'; + } + if (diffDays === 1) { + return 'Yesterday'; + } + if (diffDays < 7) { + return `${diffDays} days ago`; + } + return date.toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + }); +}; + +export const ToolDiscussion = ({ + comments, + upvotedComments, + onUpvoteComment, + className, +}: ToolDiscussionProps): ReactElement => { + return ( +
+

+ Discussion +

+
+