Frontend Components
This document provides comprehensive documentation for all components in the Frontend service, organized by domain and purpose.
Component Organization
graph TB
subgraph "components/"
subgraph "ui/"
UI1[button.tsx]
UI2[card.tsx]
UI3[dialog.tsx]
UI4[form.tsx]
UI5[input.tsx]
UI6[select.tsx]
UI7[table.tsx]
UI8[tabs.tsx]
UI9[...14 total]
end
subgraph "chat/"
C1[ChatInput.tsx]
C2[MessageBubble.tsx]
C3[MessageList.tsx]
C4[SessionSelector.tsx]
C5[TestAnswer.tsx]
C6[TestQuestion.tsx]
end
subgraph "dashboard/"
D1[DocumentManager.tsx]
D2[StatsOverview.tsx]
D3[StudentList.tsx]
D4[SubjectCard.tsx]
D5[...8 total]
end
subgraph "layout/"
L1[AppLayout.tsx]
L2[RequireAuth.tsx]
L3[RequireAdmin.tsx]
L4[RequireProfessor.tsx]
L5[RequireStudent.tsx]
L6[PublicRoute.tsx]
L7[DefaultRedirect.tsx]
end
end
UI Components (components/ui/)
These are base components following the shadcn/ui pattern, built on Radix UI primitives.
Button
Interactive button component with multiple variants.
import { Button } from "@/components/ui/button";
// Variants
<Button variant="default">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
// Sizes
<Button size="sm">Small</Button>
<Button size="default">Default</Button>
<Button size="lg">Large</Button>
<Button size="icon"><Icon /></Button>
// States
<Button disabled>Disabled</Button>
<Button asChild><Link to="/page">As Link</Link></Button>
Card
Content container with optional header and footer.
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card";
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description text</CardDescription>
</CardHeader>
<CardContent>
Main content goes here
</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
Dialog
Modal dialog for confirmations and forms.
import {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from "@/components/ui/dialog";
<Dialog>
<DialogTrigger asChild>
<Button>Open Dialog</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogDescription>Description text</DialogDescription>
</DialogHeader>
<div>Dialog content</div>
<DialogFooter>
<Button>Confirm</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Form
Integration with React Hook Form for form handling.
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from "@/components/ui/form";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
});
function MyForm() {
const form = useForm({
resolver: zodResolver(schema),
defaultValues: { email: "" },
});
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</form>
</Form>
);
}
Select
Dropdown selection component.
import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";
<Select value={value} onValueChange={setValue}>
<SelectTrigger>
<SelectValue placeholder="Select option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
</SelectContent>
</Select>
Table
Data table with headers and rows.
import { Table, TableHeader, TableBody, TableRow, TableHead, TableCell } from "@/components/ui/table";
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Email</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{users.map(user => (
<TableRow key={user.id}>
<TableCell>{user.name}</TableCell>
<TableCell>{user.email}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
Tabs
Tabbed navigation component.
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
<Tabs defaultValue="tab1">
<TabsList>
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
</TabsList>
<TabsContent value="tab1">Content 1</TabsContent>
<TabsContent value="tab2">Content 2</TabsContent>
</Tabs>
Complete UI Components List
| Component | File | Purpose |
|---|---|---|
Button | button.tsx | Interactive button |
Card | card.tsx | Content container |
Dialog | dialog.tsx | Modal dialogs |
AlertDialog | alert-dialog.tsx | Confirmation dialogs |
DropdownMenu | dropdown-menu.tsx | Dropdown menus |
Form | form.tsx | Form with validation |
Input | input.tsx | Text input |
Label | label.tsx | Form labels |
Select | select.tsx | Dropdown selection |
Sonner | sonner.tsx | Toast notifications |
Table | table.tsx | Data tables |
Tabs | tabs.tsx | Tabbed navigation |
Badge | badge.tsx | Status indicators |
Progress | progress.tsx | Progress bars |
Chat Components (components/chat/)
Components specific to the chat interface.
ChatInput
Text input with send button for composing messages.
graph LR
User[User Types] --> Textarea
Textarea --> |Enter/Click| SendButton
SendButton --> |onSend| Parent[Parent Handler]
Props: | Prop | Type | Description | |——|——|————-| | onSend | (message: string) => void | Callback when message sent | | isLoading | boolean | Disables input during API call | | disabled | boolean | Disables the input entirely | | placeholder | string | Input placeholder text |
Features:
- Auto-resizing textarea (max 150px height)
- Enter to send (Shift+Enter for newline)
- Loading state with spinner
- Disabled state support
<ChatInput
onSend={(msg) => sendMessage(msg)}
isLoading={isSending}
placeholder="Escribe tu mensaje..."
/>
MessageList
Scrollable container for displaying chat messages.
Props: | Prop | Type | Description | |——|——|————-| | messages | Message[] | Array of messages to display | | isLoading | boolean | Shows typing indicator |
Features:
- Auto-scroll to bottom on new messages
- Empty state with welcome message
- Animated typing indicator
- Smooth scroll behavior
<MessageList
messages={chatMessages}
isLoading={isWaitingForResponse}
/>
MessageBubble
Individual message display with user/assistant styling.
graph TB
subgraph "User Message"
UA[User Avatar]
UM[Message Content]
UT[Timestamp]
end
subgraph "Assistant Message"
BA[Bot Avatar]
BM[Markdown Content]
BT[Timestamp]
end
Props: | Prop | Type | Description | |——|——|————-| | message | Message | Message object with role and content |
Features:
- Different styling for user vs assistant
- Markdown rendering for assistant messages
- Code syntax highlighting via rehype-highlight
- Timestamp display
- Avatar icons (User/Bot)
<MessageBubble message= />
SessionSelector
Sidebar component for selecting and managing chat sessions.
Props: | Prop | Type | Description | |——|——|————-| | sessions | Session[] | Available sessions | | activeId | string \| null | Currently selected session | | onSelect | (id: string) => void | Selection callback | | onNew | () => void | Create new session | | onDelete | (id: string) => void | Delete session |
Features:
- List of sessions with titles
- Active session highlighting
- Create new session button
- Delete session with confirmation
TestQuestion / TestAnswer
Components for interactive test mode.
sequenceDiagram
participant TQ as TestQuestion
participant U as User
participant TA as TestAnswer
participant C as Chat Hook
TQ->>U: Display question
U->>TA: Select/type answer
TA->>C: resumeTest(answer)
C->>TQ: Next question or results
TestQuestion Props: | Prop | Type | Description | |——|——|————-| | question | string | Question text | | options | string[] | Multiple choice options (optional) | | questionNumber | number | Current question number | | totalQuestions | number | Total questions in test |
TestAnswer Props: | Prop | Type | Description | |——|——|————-| | onSubmit | (answer: string) => void | Answer submission callback | | isLoading | boolean | Submission in progress |
Dashboard Components (components/dashboard/)
Components for the professor dashboard interface.
DocumentManager
Upload and manage course documents.
graph TB
subgraph "DocumentManager"
TL[Type Selector]
UB[Upload Button]
DT[Documents Table]
DA[Delete Action]
end
TL --> |select type| UB
UB --> |file input| Upload[Upload Handler]
DT --> |delete| DA
DA --> |confirm| Delete[Delete Handler]
Props: | Prop | Type | Description | |——|——|————-| | documents | DocumentInfo[] | List of documents | | subject | string | Current subject name | | isLoading | boolean | Loading state | | isUploading | boolean | Upload in progress | | onUpload | (file, type, autoIndex) => Promise | Upload callback | | onDelete | (filePath: string) => Promise | Delete callback |
Document Types:
teoria- Theory materialsejercicios- Exercisesexamenes- Past examspracticas- Lab materialsotros- Other materials
StatsOverview
Dashboard statistics display with charts.
Props: | Prop | Type | Description | |——|——|————-| | stats | DashboardStats | Statistics data | | isLoading | boolean | Loading state |
Displays:
- Total students count
- Total sessions count
- Total documents count
- Sessions per subject breakdown
- Activity chart (last 7 days)
StudentList
Table of enrolled students.
Props: | Prop | Type | Description | |——|——|————-| | students | StudentInfo[] | List of students | | subject | string | Subject name | | isLoading | boolean | Loading state |
Features:
- Student username and email
- Enrollment status
- Link to student progress
SubjectCard
Card displaying subject information.
Props: | Prop | Type | Description | |——|——|————-| | subject | SubjectInfo | Subject data | | onSelect | () => void | Selection callback | | isSelected | boolean | Selection state |
Displays:
- Subject display name
- Student count
- Document count
- Selection highlight
StudentProgressTable
Detailed student progress view.
Props: | Prop | Type | Description | |——|——|————-| | progress | StudentProgress[] | Progress data | | subject | string | Subject name |
Displays:
- Interaction counts
- Difficulty distribution (basic/intermediate/advanced)
- Test scores
- Topic mastery levels
- Last activity date
Layout Components (components/layout/)
Components controlling application structure and access.
AppLayout
Main application shell with navigation sidebar.
graph TB
subgraph "AppLayout"
subgraph "Sidebar"
Logo[TFG Chatbot Logo]
Nav[Navigation Items]
User[User Info]
Logout[Logout Button]
end
subgraph "Main"
Outlet[Page Content]
end
end
Features:
- Responsive sidebar (collapsible on mobile)
- Role-based navigation filtering
- User info display
- Logout functionality
- Mobile hamburger menu
Navigation Items: | Route | Label | Icon | Roles | |——-|——-|——|——-| | /chat | Chat | MessageSquare | student | | /dashboard | Mis Clases | GraduationCap | professor, admin | | /admin | Admin | LayoutDashboard | admin | | /settings | Configuración | Settings | all |
RequireAuth
Route guard that requires authentication.
<Route element={<RequireAuth />}>
<Route path="/chat" element={<ChatPage />} />
</Route>
Behavior:
- Checks
isAuthenticatedfrom AuthContext - Redirects to
/loginif not authenticated - Renders
<Outlet />if authenticated
RequireStudent
Route guard requiring student role.
<Route element={<RequireStudent />}>
<Route path="/chat" element={<ChatPage />} />
</Route>
Behavior:
- Checks if user role is
student - Redirects to
/dashboardor/adminif professor/admin - Renders
<Outlet />if student
RequireProfessor
Route guard requiring professor or admin role.
<Route element={<RequireProfessor />}>
<Route path="/dashboard" element={<DashboardPage />} />
</Route>
Behavior:
- Checks if user role is
professororadmin - Redirects to
/chatif student - Renders
<Outlet />if authorized
RequireAdmin
Route guard requiring admin role.
<Route element={<RequireAdmin />}>
<Route path="/admin" element={<AdminDashboard />} />
</Route>
Behavior:
- Checks if user role is
admin - Redirects based on actual role if not admin
- Renders
<Outlet />if admin
PublicRoute
Prevents authenticated users from accessing public pages.
<Route element={<PublicRoute />}>
<Route path="/login" element={<LoginPage />} />
</Route>
Behavior:
- Checks if user is authenticated
- Redirects to appropriate page if already logged in
- Renders
<Outlet />if not authenticated
DefaultRedirect
Redirects users to their role-appropriate default page.
<Route path="/" element={<DefaultRedirect />} />
Redirect Logic: | Role | Redirect To | |——|————-| | student | /chat | | professor | /dashboard | | admin | /admin | | not logged in | /login |
Component Best Practices
1. Props Interface Definition
Always define TypeScript interfaces for props:
interface MyComponentProps {
title: string;
items: Item[];
onSelect?: (item: Item) => void;
isLoading?: boolean;
}
export function MyComponent({ title, items, onSelect, isLoading = false }: MyComponentProps) {
// ...
}
2. Composition Over Inheritance
Use composition patterns:
// Good - composable
<Card>
<CardHeader>
<CardTitle>{title}</CardTitle>
</CardHeader>
<CardContent>{children}</CardContent>
</Card>
// Avoid - monolithic
<ComplexCard title={title} content={children} hasHeader={true} />
3. Styling with cn()
Use the cn() utility for conditional classes:
import { cn } from "@/lib/utils";
<div className={cn(
"base-classes",
isActive && "active-classes",
variant === "primary" ? "primary-classes" : "secondary-classes"
)} />
4. Event Handler Naming
Use handle prefix for internal handlers, on prefix for props:
interface Props {
onSubmit: (data: Data) => void;
}
function Form({ onSubmit }: Props) {
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
onSubmit(formData);
};
return <form onSubmit={handleSubmit}>...</form>;
}
Related Documentation
- Architecture - Overall application structure
- State Management - Hooks and data fetching
- Routing - Route configuration