-
-
-
- {/* Milestone Markers */}
-
- {[0, 25, 50, 75, 100].map((milestone) => (
-
= milestone
- ? 'bg-primary border-primary'
- : 'bg-background border-border'
- }
- `}
- />
- ))}
-
-
-
- {/* Milestone Labels */}
-
- T+0
- T+7
- T+14
- T+30
- Done
-
-
-
- {/* Actions */}
-
-
-
- Next: {nextReview}
-
-
-
-
-
-
- Report
-
-
-
- Download your learning progress report
-
-
-
-
-
- {/* Review Explanation */}
-
-
- Based on spaced repetition for optimal retention
-
-
-
- );
-}
diff --git a/web/src/components/Message.tsx b/web/src/components/Message.tsx
deleted file mode 100644
index 112f7464ba919910457ec7581bdfac4ec523e626..0000000000000000000000000000000000000000
--- a/web/src/components/Message.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-// web/src/components/Message.tsx
-import React from "react";
-import ReactMarkdown from "react-markdown";
-import remarkGfm from "remark-gfm";
-
-import type { Message as MessageType, LearningMode } from "../App";
-
-interface MessageProps {
- message: MessageType;
-
- // existing props you are already passing in ChatArea
- showSenderInfo?: boolean;
- userId?: string;
- isLoggedIn: boolean;
- learningMode: LearningMode;
- docType?: string;
- lastUserText?: string;
-}
-
-export function Message({
- message,
- showSenderInfo,
- userId,
- isLoggedIn,
- learningMode,
- docType,
- lastUserText,
-}: MessageProps) {
- const isUser = message.role === "user";
-
- // If you already have avatar / sender rendering logic, keep it.
- // The only critical change is content rendering below.
- return (
-
- {!isUser && (
-
- C
-
- )}
-
-
- {/* Optional sender info (group mode) */}
- {showSenderInfo && message.sender && !isUser && (
-
- {message.sender.name}
-
- )}
-
-
- {/* ✅ THE FIX: render markdown instead of plain text */}
-
- {message.content || ""}
-
-
- {/* If you already render references, keep your original block here */}
- {message.references && message.references.length > 0 && (
-
-
References
- {message.references.map((r, idx) => (
-
- {r}
-
- ))}
-
- )}
-
- {/* If you already have feedback buttons inside Message, keep them here.
- Do not change logic—only keep UI. */}
-
-
-
- {isUser && (
-
- U
-
- )}
-
- );
-}
diff --git a/web/src/components/RightPanel.tsx b/web/src/components/RightPanel.tsx
deleted file mode 100644
index c0bc0a3f6a8c7d077e83099c23fd9d90492d3f14..0000000000000000000000000000000000000000
--- a/web/src/components/RightPanel.tsx
+++ /dev/null
@@ -1,307 +0,0 @@
-// web/src/components/RightPanel.tsx
-import React, { useState } from 'react';
-import { Button } from './ui/button';
-import { Input } from './ui/input';
-import { Label } from './ui/label';
-import { Card } from './ui/card';
-import { Separator } from './ui/separator';
-import { Textarea } from './ui/textarea';
-import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
-import { LogIn, LogOut, FileText, MessageSquare, Download, ClipboardList, Sparkles } from 'lucide-react';
-import type { User } from '../App';
-import { toast } from 'sonner';
-import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogHeader,
- DialogTitle,
- DialogFooter,
-} from './ui/dialog';
-
-// ✅ Markdown 渲染
-import ReactMarkdown from 'react-markdown';
-import remarkGfm from 'remark-gfm';
-
-interface RightPanelProps {
- user: User | null;
- onLogin: (name: string, emailOrId: string) => void;
- onLogout: () => void;
- isLoggedIn: boolean;
- onClose?: () => void;
-
- exportResult: string;
- setExportResult: (result: string) => void;
- resultType: 'export' | 'quiz' | 'summary' | null;
- setResultType: (type: 'export' | 'quiz' | 'summary' | null) => void;
-
- onExport: () => void;
- onQuiz: () => void;
- onSummary: () => void;
-}
-
-export function RightPanel({
- user,
- onLogin,
- onLogout,
- isLoggedIn,
- exportResult,
- setExportResult,
- resultType,
- setResultType,
- onExport,
- onQuiz,
- onSummary,
-}: RightPanelProps) {
- const [showLoginForm, setShowLoginForm] = useState(false);
- const [name, setName] = useState('');
- const [emailOrId, setEmailOrId] = useState('');
-
- const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
- const [feedbackText, setFeedbackText] = useState('');
- const [feedbackCategory, setFeedbackCategory] = useState<'general' | 'bug' | 'feature'>('general');
-
- const handleLoginClick = () => {
- if (!name.trim() || !emailOrId.trim()) {
- toast.error('Please fill in all fields');
- return;
- }
- onLogin(name.trim(), emailOrId.trim());
- setShowLoginForm(false);
- setName('');
- setEmailOrId('');
- };
-
- const handleLogoutClick = () => {
- onLogout();
- setShowLoginForm(false);
- };
-
- const handleFeedbackSubmit = () => {
- if (!feedbackText.trim()) {
- toast.error('Please provide feedback text');
- return;
- }
- console.log('Feedback submitted:', feedbackText, feedbackCategory);
- setFeedbackDialogOpen(false);
- setFeedbackText('');
- toast.success('Feedback submitted!');
- };
-
- return (
-
- {/* Account */}
-
- {!isLoggedIn ? (
-
-
-
Welcome to Clare!
-
- Log in to start your learning session
-
-
-
- {!showLoginForm ? (
-
setShowLoginForm(true)} className="w-full gap-2">
-
- Student Login
-
- ) : (
-
-
- Name
- setName(e.target.value)}
- placeholder="Enter your name"
- />
-
-
- Email / Student ID
- setEmailOrId(e.target.value)}
- placeholder="Enter your email or ID"
- />
-
-
-
- Enter
-
- setShowLoginForm(false)}>
- Cancel
-
-
-
- )}
-
- ) : (
-
-
-
- {user?.name?.charAt(0).toUpperCase()}
-
-
-
{user?.name}
-
{user?.email}
-
user_id: {user?.user_id}
-
-
-
-
-
- Log Out
-
-
- )}
-
-
- {/* Actions (3 buttons in one row) */}
-
-
-
Actions
-
-
- {/* ✅ 关键点:
- - flex + flex-1 保证同宽
- - min-w-0 防止内容撑开导致换行
- - h-11 / rounded-xl 统一尺寸
- */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* Results */}
-
-
- {resultType === 'export' && 'Exported Conversation'}
- {resultType === 'quiz' && 'Micro-Quiz'}
- {resultType === 'summary' && 'Summarization'}
- {!resultType && 'Results'}
-
-
-
- {exportResult ? (
-
-
-
- {
- navigator.clipboard.writeText(exportResult);
- toast.success('Copied to clipboard!');
- }}
- >
- Copy
-
-
-
- {/* ✅ Markdown render (fix **bold**, lists, code blocks, etc.) */}
-
-
- {exportResult}
-
-
-
- ) : (
-
- Results (export / summary / quiz) will appear here after actions run
-
- )}
-
-
-
-
-
- {/* Feedback */}
-
-
Feedback
- setFeedbackDialogOpen(true)}
- >
-
- Provide Feedback
-
-
-
-
-
-
- Provide Feedback
- Help us improve Clare by sharing your thoughts and suggestions.
-
-
-
-
- Category
- setFeedbackCategory(v as any)}>
-
-
-
-
- General Feedback
- Bug Report
- Feature Request
-
-
-
-
-
- Feedback
-
-
-
-
- setFeedbackDialogOpen(false)}>
- Cancel
-
- Submit
-
-
-
-
- );
-}
diff --git a/web/src/components/UserGuide.tsx b/web/src/components/UserGuide.tsx
deleted file mode 100644
index a303e217e4686b3228f6e79ac2ee790c60aae6ec..0000000000000000000000000000000000000000
--- a/web/src/components/UserGuide.tsx
+++ /dev/null
@@ -1,151 +0,0 @@
-import React from 'react';
-import {
- Accordion,
- AccordionContent,
- AccordionItem,
- AccordionTrigger,
-} from './ui/accordion';
-
-export function UserGuide() {
- return (
-
-
User Guide
-
-
-
- Getting Started
-
- Welcome to Clare! To begin:
-
- Log in using your student credentials
- Select your preferred learning mode
- Upload course materials (optional)
- Start asking questions!
-
-
-
-
-
- Learning Modes
-
- Concept Explainer: Get detailed explanations of complex topics with examples.
- Socratic Tutor: Learn through guided questions and discovery.
- Exam Prep/Quiz: Test your knowledge with practice questions.
- Assignment Helper: Get step-by-step guidance on homework.
- Quick Summary: Receive concise summaries of key concepts.
-
-
-
-
- How Clare Works
-
- Clare uses advanced AI to provide personalized tutoring:
-
- Analyzes your questions and learning style
- References uploaded course materials
- Adapts explanations to your level
- Tracks your learning progress over time
-
-
-
-
-
- Memory Line
-
- The Memory Line tracks your retention using spaced repetition:
-
- T+0: Initial learning
- T+7: First review (1 week)
- T+14: Second review (2 weeks)
- T+30: Final review (1 month)
-
- Regular reviews help solidify your understanding!
-
-
-
-
- Learning Progress Report
-
- Your progress report shows:
-
- Topics covered and mastered
- Time spent learning
- Quiz scores and performance
- Recommended review topics
-
-
-
-
-
- Using Your Files
-
- Upload course materials to enhance Clare's responses:
-
- Supported formats: .docx, .pdf, .pptx
- Clare will reference your materials in answers
- Files are processed securely and privately
- You can remove files anytime
-
-
-
-
-
- Micro-Quiz
-
- Test your understanding with quick quizzes:
-
- Generated based on your conversation
- Multiple choice and short answer questions
- Instant feedback and explanations
- Track your quiz performance over time
-
-
-
-
-
- Summarization
-
- Get concise summaries of your learning session:
-
- Key concepts discussed
- Important takeaways
- Topics to review
- Next steps for learning
-
-
-
-
-
- Export Conversation
-
- Save your conversation for later review:
-
- Export as PDF or text file
- Includes all messages and references
- Perfect for study notes
- Share with study groups (optional)
-
-
-
-
-
- FAQ
-
-
-
Is my data private?
-
Yes! Your conversations and files are encrypted and never shared.
-
-
-
Can Clare do my homework?
-
Clare is designed to help you learn, not do work for you. It provides guidance and explanations to help you understand concepts.
-
-
-
How accurate is Clare?
-
Clare is highly accurate but should be used as a learning aid. Always verify important information with course materials.
-
-
-
-
-
- );
-}
diff --git a/web/src/components/figma/ImageWithFallback.tsx b/web/src/components/figma/ImageWithFallback.tsx
deleted file mode 100644
index 0e26139bea20f0f26f899190b21161e44510e740..0000000000000000000000000000000000000000
--- a/web/src/components/figma/ImageWithFallback.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, { useState } from 'react'
-
-const ERROR_IMG_SRC =
- 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODgiIGhlaWdodD0iODgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjMDAwIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBvcGFjaXR5PSIuMyIgZmlsbD0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIzLjciPjxyZWN0IHg9IjE2IiB5PSIxNiIgd2lkdGg9IjU2IiBoZWlnaHQ9IjU2IiByeD0iNiIvPjxwYXRoIGQ9Im0xNiA1OCAxNi0xOCAzMiAzMiIvPjxjaXJjbGUgY3g9IjUzIiBjeT0iMzUiIHI9IjciLz48L3N2Zz4KCg=='
-
-export function ImageWithFallback(props: React.ImgHTMLAttributes
) {
- const [didError, setDidError] = useState(false)
-
- const handleError = () => {
- setDidError(true)
- }
-
- const { src, alt, style, className, ...rest } = props
-
- return didError ? (
-
-
-
-
-
- ) : (
-
- )
-}
diff --git a/web/src/components/ui/accordion.tsx b/web/src/components/ui/accordion.tsx
deleted file mode 100644
index aa2c37b252a6f4fa36c608f91e09f10740bdbfaa..0000000000000000000000000000000000000000
--- a/web/src/components/ui/accordion.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as AccordionPrimitive from "@radix-ui/react-accordion@1.2.3";
-import { ChevronDownIcon } from "lucide-react@0.487.0";
-
-import { cn } from "./utils";
-
-function Accordion({
- ...props
-}: React.ComponentProps) {
- return ;
-}
-
-function AccordionItem({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AccordionTrigger({
- className,
- children,
- ...props
-}: React.ComponentProps) {
- return (
-
- svg]:rotate-180",
- className,
- )}
- {...props}
- >
- {children}
-
-
-
- );
-}
-
-function AccordionContent({
- className,
- children,
- ...props
-}: React.ComponentProps) {
- return (
-
- {children}
-
- );
-}
-
-export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/web/src/components/ui/alert-dialog.tsx b/web/src/components/ui/alert-dialog.tsx
deleted file mode 100644
index 68f3605cf204b73a0fc98b493900a9569e98c684..0000000000000000000000000000000000000000
--- a/web/src/components/ui/alert-dialog.tsx
+++ /dev/null
@@ -1,157 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog@1.1.6";
-
-import { cn } from "./utils";
-import { buttonVariants } from "./button";
-
-function AlertDialog({
- ...props
-}: React.ComponentProps) {
- return ;
-}
-
-function AlertDialogTrigger({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogPortal({
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogOverlay({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogContent({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
-
-
-
- );
-}
-
-function AlertDialogHeader({
- className,
- ...props
-}: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function AlertDialogFooter({
- className,
- ...props
-}: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function AlertDialogTitle({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogDescription({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogAction({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AlertDialogCancel({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-export {
- AlertDialog,
- AlertDialogPortal,
- AlertDialogOverlay,
- AlertDialogTrigger,
- AlertDialogContent,
- AlertDialogHeader,
- AlertDialogFooter,
- AlertDialogTitle,
- AlertDialogDescription,
- AlertDialogAction,
- AlertDialogCancel,
-};
diff --git a/web/src/components/ui/alert.tsx b/web/src/components/ui/alert.tsx
deleted file mode 100644
index 856b94db245b98afb9b458567b8f050804179f76..0000000000000000000000000000000000000000
--- a/web/src/components/ui/alert.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import * as React from "react";
-import { cva, type VariantProps } from "class-variance-authority@0.7.1";
-
-import { cn } from "./utils";
-
-const alertVariants = cva(
- "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
- {
- variants: {
- variant: {
- default: "bg-card text-card-foreground",
- destructive:
- "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-function Alert({
- className,
- variant,
- ...props
-}: React.ComponentProps<"div"> & VariantProps) {
- return (
-
- );
-}
-
-function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function AlertDescription({
- className,
- ...props
-}: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-export { Alert, AlertTitle, AlertDescription };
diff --git a/web/src/components/ui/aspect-ratio.tsx b/web/src/components/ui/aspect-ratio.tsx
deleted file mode 100644
index 2a2f462e2485fed8eed98afc7ea1377d6c5014d8..0000000000000000000000000000000000000000
--- a/web/src/components/ui/aspect-ratio.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-"use client";
-
-import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio@1.1.2";
-
-function AspectRatio({
- ...props
-}: React.ComponentProps) {
- return ;
-}
-
-export { AspectRatio };
diff --git a/web/src/components/ui/avatar.tsx b/web/src/components/ui/avatar.tsx
deleted file mode 100644
index 589b166583f59799d32a83b2e9eec64c542839df..0000000000000000000000000000000000000000
--- a/web/src/components/ui/avatar.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as AvatarPrimitive from "@radix-ui/react-avatar@1.1.3";
-
-import { cn } from "./utils";
-
-function Avatar({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AvatarImage({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-function AvatarFallback({
- className,
- ...props
-}: React.ComponentProps) {
- return (
-
- );
-}
-
-export { Avatar, AvatarImage, AvatarFallback };
diff --git a/web/src/components/ui/badge.tsx b/web/src/components/ui/badge.tsx
deleted file mode 100644
index 3f8eff87fb40c75325cf0b27aa175d3bc1e8a27f..0000000000000000000000000000000000000000
--- a/web/src/components/ui/badge.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot@1.1.2";
-import { cva, type VariantProps } from "class-variance-authority@0.7.1";
-
-import { cn } from "./utils";
-
-const badgeVariants = cva(
- "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
- {
- variants: {
- variant: {
- default:
- "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
- secondary:
- "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
- destructive:
- "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline:
- "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
- },
- },
- defaultVariants: {
- variant: "default",
- },
- },
-);
-
-function Badge({
- className,
- variant,
- asChild = false,
- ...props
-}: React.ComponentProps<"span"> &
- VariantProps & { asChild?: boolean }) {
- const Comp = asChild ? Slot : "span";
-
- return (
-
- );
-}
-
-export { Badge, badgeVariants };
diff --git a/web/src/components/ui/breadcrumb.tsx b/web/src/components/ui/breadcrumb.tsx
deleted file mode 100644
index d2adf987870332563506c7aa93346f0c44d958c9..0000000000000000000000000000000000000000
--- a/web/src/components/ui/breadcrumb.tsx
+++ /dev/null
@@ -1,109 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot@1.1.2";
-import { ChevronRight, MoreHorizontal } from "lucide-react@0.487.0";
-
-import { cn } from "./utils";
-
-function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
- return ;
-}
-
-function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
- return (
-
- );
-}
-
-function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
- return (
-
- );
-}
-
-function BreadcrumbLink({
- asChild,
- className,
- ...props
-}: React.ComponentProps<"a"> & {
- asChild?: boolean;
-}) {
- const Comp = asChild ? Slot : "a";
-
- return (
-
- );
-}
-
-function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
- return (
-
- );
-}
-
-function BreadcrumbSeparator({
- children,
- className,
- ...props
-}: React.ComponentProps<"li">) {
- return (
- svg]:size-3.5", className)}
- {...props}
- >
- {children ?? }
-
- );
-}
-
-function BreadcrumbEllipsis({
- className,
- ...props
-}: React.ComponentProps<"span">) {
- return (
-
-
- More
-
- );
-}
-
-export {
- Breadcrumb,
- BreadcrumbList,
- BreadcrumbItem,
- BreadcrumbLink,
- BreadcrumbPage,
- BreadcrumbSeparator,
- BreadcrumbEllipsis,
-};
diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx
deleted file mode 100644
index 16a719ecce48f53bebeb90bc44378e95111b0105..0000000000000000000000000000000000000000
--- a/web/src/components/ui/button.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import * as React from "react";
-import { Slot } from "@radix-ui/react-slot@1.1.2";
-import { cva, type VariantProps } from "class-variance-authority@0.7.1";
-
-import { cn } from "./utils";
-
-const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
- {
- variants: {
- variant: {
- default: "bg-primary text-primary-foreground hover:bg-primary/90",
- destructive:
- "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
- outline:
- "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
- secondary:
- "bg-secondary text-secondary-foreground hover:bg-secondary/80",
- ghost:
- "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
- link: "text-primary underline-offset-4 hover:underline",
- },
- size: {
- default: "h-9 px-4 py-2 has-[>svg]:px-3",
- sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
- lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
- icon: "size-9 rounded-md",
- },
- },
- defaultVariants: {
- variant: "default",
- size: "default",
- },
- },
-);
-
-const Button = React.forwardRef<
- HTMLButtonElement,
- React.ComponentProps<"button"> &
- VariantProps & {
- asChild?: boolean;
- }
->(({ className, variant, size, asChild = false, ...props }, ref) => {
- const Comp = asChild ? Slot : "button";
-
- return (
-
- );
-});
-
-Button.displayName = "Button";
-
-export { Button, buttonVariants };
\ No newline at end of file
diff --git a/web/src/components/ui/calendar.tsx b/web/src/components/ui/calendar.tsx
deleted file mode 100644
index b30236faaeb97276eda9777d5da8bfcc7a3e1cea..0000000000000000000000000000000000000000
--- a/web/src/components/ui/calendar.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-"use client";
-
-import * as React from "react";
-import { ChevronLeft, ChevronRight } from "lucide-react@0.487.0";
-import { DayPicker } from "react-day-picker@8.10.1";
-
-import { cn } from "./utils";
-import { buttonVariants } from "./button";
-
-function Calendar({
- className,
- classNames,
- showOutsideDays = true,
- ...props
-}: React.ComponentProps) {
- return (
- .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
- : "[&:has([aria-selected])]:rounded-md",
- ),
- day: cn(
- buttonVariants({ variant: "ghost" }),
- "size-8 p-0 font-normal aria-selected:opacity-100",
- ),
- day_range_start:
- "day-range-start aria-selected:bg-primary aria-selected:text-primary-foreground",
- day_range_end:
- "day-range-end aria-selected:bg-primary aria-selected:text-primary-foreground",
- day_selected:
- "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
- day_today: "bg-accent text-accent-foreground",
- day_outside:
- "day-outside text-muted-foreground aria-selected:text-muted-foreground",
- day_disabled: "text-muted-foreground opacity-50",
- day_range_middle:
- "aria-selected:bg-accent aria-selected:text-accent-foreground",
- day_hidden: "invisible",
- ...classNames,
- }}
- components={{
- IconLeft: ({ className, ...props }) => (
-
- ),
- IconRight: ({ className, ...props }) => (
-
- ),
- }}
- {...props}
- />
- );
-}
-
-export { Calendar };
diff --git a/web/src/components/ui/card.tsx b/web/src/components/ui/card.tsx
deleted file mode 100644
index 5f9d58a566782b3bd04c13e9e4aa31eca7d6e9f7..0000000000000000000000000000000000000000
--- a/web/src/components/ui/card.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import * as React from "react";
-
-import { cn } from "./utils";
-
-function Card({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardAction({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardContent({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
- return (
-
- );
-}
-
-export {
- Card,
- CardHeader,
- CardFooter,
- CardTitle,
- CardAction,
- CardDescription,
- CardContent,
-};
diff --git a/web/src/components/ui/carousel.tsx b/web/src/components/ui/carousel.tsx
deleted file mode 100644
index 2752b36c7a87383674ab853aa489083c21a8d5d3..0000000000000000000000000000000000000000
--- a/web/src/components/ui/carousel.tsx
+++ /dev/null
@@ -1,241 +0,0 @@
-"use client";
-
-import * as React from "react";
-import useEmblaCarousel, {
- type UseEmblaCarouselType,
-} from "embla-carousel-react@8.6.0";
-import { ArrowLeft, ArrowRight } from "lucide-react@0.487.0";
-
-import { cn } from "./utils";
-import { Button } from "./button";
-
-type CarouselApi = UseEmblaCarouselType[1];
-type UseCarouselParameters = Parameters;
-type CarouselOptions = UseCarouselParameters[0];
-type CarouselPlugin = UseCarouselParameters[1];
-
-type CarouselProps = {
- opts?: CarouselOptions;
- plugins?: CarouselPlugin;
- orientation?: "horizontal" | "vertical";
- setApi?: (api: CarouselApi) => void;
-};
-
-type CarouselContextProps = {
- carouselRef: ReturnType[0];
- api: ReturnType[1];
- scrollPrev: () => void;
- scrollNext: () => void;
- canScrollPrev: boolean;
- canScrollNext: boolean;
-} & CarouselProps;
-
-const CarouselContext = React.createContext(null);
-
-function useCarousel() {
- const context = React.useContext(CarouselContext);
-
- if (!context) {
- throw new Error("useCarousel must be used within a ");
- }
-
- return context;
-}
-
-function Carousel({
- orientation = "horizontal",
- opts,
- setApi,
- plugins,
- className,
- children,
- ...props
-}: React.ComponentProps<"div"> & CarouselProps) {
- const [carouselRef, api] = useEmblaCarousel(
- {
- ...opts,
- axis: orientation === "horizontal" ? "x" : "y",
- },
- plugins,
- );
- const [canScrollPrev, setCanScrollPrev] = React.useState(false);
- const [canScrollNext, setCanScrollNext] = React.useState(false);
-
- const onSelect = React.useCallback((api: CarouselApi) => {
- if (!api) return;
- setCanScrollPrev(api.canScrollPrev());
- setCanScrollNext(api.canScrollNext());
- }, []);
-
- const scrollPrev = React.useCallback(() => {
- api?.scrollPrev();
- }, [api]);
-
- const scrollNext = React.useCallback(() => {
- api?.scrollNext();
- }, [api]);
-
- const handleKeyDown = React.useCallback(
- (event: React.KeyboardEvent) => {
- if (event.key === "ArrowLeft") {
- event.preventDefault();
- scrollPrev();
- } else if (event.key === "ArrowRight") {
- event.preventDefault();
- scrollNext();
- }
- },
- [scrollPrev, scrollNext],
- );
-
- React.useEffect(() => {
- if (!api || !setApi) return;
- setApi(api);
- }, [api, setApi]);
-
- React.useEffect(() => {
- if (!api) return;
- onSelect(api);
- api.on("reInit", onSelect);
- api.on("select", onSelect);
-
- return () => {
- api?.off("select", onSelect);
- };
- }, [api, onSelect]);
-
- return (
-
-
- {children}
-
-
- );
-}
-
-function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
- const { carouselRef, orientation } = useCarousel();
-
- return (
-
- );
-}
-
-function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
- const { orientation } = useCarousel();
-
- return (
-
- );
-}
-
-function CarouselPrevious({
- className,
- variant = "outline",
- size = "icon",
- ...props
-}: React.ComponentProps) {
- const { orientation, scrollPrev, canScrollPrev } = useCarousel();
-
- return (
-
-
- Previous slide
-
- );
-}
-
-function CarouselNext({
- className,
- variant = "outline",
- size = "icon",
- ...props
-}: React.ComponentProps) {
- const { orientation, scrollNext, canScrollNext } = useCarousel();
-
- return (
-
-
- Next slide
-
- );
-}
-
-export {
- type CarouselApi,
- Carousel,
- CarouselContent,
- CarouselItem,
- CarouselPrevious,
- CarouselNext,
-};
diff --git a/web/src/components/ui/chart.tsx b/web/src/components/ui/chart.tsx
deleted file mode 100644
index d558e197cddf5a44baceec57a935e9cc1ba8980f..0000000000000000000000000000000000000000
--- a/web/src/components/ui/chart.tsx
+++ /dev/null
@@ -1,353 +0,0 @@
-"use client";
-
-import * as React from "react";
-import * as RechartsPrimitive from "recharts@2.15.2";
-
-import { cn } from "./utils";
-
-// Format: { THEME_NAME: CSS_SELECTOR }
-const THEMES = { light: "", dark: ".dark" } as const;
-
-export type ChartConfig = {
- [k in string]: {
- label?: React.ReactNode;
- icon?: React.ComponentType;
- } & (
- | { color?: string; theme?: never }
- | { color?: never; theme: Record }
- );
-};
-
-type ChartContextProps = {
- config: ChartConfig;
-};
-
-const ChartContext = React.createContext(null);
-
-function useChart() {
- const context = React.useContext(ChartContext);
-
- if (!context) {
- throw new Error("useChart must be used within a ");
- }
-
- return context;
-}
-
-function ChartContainer({
- id,
- className,
- children,
- config,
- ...props
-}: React.ComponentProps<"div"> & {
- config: ChartConfig;
- children: React.ComponentProps<
- typeof RechartsPrimitive.ResponsiveContainer
- >["children"];
-}) {
- const uniqueId = React.useId();
- const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`;
-
- return (
-
-
-
-
- {children}
-
-
-
- );
-}
-
-const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
- const colorConfig = Object.entries(config).filter(
- ([, config]) => config.theme || config.color,
- );
-
- if (!colorConfig.length) {
- return null;
- }
-
- return (
-