From abe4041cd1d40d4e49f14e0cbedb5c068209560f Mon Sep 17 00:00:00 2001 From: marco370 <48531002-marco370@users.noreply.replit.com> Date: Sat, 11 Oct 2025 09:36:55 +0000 Subject: [PATCH] Add basic UI components and structure for the application Initial commit adds core UI components, including layout elements, form controls, and navigation elements, along with the main application structure and routing. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 99f0fce6-9386-489a-9632-1d81223cab44 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/6d543d2c-20b9-4ea6-93fe-70fe9b1d9f80/99f0fce6-9386-489a-9632-1d81223cab44/nGJAldO --- .gitignore | 6 + .replit | 50 + client/index.html | 26 + client/src/App.tsx | 84 + client/src/components/app-sidebar.tsx | 134 + client/src/components/kpi-card.tsx | 37 + client/src/components/status-badge.tsx | 31 + client/src/components/theme-provider.tsx | 73 + client/src/components/theme-toggle.tsx | 20 + client/src/components/ui/accordion.tsx | 56 + client/src/components/ui/alert-dialog.tsx | 139 + client/src/components/ui/alert.tsx | 59 + client/src/components/ui/aspect-ratio.tsx | 5 + client/src/components/ui/avatar.tsx | 51 + client/src/components/ui/badge.tsx | 38 + client/src/components/ui/breadcrumb.tsx | 115 + client/src/components/ui/button.tsx | 62 + client/src/components/ui/calendar.tsx | 68 + client/src/components/ui/card.tsx | 85 + client/src/components/ui/carousel.tsx | 260 + client/src/components/ui/chart.tsx | 365 + client/src/components/ui/checkbox.tsx | 28 + client/src/components/ui/collapsible.tsx | 11 + client/src/components/ui/command.tsx | 151 + client/src/components/ui/context-menu.tsx | 198 + client/src/components/ui/dialog.tsx | 122 + client/src/components/ui/drawer.tsx | 118 + client/src/components/ui/dropdown-menu.tsx | 198 + client/src/components/ui/form.tsx | 178 + client/src/components/ui/hover-card.tsx | 29 + client/src/components/ui/input-otp.tsx | 69 + client/src/components/ui/input.tsx | 23 + client/src/components/ui/label.tsx | 24 + client/src/components/ui/menubar.tsx | 256 + client/src/components/ui/navigation-menu.tsx | 128 + client/src/components/ui/pagination.tsx | 117 + client/src/components/ui/popover.tsx | 29 + client/src/components/ui/progress.tsx | 28 + client/src/components/ui/radio-group.tsx | 42 + client/src/components/ui/resizable.tsx | 45 + client/src/components/ui/scroll-area.tsx | 46 + client/src/components/ui/select.tsx | 160 + client/src/components/ui/separator.tsx | 29 + client/src/components/ui/sheet.tsx | 140 + client/src/components/ui/sidebar.tsx | 727 ++ client/src/components/ui/skeleton.tsx | 15 + client/src/components/ui/slider.tsx | 26 + client/src/components/ui/switch.tsx | 27 + client/src/components/ui/table.tsx | 117 + client/src/components/ui/tabs.tsx | 53 + client/src/components/ui/textarea.tsx | 22 + client/src/components/ui/toast.tsx | 127 + client/src/components/ui/toaster.tsx | 33 + client/src/components/ui/toggle-group.tsx | 61 + client/src/components/ui/toggle.tsx | 43 + client/src/components/ui/tooltip.tsx | 30 + client/src/hooks/use-mobile.tsx | 19 + client/src/hooks/use-toast.ts | 191 + client/src/hooks/useAuth.ts | 15 + client/src/index.css | 355 + client/src/lib/authUtils.ts | 3 + client/src/lib/queryClient.ts | 57 + client/src/lib/utils.ts | 6 + client/src/main.tsx | 5 + client/src/pages/dashboard.tsx | 192 + client/src/pages/guards.tsx | 293 + client/src/pages/landing.tsx | 117 + client/src/pages/not-found.tsx | 21 + client/src/pages/notifications.tsx | 128 + client/src/pages/reports.tsx | 227 + client/src/pages/shifts.tsx | 274 + client/src/pages/sites.tsx | 286 + components.json | 20 + design_guidelines.md | 152 + drizzle.config.ts | 14 + package-lock.json | 8457 ++++++++++++++++++ package.json | 109 + postcss.config.js | 6 + replit.md | 140 + server/db.ts | 16 + server/index.ts | 71 + server/replitAuth.ts | 158 + server/routes.ts | 253 + server/storage.ts | 235 + server/vite.ts | 85 + shared/schema.ts | 315 + tailwind.config.ts | 107 + tsconfig.json | 23 + vite.config.ts | 40 + 89 files changed, 17604 insertions(+) create mode 100644 .gitignore create mode 100644 .replit create mode 100644 client/index.html create mode 100644 client/src/App.tsx create mode 100644 client/src/components/app-sidebar.tsx create mode 100644 client/src/components/kpi-card.tsx create mode 100644 client/src/components/status-badge.tsx create mode 100644 client/src/components/theme-provider.tsx create mode 100644 client/src/components/theme-toggle.tsx create mode 100644 client/src/components/ui/accordion.tsx create mode 100644 client/src/components/ui/alert-dialog.tsx create mode 100644 client/src/components/ui/alert.tsx create mode 100644 client/src/components/ui/aspect-ratio.tsx create mode 100644 client/src/components/ui/avatar.tsx create mode 100644 client/src/components/ui/badge.tsx create mode 100644 client/src/components/ui/breadcrumb.tsx create mode 100644 client/src/components/ui/button.tsx create mode 100644 client/src/components/ui/calendar.tsx create mode 100644 client/src/components/ui/card.tsx create mode 100644 client/src/components/ui/carousel.tsx create mode 100644 client/src/components/ui/chart.tsx create mode 100644 client/src/components/ui/checkbox.tsx create mode 100644 client/src/components/ui/collapsible.tsx create mode 100644 client/src/components/ui/command.tsx create mode 100644 client/src/components/ui/context-menu.tsx create mode 100644 client/src/components/ui/dialog.tsx create mode 100644 client/src/components/ui/drawer.tsx create mode 100644 client/src/components/ui/dropdown-menu.tsx create mode 100644 client/src/components/ui/form.tsx create mode 100644 client/src/components/ui/hover-card.tsx create mode 100644 client/src/components/ui/input-otp.tsx create mode 100644 client/src/components/ui/input.tsx create mode 100644 client/src/components/ui/label.tsx create mode 100644 client/src/components/ui/menubar.tsx create mode 100644 client/src/components/ui/navigation-menu.tsx create mode 100644 client/src/components/ui/pagination.tsx create mode 100644 client/src/components/ui/popover.tsx create mode 100644 client/src/components/ui/progress.tsx create mode 100644 client/src/components/ui/radio-group.tsx create mode 100644 client/src/components/ui/resizable.tsx create mode 100644 client/src/components/ui/scroll-area.tsx create mode 100644 client/src/components/ui/select.tsx create mode 100644 client/src/components/ui/separator.tsx create mode 100644 client/src/components/ui/sheet.tsx create mode 100644 client/src/components/ui/sidebar.tsx create mode 100644 client/src/components/ui/skeleton.tsx create mode 100644 client/src/components/ui/slider.tsx create mode 100644 client/src/components/ui/switch.tsx create mode 100644 client/src/components/ui/table.tsx create mode 100644 client/src/components/ui/tabs.tsx create mode 100644 client/src/components/ui/textarea.tsx create mode 100644 client/src/components/ui/toast.tsx create mode 100644 client/src/components/ui/toaster.tsx create mode 100644 client/src/components/ui/toggle-group.tsx create mode 100644 client/src/components/ui/toggle.tsx create mode 100644 client/src/components/ui/tooltip.tsx create mode 100644 client/src/hooks/use-mobile.tsx create mode 100644 client/src/hooks/use-toast.ts create mode 100644 client/src/hooks/useAuth.ts create mode 100644 client/src/index.css create mode 100644 client/src/lib/authUtils.ts create mode 100644 client/src/lib/queryClient.ts create mode 100644 client/src/lib/utils.ts create mode 100644 client/src/main.tsx create mode 100644 client/src/pages/dashboard.tsx create mode 100644 client/src/pages/guards.tsx create mode 100644 client/src/pages/landing.tsx create mode 100644 client/src/pages/not-found.tsx create mode 100644 client/src/pages/notifications.tsx create mode 100644 client/src/pages/reports.tsx create mode 100644 client/src/pages/shifts.tsx create mode 100644 client/src/pages/sites.tsx create mode 100644 components.json create mode 100644 design_guidelines.md create mode 100644 drizzle.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 replit.md create mode 100644 server/db.ts create mode 100644 server/index.ts create mode 100644 server/replitAuth.ts create mode 100644 server/routes.ts create mode 100644 server/storage.ts create mode 100644 server/vite.ts create mode 100644 shared/schema.ts create mode 100644 tailwind.config.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9ba7f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +dist +.DS_Store +server/public +vite.config.ts.* +*.tar.gz \ No newline at end of file diff --git a/.replit b/.replit new file mode 100644 index 0000000..03709f0 --- /dev/null +++ b/.replit @@ -0,0 +1,50 @@ +modules = ["nodejs-20", "web", "postgresql-16"] +run = "npm run dev" +hidden = [".config", ".git", "generated-icon.png", "node_modules", "dist"] + +[nix] +channel = "stable-24_05" + +[deployment] +deploymentTarget = "autoscale" +build = ["npm", "run", "build"] +run = ["npm", "run", "start"] + +[[ports]] +localPort = 5000 +externalPort = 80 + +[[ports]] +localPort = 33035 +externalPort = 3001 + +[[ports]] +localPort = 41343 +externalPort = 3000 + +[env] +PORT = "5000" + +[workflows] +runButton = "Project" + +[[workflows.workflow]] +name = "Project" +mode = "parallel" +author = "agent" + +[[workflows.workflow.tasks]] +task = "workflow.run" +args = "Start application" + +[[workflows.workflow]] +name = "Start application" +author = "agent" + +[[workflows.workflow.tasks]] +task = "shell.exec" +args = "npm run dev" +waitForPort = 5000 + +[agent] +integrations = ["javascript_database:1.0.0", "javascript_log_in_with_replit:1.0.0"] diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..aae19fb --- /dev/null +++ b/client/index.html @@ -0,0 +1,26 @@ + + + + + + + + VigilanzaTurni - Sistema Gestione Turni Professionale per Istituti di Vigilanza + + + + + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx new file mode 100644 index 0000000..2c81685 --- /dev/null +++ b/client/src/App.tsx @@ -0,0 +1,84 @@ +import { Switch, Route } from "wouter"; +import { queryClient } from "./lib/queryClient"; +import { QueryClientProvider } from "@tanstack/react-query"; +import { Toaster } from "@/components/ui/toaster"; +import { TooltipProvider } from "@/components/ui/tooltip"; +import { ThemeProvider } from "@/components/theme-provider"; +import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; +import { AppSidebar } from "@/components/app-sidebar"; +import { useAuth } from "@/hooks/useAuth"; +import NotFound from "@/pages/not-found"; +import Landing from "@/pages/landing"; +import Dashboard from "@/pages/dashboard"; +import Guards from "@/pages/guards"; +import Sites from "@/pages/sites"; +import Shifts from "@/pages/shifts"; +import Reports from "@/pages/reports"; +import Notifications from "@/pages/notifications"; + +function Router() { + const { isAuthenticated, isLoading } = useAuth(); + + return ( + + {isLoading || !isAuthenticated ? ( + + ) : ( + <> + + + + + + + + )} + + + ); +} + +function AppContent() { + const { isAuthenticated, isLoading } = useAuth(); + + // Sidebar style configuration for operational dashboard + const sidebarStyle = { + "--sidebar-width": "16rem", + "--sidebar-width-icon": "3rem", + } as React.CSSProperties; + + return ( + + + {!isLoading && isAuthenticated ? ( + +
+ +
+
+ +
+
+ +
+
+
+
+ ) : ( + + )} + +
+
+ ); +} + +function App() { + return ( + + + + ); +} + +export default App; diff --git a/client/src/components/app-sidebar.tsx b/client/src/components/app-sidebar.tsx new file mode 100644 index 0000000..bc51cc8 --- /dev/null +++ b/client/src/components/app-sidebar.tsx @@ -0,0 +1,134 @@ +import { + Calendar, + Shield, + MapPin, + Users, + BarChart3, + Bell, + Settings, +} from "lucide-react"; +import { Link, useLocation } from "wouter"; +import { + Sidebar, + SidebarContent, + SidebarGroup, + SidebarGroupContent, + SidebarGroupLabel, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + SidebarHeader, + SidebarFooter, +} from "@/components/ui/sidebar"; +import { useAuth } from "@/hooks/useAuth"; +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Button } from "@/components/ui/button"; +import { ThemeToggle } from "@/components/theme-toggle"; + +const menuItems = [ + { + title: "Dashboard", + url: "/", + icon: Shield, + roles: ["admin", "coordinator", "guard", "client"], + }, + { + title: "Turni", + url: "/shifts", + icon: Calendar, + roles: ["admin", "coordinator", "guard"], + }, + { + title: "Guardie", + url: "/guards", + icon: Users, + roles: ["admin", "coordinator"], + }, + { + title: "Siti", + url: "/sites", + icon: MapPin, + roles: ["admin", "coordinator", "client"], + }, + { + title: "Report", + url: "/reports", + icon: BarChart3, + roles: ["admin", "coordinator", "client"], + }, + { + title: "Notifiche", + url: "/notifications", + icon: Bell, + roles: ["admin", "coordinator", "guard"], + }, +]; + +export function AppSidebar() { + const { user } = useAuth(); + const [location] = useLocation(); + + const filteredItems = menuItems.filter( + (item) => user && item.roles.includes(user.role) + ); + + return ( + + +
+ +
+

VigilanzaTurni

+

Sistema Gestione

+
+
+
+ + + + Menu Principale + + + {filteredItems.map((item) => ( + + + + + {item.title} + + + + ))} + + + + + + +
+
+ + + + {user?.firstName?.[0]}{user?.lastName?.[0]} + + +
+

+ {user?.firstName} {user?.lastName} +

+

+ {user?.role} +

+
+
+ +
+
+
+ ); +} diff --git a/client/src/components/kpi-card.tsx b/client/src/components/kpi-card.tsx new file mode 100644 index 0000000..2cbccc3 --- /dev/null +++ b/client/src/components/kpi-card.tsx @@ -0,0 +1,37 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { LucideIcon } from "lucide-react"; +import { cn } from "@/lib/utils"; + +interface KPICardProps extends React.HTMLAttributes { + title: string; + value: string | number; + icon: LucideIcon; + trend?: { + value: string; + isPositive: boolean; + }; +} + +export function KPICard({ title, value, icon: Icon, trend, className, ...props }: KPICardProps) { + return ( + + + + {title} + + + + +
{value}
+ {trend && ( +

+ {trend.value} +

+ )} +
+
+ ); +} diff --git a/client/src/components/status-badge.tsx b/client/src/components/status-badge.tsx new file mode 100644 index 0000000..9bba669 --- /dev/null +++ b/client/src/components/status-badge.tsx @@ -0,0 +1,31 @@ +import { Badge } from "@/components/ui/badge"; +import { cn } from "@/lib/utils"; + +interface StatusBadgeProps { + status: "active" | "inactive" | "late" | "emergency" | "pending" | "completed"; + children: React.ReactNode; + className?: string; +} + +export function StatusBadge({ status, children, className }: StatusBadgeProps) { + const statusColors = { + active: "bg-[hsl(140,60%,45%)] text-white border-[hsl(140,60%,35%)]", + inactive: "bg-muted text-muted-foreground border-muted-border", + late: "bg-[hsl(25,90%,55%)] text-white border-[hsl(25,90%,45%)]", + emergency: "bg-destructive text-destructive-foreground border-destructive-border", + pending: "bg-[hsl(45,90%,55%)] text-white border-[hsl(45,90%,45%)]", + completed: "bg-[hsl(140,60%,45%)] text-white border-[hsl(140,60%,35%)]", + }; + + return ( + + {children} + + ); +} diff --git a/client/src/components/theme-provider.tsx b/client/src/components/theme-provider.tsx new file mode 100644 index 0000000..74c81c4 --- /dev/null +++ b/client/src/components/theme-provider.tsx @@ -0,0 +1,73 @@ +import { createContext, useContext, useEffect, useState } from "react"; + +type Theme = "dark" | "light" | "system"; + +type ThemeProviderProps = { + children: React.ReactNode; + defaultTheme?: Theme; + storageKey?: string; +}; + +type ThemeProviderState = { + theme: Theme; + setTheme: (theme: Theme) => void; +}; + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +}; + +const ThemeProviderContext = createContext(initialState); + +export function ThemeProvider({ + children, + defaultTheme = "dark", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme + ); + + useEffect(() => { + const root = window.document.documentElement; + + root.classList.remove("light", "dark"); + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + + root.classList.add(systemTheme); + return; + } + + root.classList.add(theme); + }, [theme]); + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme); + setTheme(theme); + }, + }; + + return ( + + {children} + + ); +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext); + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider"); + + return context; +}; diff --git a/client/src/components/theme-toggle.tsx b/client/src/components/theme-toggle.tsx new file mode 100644 index 0000000..31a37ee --- /dev/null +++ b/client/src/components/theme-toggle.tsx @@ -0,0 +1,20 @@ +import { Moon, Sun } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { useTheme } from "@/components/theme-provider"; + +export function ThemeToggle() { + const { theme, setTheme } = useTheme(); + + return ( + + ); +} diff --git a/client/src/components/ui/accordion.tsx b/client/src/components/ui/accordion.tsx new file mode 100644 index 0000000..e6a723d --- /dev/null +++ b/client/src/components/ui/accordion.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/client/src/components/ui/alert-dialog.tsx b/client/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..8722561 --- /dev/null +++ b/client/src/components/ui/alert-dialog.tsx @@ -0,0 +1,139 @@ +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/client/src/components/ui/alert.tsx b/client/src/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/client/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/client/src/components/ui/aspect-ratio.tsx b/client/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..c4abbf3 --- /dev/null +++ b/client/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/client/src/components/ui/avatar.tsx b/client/src/components/ui/avatar.tsx new file mode 100644 index 0000000..fc7f964 --- /dev/null +++ b/client/src/components/ui/avatar.tsx @@ -0,0 +1,51 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/client/src/components/ui/badge.tsx b/client/src/components/ui/badge.tsx new file mode 100644 index 0000000..b59d7ad --- /dev/null +++ b/client/src/components/ui/badge.tsx @@ -0,0 +1,38 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + // Whitespace-nowrap: Badges should never wrap. + "whitespace-nowrap inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2" + + " hover-elevate " , + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow-xs", + secondary: "border-transparent bg-secondary text-secondary-foreground", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow-xs", + + outline: " border [border-color:var(--badge-outline)] shadow-xs", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ); +} + +export { Badge, badgeVariants } diff --git a/client/src/components/ui/breadcrumb.tsx b/client/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/client/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>