From 9e547e3b49362fee6ef949727e3b7937077c5e0d Mon Sep 17 00:00:00 2001 From: Astar95 <1427026946@qq.com> Date: Mon, 30 Mar 2026 12:03:56 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E6=8F=90=E4=BA=A4=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ATTRIBUTIONS.md | 3 + README.md | 11 + guidelines/Guidelines.md | 61 ++ index.html | 15 + package.json | 90 +++ postcss.config.mjs | 15 + src/app/App.tsx | 6 + src/app/Layout.tsx | 13 + src/app/components/Sidebar.tsx | 114 +++ .../components/figma/ImageWithFallback.tsx | 27 + src/app/components/ui/accordion.tsx | 66 ++ src/app/components/ui/alert-dialog.tsx | 157 ++++ src/app/components/ui/alert.tsx | 66 ++ src/app/components/ui/aspect-ratio.tsx | 11 + src/app/components/ui/avatar.tsx | 53 ++ src/app/components/ui/badge.tsx | 46 ++ src/app/components/ui/breadcrumb.tsx | 109 +++ src/app/components/ui/button.tsx | 58 ++ src/app/components/ui/calendar.tsx | 75 ++ src/app/components/ui/card.tsx | 92 +++ src/app/components/ui/carousel.tsx | 241 ++++++ src/app/components/ui/chart.tsx | 353 +++++++++ src/app/components/ui/checkbox.tsx | 32 + src/app/components/ui/collapsible.tsx | 33 + src/app/components/ui/command.tsx | 177 +++++ src/app/components/ui/context-menu.tsx | 252 ++++++ src/app/components/ui/dialog.tsx | 135 ++++ src/app/components/ui/drawer.tsx | 132 ++++ src/app/components/ui/dropdown-menu.tsx | 257 +++++++ src/app/components/ui/form.tsx | 168 ++++ src/app/components/ui/hover-card.tsx | 44 ++ src/app/components/ui/input-otp.tsx | 77 ++ src/app/components/ui/input.tsx | 21 + src/app/components/ui/label.tsx | 24 + src/app/components/ui/menubar.tsx | 276 +++++++ src/app/components/ui/navigation-menu.tsx | 168 ++++ src/app/components/ui/pagination.tsx | 127 +++ src/app/components/ui/popover.tsx | 48 ++ src/app/components/ui/progress.tsx | 31 + src/app/components/ui/radio-group.tsx | 45 ++ src/app/components/ui/resizable.tsx | 56 ++ src/app/components/ui/scroll-area.tsx | 58 ++ src/app/components/ui/select.tsx | 189 +++++ src/app/components/ui/separator.tsx | 28 + src/app/components/ui/sheet.tsx | 139 ++++ src/app/components/ui/sidebar.tsx | 726 ++++++++++++++++++ src/app/components/ui/skeleton.tsx | 13 + src/app/components/ui/slider.tsx | 63 ++ src/app/components/ui/sonner.tsx | 25 + src/app/components/ui/switch.tsx | 31 + src/app/components/ui/table.tsx | 116 +++ src/app/components/ui/tabs.tsx | 66 ++ src/app/components/ui/textarea.tsx | 18 + src/app/components/ui/toggle-group.tsx | 73 ++ src/app/components/ui/toggle.tsx | 47 ++ src/app/components/ui/tooltip.tsx | 61 ++ src/app/components/ui/use-mobile.ts | 21 + src/app/components/ui/utils.ts | 6 + src/app/pages/ActivationManagement.tsx | 492 ++++++++++++ src/app/pages/CalibrationRecords.tsx | 444 +++++++++++ src/app/pages/ConfigFileManagement.tsx | 391 ++++++++++ src/app/pages/Dashboard.tsx | 187 +++++ src/app/pages/DeviceDetail.tsx | 268 +++++++ src/app/pages/DeviceList.tsx | 281 +++++++ src/app/pages/DeviceModelManagement.tsx | 331 ++++++++ src/app/pages/DeviceRegistration.tsx | 337 ++++++++ src/app/pages/LicenseManagement.tsx | 249 ++++++ src/app/pages/ParameterConfiguration.tsx | 310 ++++++++ src/app/pages/PlaceholderPage.tsx | 10 + src/app/pages/RepairOrderDetail.tsx | 322 ++++++++ src/app/pages/ScrapManagement.tsx | 272 +++++++ src/app/routes.tsx | 40 + src/main.tsx | 7 + src/styles/fonts.css | 1 + src/styles/index.css | 3 + src/styles/tailwind.css | 4 + src/styles/theme.css | 190 +++++ vite.config.ts | 22 + 78 files changed, 9626 insertions(+) create mode 100644 ATTRIBUTIONS.md create mode 100644 README.md create mode 100644 guidelines/Guidelines.md create mode 100644 index.html create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 src/app/App.tsx create mode 100644 src/app/Layout.tsx create mode 100644 src/app/components/Sidebar.tsx create mode 100644 src/app/components/figma/ImageWithFallback.tsx create mode 100644 src/app/components/ui/accordion.tsx create mode 100644 src/app/components/ui/alert-dialog.tsx create mode 100644 src/app/components/ui/alert.tsx create mode 100644 src/app/components/ui/aspect-ratio.tsx create mode 100644 src/app/components/ui/avatar.tsx create mode 100644 src/app/components/ui/badge.tsx create mode 100644 src/app/components/ui/breadcrumb.tsx create mode 100644 src/app/components/ui/button.tsx create mode 100644 src/app/components/ui/calendar.tsx create mode 100644 src/app/components/ui/card.tsx create mode 100644 src/app/components/ui/carousel.tsx create mode 100644 src/app/components/ui/chart.tsx create mode 100644 src/app/components/ui/checkbox.tsx create mode 100644 src/app/components/ui/collapsible.tsx create mode 100644 src/app/components/ui/command.tsx create mode 100644 src/app/components/ui/context-menu.tsx create mode 100644 src/app/components/ui/dialog.tsx create mode 100644 src/app/components/ui/drawer.tsx create mode 100644 src/app/components/ui/dropdown-menu.tsx create mode 100644 src/app/components/ui/form.tsx create mode 100644 src/app/components/ui/hover-card.tsx create mode 100644 src/app/components/ui/input-otp.tsx create mode 100644 src/app/components/ui/input.tsx create mode 100644 src/app/components/ui/label.tsx create mode 100644 src/app/components/ui/menubar.tsx create mode 100644 src/app/components/ui/navigation-menu.tsx create mode 100644 src/app/components/ui/pagination.tsx create mode 100644 src/app/components/ui/popover.tsx create mode 100644 src/app/components/ui/progress.tsx create mode 100644 src/app/components/ui/radio-group.tsx create mode 100644 src/app/components/ui/resizable.tsx create mode 100644 src/app/components/ui/scroll-area.tsx create mode 100644 src/app/components/ui/select.tsx create mode 100644 src/app/components/ui/separator.tsx create mode 100644 src/app/components/ui/sheet.tsx create mode 100644 src/app/components/ui/sidebar.tsx create mode 100644 src/app/components/ui/skeleton.tsx create mode 100644 src/app/components/ui/slider.tsx create mode 100644 src/app/components/ui/sonner.tsx create mode 100644 src/app/components/ui/switch.tsx create mode 100644 src/app/components/ui/table.tsx create mode 100644 src/app/components/ui/tabs.tsx create mode 100644 src/app/components/ui/textarea.tsx create mode 100644 src/app/components/ui/toggle-group.tsx create mode 100644 src/app/components/ui/toggle.tsx create mode 100644 src/app/components/ui/tooltip.tsx create mode 100644 src/app/components/ui/use-mobile.ts create mode 100644 src/app/components/ui/utils.ts create mode 100644 src/app/pages/ActivationManagement.tsx create mode 100644 src/app/pages/CalibrationRecords.tsx create mode 100644 src/app/pages/ConfigFileManagement.tsx create mode 100644 src/app/pages/Dashboard.tsx create mode 100644 src/app/pages/DeviceDetail.tsx create mode 100644 src/app/pages/DeviceList.tsx create mode 100644 src/app/pages/DeviceModelManagement.tsx create mode 100644 src/app/pages/DeviceRegistration.tsx create mode 100644 src/app/pages/LicenseManagement.tsx create mode 100644 src/app/pages/ParameterConfiguration.tsx create mode 100644 src/app/pages/PlaceholderPage.tsx create mode 100644 src/app/pages/RepairOrderDetail.tsx create mode 100644 src/app/pages/ScrapManagement.tsx create mode 100644 src/app/routes.tsx create mode 100644 src/main.tsx create mode 100644 src/styles/fonts.css create mode 100644 src/styles/index.css create mode 100644 src/styles/tailwind.css create mode 100644 src/styles/theme.css create mode 100644 vite.config.ts diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md new file mode 100644 index 0000000..5df5c40 --- /dev/null +++ b/ATTRIBUTIONS.md @@ -0,0 +1,3 @@ +This Figma Make file includes components from [shadcn/ui](https://ui.shadcn.com/) used under [MIT license](https://github.com/shadcn-ui/ui/blob/main/LICENSE.md). + +This Figma Make file includes photos from [Unsplash](https://unsplash.com) used under [license](https://unsplash.com/license). diff --git a/README.md b/README.md new file mode 100644 index 0000000..17e454a --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ + + # Enterprise SaaS Dashboard Design + + This is a code bundle for Enterprise SaaS Dashboard Design. The original project is available at https://www.figma.com/design/SD8ReJmCwErmfmNVnHRlJb/Enterprise-SaaS-Dashboard-Design. + + ## Running the code + + Run `npm i` to install the dependencies. + + Run `npm run dev` to start the development server. + \ No newline at end of file diff --git a/guidelines/Guidelines.md b/guidelines/Guidelines.md new file mode 100644 index 0000000..110f117 --- /dev/null +++ b/guidelines/Guidelines.md @@ -0,0 +1,61 @@ +**Add your own guidelines here** + diff --git a/index.html b/index.html new file mode 100644 index 0000000..8f557dc --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + Enterprise SaaS Dashboard Design + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..fad7042 --- /dev/null +++ b/package.json @@ -0,0 +1,90 @@ +{ + "name": "@figma/my-make-file", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "build": "vite build", + "dev": "vite" + }, + "dependencies": { + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/icons-material": "7.3.5", + "@mui/material": "7.3.5", + "@popperjs/core": "2.11.8", + "@radix-ui/react-accordion": "1.2.3", + "@radix-ui/react-alert-dialog": "1.1.6", + "@radix-ui/react-aspect-ratio": "1.1.2", + "@radix-ui/react-avatar": "1.1.3", + "@radix-ui/react-checkbox": "1.1.4", + "@radix-ui/react-collapsible": "1.1.3", + "@radix-ui/react-context-menu": "2.2.6", + "@radix-ui/react-dialog": "1.1.6", + "@radix-ui/react-dropdown-menu": "2.1.6", + "@radix-ui/react-hover-card": "1.1.6", + "@radix-ui/react-label": "2.1.2", + "@radix-ui/react-menubar": "1.1.6", + "@radix-ui/react-navigation-menu": "1.2.5", + "@radix-ui/react-popover": "1.1.6", + "@radix-ui/react-progress": "1.1.2", + "@radix-ui/react-radio-group": "1.2.3", + "@radix-ui/react-scroll-area": "1.2.3", + "@radix-ui/react-select": "2.1.6", + "@radix-ui/react-separator": "1.1.2", + "@radix-ui/react-slider": "1.2.3", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-switch": "1.1.3", + "@radix-ui/react-tabs": "1.1.3", + "@radix-ui/react-toggle-group": "1.1.2", + "@radix-ui/react-toggle": "1.1.2", + "@radix-ui/react-tooltip": "1.1.8", + "canvas-confetti": "1.9.4", + "class-variance-authority": "0.7.1", + "clsx": "2.1.1", + "cmdk": "1.1.1", + "date-fns": "3.6.0", + "embla-carousel-react": "8.6.0", + "input-otp": "1.4.2", + "lucide-react": "0.487.0", + "motion": "12.23.24", + "next-themes": "0.4.6", + "react-day-picker": "8.10.1", + "react-dnd": "16.0.1", + "react-dnd-html5-backend": "16.0.1", + "react-hook-form": "7.55.0", + "react-popper": "2.3.0", + "react-resizable-panels": "2.1.7", + "react-responsive-masonry": "2.7.1", + "react-router": "7.13.0", + "react-slick": "0.31.0", + "recharts": "2.15.2", + "sonner": "2.0.3", + "tailwind-merge": "3.2.0", + "tw-animate-css": "1.3.8", + "vaul": "1.1.2" + }, + "devDependencies": { + "@tailwindcss/vite": "4.1.12", + "@vitejs/plugin-react": "4.7.0", + "tailwindcss": "4.1.12", + "vite": "6.3.5" + }, + "peerDependencies": { + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + }, + "pnpm": { + "overrides": { + "vite": "6.3.5" + } + } +} \ No newline at end of file diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..531dbec --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,15 @@ +/** + * PostCSS Configuration + * + * Tailwind CSS v4 (via @tailwindcss/vite) automatically sets up all required + * PostCSS plugins — you do NOT need to include `tailwindcss` or `autoprefixer` here. + * + * This file only exists for adding additional PostCSS plugins, if needed. + * For example: + * + * import postcssNested from 'postcss-nested' + * export default { plugins: [postcssNested()] } + * + * Otherwise, you can leave this file empty. + */ +export default {} diff --git a/src/app/App.tsx b/src/app/App.tsx new file mode 100644 index 0000000..af3badc --- /dev/null +++ b/src/app/App.tsx @@ -0,0 +1,6 @@ +import { RouterProvider } from "react-router"; +import { router } from "./routes"; + +export default function App() { + return ; +} diff --git a/src/app/Layout.tsx b/src/app/Layout.tsx new file mode 100644 index 0000000..279f72b --- /dev/null +++ b/src/app/Layout.tsx @@ -0,0 +1,13 @@ +import { Outlet } from "react-router"; +import { Sidebar } from "./components/Sidebar"; + +export function Layout() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/app/components/Sidebar.tsx b/src/app/components/Sidebar.tsx new file mode 100644 index 0000000..879e747 --- /dev/null +++ b/src/app/components/Sidebar.tsx @@ -0,0 +1,114 @@ +import { Link, useLocation } from "react-router"; +import { + LayoutDashboard, + Server, + Settings, + FileText, + Users, + BarChart3, + ClipboardList, + Database, + Key, + Smartphone, + Gauge, + Trash2, + FileCode, + Sliders, +} from "lucide-react"; + +const menuItems = [ + { path: "/", icon: LayoutDashboard, label: "首页" }, + { path: "/models", icon: Server, label: "型号管理" }, + { path: "/devices", icon: Database, label: "设备列表" }, + { + path: "/registration", + icon: ClipboardList, + label: "设备登记", + }, + { path: "/licenses", icon: Key, label: "授权管理" }, + { path: "/activation", icon: Smartphone, label: "激活管理" }, + { path: "/calibration", icon: Gauge, label: "校准管理" }, + { path: "/scrap", icon: Trash2, label: "报废管理" }, + { path: "/config-files", icon: FileCode, label: "配置文件" }, + { path: "/reports", icon: BarChart3, label: "数据报表" }, + { path: "/logs", icon: FileText, label: "操作日志" }, + { path: "/settings", icon: Settings, label: "系统设置" }, +]; + +export function Sidebar() { + const location = useLocation(); + + return ( + + ); +} \ No newline at end of file diff --git a/src/app/components/figma/ImageWithFallback.tsx b/src/app/components/figma/ImageWithFallback.tsx new file mode 100644 index 0000000..0e26139 --- /dev/null +++ b/src/app/components/figma/ImageWithFallback.tsx @@ -0,0 +1,27 @@ +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 ? ( +
+
+ Error loading image +
+
+ ) : ( + {alt} + ) +} diff --git a/src/app/components/ui/accordion.tsx b/src/app/components/ui/accordion.tsx new file mode 100644 index 0000000..bd6b1e3 --- /dev/null +++ b/src/app/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +"use client"; + +import * as React from "react"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDownIcon } from "lucide-react"; + +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/src/app/components/ui/alert-dialog.tsx b/src/app/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..875b8df --- /dev/null +++ b/src/app/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +"use client"; + +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +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/src/app/components/ui/alert.tsx b/src/app/components/ui/alert.tsx new file mode 100644 index 0000000..9c35976 --- /dev/null +++ b/src/app/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; + +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/src/app/components/ui/aspect-ratio.tsx b/src/app/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..c16d6bc --- /dev/null +++ b/src/app/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +"use client"; + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return ; +} + +export { AspectRatio }; diff --git a/src/app/components/ui/avatar.tsx b/src/app/components/ui/avatar.tsx new file mode 100644 index 0000000..c990451 --- /dev/null +++ b/src/app/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client"; + +import * as React from "react"; +import * as AvatarPrimitive from "@radix-ui/react-avatar"; + +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/src/app/components/ui/badge.tsx b/src/app/components/ui/badge.tsx new file mode 100644 index 0000000..2ccc2c4 --- /dev/null +++ b/src/app/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +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/src/app/components/ui/breadcrumb.tsx b/src/app/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..8f84d7e --- /dev/null +++ b/src/app/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { ChevronRight, MoreHorizontal } from "lucide-react"; + +import { cn } from "./utils"; + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return