From e60949ce4fae9f4a661309abc6fda539ae601885 Mon Sep 17 00:00:00 2001 From: Rami Bitar Date: Sat, 11 Apr 2026 14:03:15 -0400 Subject: [PATCH] Initial commit --- .env.local | 0 .gitignore | 4 + app/api/hello/route.ts | 5 + app/error.tsx | 23 + app/globals.css | 134 ++ app/layout.tsx | 26 + app/not-found.tsx | 17 + app/page.tsx | 11 + components.json | 22 + components/magicui/aurora-text.tsx | 40 + components/magicui/blur-fade.tsx | 63 + components/ui/accordion.tsx | 66 + components/ui/alert-dialog.tsx | 144 ++ components/ui/alert.tsx | 66 + components/ui/aspect-ratio.tsx | 11 + components/ui/avatar.tsx | 109 + components/ui/badge.tsx | 48 + components/ui/breadcrumb.tsx | 109 + components/ui/button-group.tsx | 83 + components/ui/button.tsx | 64 + components/ui/calendar.tsx | 220 ++ components/ui/card.tsx | 92 + components/ui/carousel.tsx | 241 ++ components/ui/chart.tsx | 357 +++ components/ui/checkbox.tsx | 32 + components/ui/collapsible.tsx | 33 + components/ui/combobox.tsx | 310 +++ components/ui/command.tsx | 184 ++ components/ui/context-menu.tsx | 252 ++ components/ui/dialog.tsx | 128 + components/ui/direction.tsx | 22 + components/ui/drawer.tsx | 135 ++ components/ui/dropdown-menu.tsx | 257 ++ components/ui/empty.tsx | 104 + components/ui/field.tsx | 248 ++ components/ui/form.tsx | 167 ++ components/ui/hover-card.tsx | 44 + components/ui/input-group.tsx | 170 ++ components/ui/input-otp.tsx | 77 + components/ui/input.tsx | 21 + components/ui/item.tsx | 193 ++ components/ui/kbd.tsx | 28 + components/ui/label.tsx | 24 + components/ui/loader.tsx | 96 + components/ui/menubar.tsx | 276 +++ components/ui/native-select.tsx | 53 + components/ui/navigation-menu.tsx | 168 ++ components/ui/pagination.tsx | 127 + components/ui/popover.tsx | 89 + components/ui/progress.tsx | 31 + components/ui/radio-group.tsx | 45 + components/ui/resizable.tsx | 56 + components/ui/scroll-area.tsx | 58 + components/ui/select.tsx | 190 ++ components/ui/separator.tsx | 28 + components/ui/sheet.tsx | 143 ++ components/ui/sidebar.tsx | 726 ++++++ components/ui/skeleton.tsx | 13 + components/ui/slider.tsx | 63 + components/ui/sonner.tsx | 40 + components/ui/spinner.tsx | 16 + components/ui/switch.tsx | 35 + components/ui/table.tsx | 116 + components/ui/tabs.tsx | 91 + components/ui/textarea.tsx | 18 + components/ui/toggle-group.tsx | 83 + components/ui/toggle.tsx | 47 + components/ui/tooltip.tsx | 61 + hooks/use-mobile.ts | 19 + lib/utils.ts | 18 + next.config.mjs | 9 + package.json | 86 + postcss.config.js | 7 + tsconfig.json | 49 + yarn.lock | 3489 ++++++++++++++++++++++++++++ 75 files changed, 10730 insertions(+) create mode 100644 .env.local create mode 100644 .gitignore create mode 100644 app/api/hello/route.ts create mode 100644 app/error.tsx create mode 100644 app/globals.css create mode 100644 app/layout.tsx create mode 100644 app/not-found.tsx create mode 100644 app/page.tsx create mode 100644 components.json create mode 100644 components/magicui/aurora-text.tsx create mode 100644 components/magicui/blur-fade.tsx create mode 100644 components/ui/accordion.tsx create mode 100644 components/ui/alert-dialog.tsx create mode 100644 components/ui/alert.tsx create mode 100644 components/ui/aspect-ratio.tsx create mode 100644 components/ui/avatar.tsx create mode 100644 components/ui/badge.tsx create mode 100644 components/ui/breadcrumb.tsx create mode 100644 components/ui/button-group.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/calendar.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/carousel.tsx create mode 100644 components/ui/chart.tsx create mode 100644 components/ui/checkbox.tsx create mode 100644 components/ui/collapsible.tsx create mode 100644 components/ui/combobox.tsx create mode 100644 components/ui/command.tsx create mode 100644 components/ui/context-menu.tsx create mode 100644 components/ui/dialog.tsx create mode 100644 components/ui/direction.tsx create mode 100644 components/ui/drawer.tsx create mode 100644 components/ui/dropdown-menu.tsx create mode 100644 components/ui/empty.tsx create mode 100644 components/ui/field.tsx create mode 100644 components/ui/form.tsx create mode 100644 components/ui/hover-card.tsx create mode 100644 components/ui/input-group.tsx create mode 100644 components/ui/input-otp.tsx create mode 100644 components/ui/input.tsx create mode 100644 components/ui/item.tsx create mode 100644 components/ui/kbd.tsx create mode 100644 components/ui/label.tsx create mode 100644 components/ui/loader.tsx create mode 100644 components/ui/menubar.tsx create mode 100644 components/ui/native-select.tsx create mode 100644 components/ui/navigation-menu.tsx create mode 100644 components/ui/pagination.tsx create mode 100644 components/ui/popover.tsx create mode 100644 components/ui/progress.tsx create mode 100644 components/ui/radio-group.tsx create mode 100644 components/ui/resizable.tsx create mode 100644 components/ui/scroll-area.tsx create mode 100644 components/ui/select.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/slider.tsx create mode 100644 components/ui/sonner.tsx create mode 100644 components/ui/spinner.tsx create mode 100644 components/ui/switch.tsx create mode 100644 components/ui/table.tsx create mode 100644 components/ui/tabs.tsx create mode 100644 components/ui/textarea.tsx create mode 100644 components/ui/toggle-group.tsx create mode 100644 components/ui/toggle.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 hooks/use-mobile.ts create mode 100644 lib/utils.ts create mode 100644 next.config.mjs create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 tsconfig.json create mode 100644 yarn.lock diff --git a/.env.local b/.env.local new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..794a724 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +node_modules/* +.next +next-env.d.ts \ No newline at end of file diff --git a/app/api/hello/route.ts b/app/api/hello/route.ts new file mode 100644 index 0000000..3c2ac37 --- /dev/null +++ b/app/api/hello/route.ts @@ -0,0 +1,5 @@ +import { NextResponse } from 'next/server'; + +export async function POST(request: Request) { + return NextResponse.json({ message: 'Hello, World' }); +} diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..86f34a6 --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,23 @@ +"use client"; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + return ( +
+
+
+ Something went wrong +
+
+
+ {error.message} +
+
+
+ ); +} diff --git a/app/globals.css b/app/globals.css new file mode 100644 index 0000000..c8eac4c --- /dev/null +++ b/app/globals.css @@ -0,0 +1,134 @@ +@import 'tailwindcss'; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +@theme { + /* Background and foreground */ + --color-background: hsl(0 0% 100%); + --color-foreground: hsl(222.2 84% 4.9%); + + /* Card */ + --color-card: hsl(0 0% 100%); + --color-card-foreground: hsl(222.2 84% 4.9%); + + /* Popover */ + --color-popover: hsl(0 0% 100%); + --color-popover-foreground: hsl(222.2 84% 4.9%); + + /* Primary */ + --color-primary: hsl(222.2 47.4% 11.2%); + --color-primary-foreground: hsl(210 40% 98%); + + /* Secondary */ + --color-secondary: hsl(210 40% 96.1%); + --color-secondary-foreground: hsl(222.2 47.4% 11.2%); + + /* Muted */ + --color-muted: hsl(210 40% 96.1%); + --color-muted-foreground: hsl(215.4 16.3% 46.9%); + + /* Accent */ + --color-accent: hsl(210 40% 96.1%); + --color-accent-foreground: hsl(222.2 47.4% 11.2%); + + /* Destructive */ + --color-destructive: hsl(0 84.2% 60.2%); + --color-destructive-foreground: hsl(210 40% 98%); + + /* Border, input, ring */ + --color-border: hsl(214.3 31.8% 91.4%); + --color-input: hsl(214.3 31.8% 91.4%); + --color-ring: hsl(222.2 84% 4.9%); + + /* Sidebar */ + --color-sidebar: hsl(0 0% 98%); + --color-sidebar-foreground: hsl(240 5.3% 26.1%); + --color-sidebar-primary: hsl(240 5.9% 10%); + --color-sidebar-primary-foreground: hsl(0 0% 98%); + --color-sidebar-accent: hsl(240 4.8% 95.9%); + --color-sidebar-accent-foreground: hsl(240 5.9% 10%); + --color-sidebar-border: hsl(220 13% 91%); + --color-sidebar-ring: hsl(217.2 91.2% 59.8%); + + /* Border radius */ + --radius-sm: 0.25rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + + /* Fonts */ + --font-heading: "Space Grotesk", sans-serif; + --font-body: "Inter", sans-serif; +} + +/* Dark mode */ +.dark { + --color-background: hsl(222.2 84% 4.9%); + --color-foreground: hsl(210 40% 98%); + --color-card: hsl(222.2 84% 4.9%); + --color-card-foreground: hsl(210 40% 98%); + --color-popover: hsl(222.2 84% 4.9%); + --color-popover-foreground: hsl(210 40% 98%); + --color-primary: hsl(210 40% 98%); + --color-primary-foreground: hsl(222.2 47.4% 11.2%); + --color-secondary: hsl(217.2 32.6% 17.5%); + --color-secondary-foreground: hsl(210 40% 98%); + --color-muted: hsl(217.2 32.6% 17.5%); + --color-muted-foreground: hsl(215 20.2% 65.1%); + --color-accent: hsl(217.2 32.6% 17.5%); + --color-accent-foreground: hsl(210 40% 98%); + --color-destructive: hsl(0 62.8% 30.6%); + --color-destructive-foreground: hsl(210 40% 98%); + --color-border: hsl(217.2 32.6% 17.5%); + --color-input: hsl(217.2 32.6% 17.5%); + --color-ring: hsl(212.7 26.8% 83.9%); + --color-sidebar: hsl(240 5.9% 10%); + --color-sidebar-foreground: hsl(240 4.8% 95.9%); + --color-sidebar-primary: hsl(224.3 76.3% 48%); + --color-sidebar-primary-foreground: hsl(0 0% 100%); + --color-sidebar-accent: hsl(240 3.7% 15.9%); + --color-sidebar-accent-foreground: hsl(240 4.8% 95.9%); + --color-sidebar-border: hsl(240 3.7% 15.9%); + --color-sidebar-ring: hsl(217.2 91.2% 59.8%); + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx new file mode 100644 index 0000000..4756c34 --- /dev/null +++ b/app/layout.tsx @@ -0,0 +1,26 @@ +import type { Metadata } from 'next'; +import { Toaster } from 'sonner'; +import './globals.css'; + +export const metadata: Metadata = { + title: 'Frontend', + description: 'Start prompting', +}; + +export default function RootLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + + + + + + {children} + + + + ); +} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..5a5ae5d --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,17 @@ +"use client"; + +export default function NotFound() { + return ( +
+
+
+ 404 +
+
+
+ This page could not be found. +
+
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx new file mode 100644 index 0000000..277852d --- /dev/null +++ b/app/page.tsx @@ -0,0 +1,11 @@ +const Home: React.FC = () => { + return ( +
+

+ Start prompting +

+
+ ) +} + +export default Home \ No newline at end of file diff --git a/components.json b/components.json new file mode 100644 index 0000000..b7b9791 --- /dev/null +++ b/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/components/magicui/aurora-text.tsx b/components/magicui/aurora-text.tsx new file mode 100644 index 0000000..8b49e42 --- /dev/null +++ b/components/magicui/aurora-text.tsx @@ -0,0 +1,40 @@ +import React, { memo } from 'react'; +import { cn } from '@/lib/utils'; + +interface AuroraTextProps { + children: React.ReactNode; + className?: string; + colors?: string[]; + speed?: number; +} + +export const AuroraText = memo( + ({ + children, + className, + colors = ['#FF0080', '#7928CA', '#0070F3', '#38bdf8'], + speed = 1, + }: AuroraTextProps) => { + const animationDuration = `${10 / speed}s`; + + const gradientStyle = { + backgroundImage: `linear-gradient(90deg, ${colors.join(', ')}, ${colors[0]})`, + backgroundSize: '200% 100%', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + backgroundClip: 'text', + animation: `aurora-flow ${animationDuration} ease-in-out infinite`, + } as React.CSSProperties; + + return ( + + {children} + + + ); + } +); + +AuroraText.displayName = 'AuroraText'; diff --git a/components/magicui/blur-fade.tsx b/components/magicui/blur-fade.tsx new file mode 100644 index 0000000..682c2b3 --- /dev/null +++ b/components/magicui/blur-fade.tsx @@ -0,0 +1,63 @@ +import React from 'react'; +import { useRef, useEffect, useState } from 'react'; +import { cn } from '@/lib/utils'; + +interface BlurFadeProps { + children: React.ReactNode; + className?: string; + duration?: number; + delay?: number; + inView?: boolean; +} + +export function BlurFade({ + children, + className, + duration = 0.4, + delay = 0, + inView = false, +}: BlurFadeProps) { + const ref = useRef(null); + const [isVisible, setIsVisible] = useState(!inView); + + useEffect(() => { + if (!inView) return; + + const observer = new IntersectionObserver( + ([entry]) => { + if (entry.isIntersecting) { + setIsVisible(true); + observer.unobserve(entry.target); + } + }, + { threshold: 0.1, rootMargin: '-50px' } + ); + + if (ref.current) { + observer.observe(ref.current); + } + + return () => { + if (ref.current) { + observer.unobserve(ref.current); + } + }; + }, [inView]); + + return ( +
+ {children} +
+ ); +} diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx new file mode 100644 index 0000000..4a8cca4 --- /dev/null +++ b/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 "@/lib/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/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..4dbb919 --- /dev/null +++ b/components/ui/alert-dialog.tsx @@ -0,0 +1,144 @@ +'use client'; + +import * as React from 'react'; + +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; + +import { buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +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/components/ui/alert.tsx b/components/ui/alert.tsx new file mode 100644 index 0000000..1421354 --- /dev/null +++ b/components/ui/alert.tsx @@ -0,0 +1,66 @@ +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 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/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..3df3fd0 --- /dev/null +++ b/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/components/ui/avatar.tsx b/components/ui/avatar.tsx new file mode 100644 index 0000000..a38fe5d --- /dev/null +++ b/components/ui/avatar.tsx @@ -0,0 +1,109 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" | "lg" +}) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...props} + /> + ) +} + +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AvatarGroupCount({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className + )} + {...props} + /> + ) +} + +export { + Avatar, + AvatarImage, + AvatarFallback, + AvatarBadge, + AvatarGroup, + AvatarGroupCount, +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..ba40cc1 --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center justify-center rounded-full border border-transparent 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: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "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: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..eb88f32 --- /dev/null +++ b/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 "@/lib/utils" + +function Breadcrumb({ ...props }: React.ComponentProps<"nav">) { + return