diff --git a/app/globals.css b/app/globals.css index a2dc41e..aace076 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,692 @@ -@import "tailwindcss"; +@tailwind base; +@tailwind components; +@tailwind utilities; -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { +@layer base { :root { - --background: #0a0a0a; - --foreground: #ededed; + /* Light Mode - Smart-Admin Copilot Palette */ + --background: 0 0% 100%; + --foreground: 222 47% 11%; + --card: 210 40% 98%; + --card-foreground: 222 47% 11%; + --popover: 0 0% 100%; + --popover-foreground: 222 47% 11%; + --primary: 221 83% 53%; + --primary-foreground: 0 0% 100%; + --secondary: 168 76% 40%; + --secondary-foreground: 0 0% 100%; + --accent: 262 83% 58%; + --accent-foreground: 0 0% 100%; + --muted: 210 40% 96%; + --muted-foreground: 215 16% 47%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 98%; + --border: 214 32% 91%; + --input: 214 32% 91%; + --ring: 221 83% 53%; + --radius: 0.75rem; + + /* Custom Colors */ + --success: 160 84% 39%; + --warning: 38 92% 50%; + --royal-blue: 221 83% 53%; + --teal: 168 76% 40%; + --violet: 262 83% 58%; + --emerald: 160 84% 39%; + --amber: 38 92% 50%; + --deep-slate: 222 47% 11%; + --medium-slate: 215 16% 47%; + --light-slate: 214 32% 91%; + --off-white: 210 40% 98%; + } + + .dark { + /* Dark Mode - Smart-Admin Copilot Palette */ + --background: 222 47% 5%; + --foreground: 210 40% 98%; + --card: 217 33% 17%; + --card-foreground: 210 40% 98%; + --popover: 217 33% 17%; + --popover-foreground: 210 40% 98%; + --primary: 217 91% 60%; + --primary-foreground: 0 0% 100%; + --secondary: 189 94% 43%; + --secondary-foreground: 0 0% 100%; + --accent: 258 90% 66%; + --accent-foreground: 0 0% 100%; + --muted: 217 33% 17%; + --muted-foreground: 215 20% 65%; + --destructive: 0 62% 30%; + --destructive-foreground: 0 0% 98%; + --border: 217 33% 25%; + --input: 217 33% 25%; + --ring: 217 91% 60%; + + /* Custom Dark Colors */ + --success: 158 64% 52%; + --warning: 48 96% 53%; + --bright-blue: 217 91% 60%; + --cyan: 189 94% 43%; + --purple: 258 90% 76%; + --green: 158 64% 52%; + --dark-slate: 217 33% 17%; + --almost-white: 210 40% 98%; } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +@layer base { + * { + @apply border-border; + } + + html { + scroll-behavior: smooth; + } + + body { + @apply bg-background text-foreground antialiased; + font-feature-settings: + "rlig" 1, + "calt" 1; + } +} + +@layer utilities { + /* Gradient Text */ + .gradient-text { + @apply bg-clip-text text-transparent; + background-image: linear-gradient( + 135deg, + hsl(var(--primary)) 0%, + hsl(var(--accent)) 50%, + hsl(var(--secondary)) 100% + ); + } + + .gradient-text-hero-light { + @apply bg-clip-text text-transparent; + background-image: linear-gradient( + 135deg, + #0f172a 0%, + #1e40af 50%, + #0f172a 100% + ); + } + + .gradient-text-hero-dark { + @apply bg-clip-text text-transparent; + background-image: linear-gradient( + 135deg, + #f8fafc 0%, + #93c5fd 50%, + #f8fafc 100% + ); + } + + /* Glass Morphism */ + .glass { + @apply backdrop-blur-xl; + background: rgba(255, 255, 255, 0.7); + border: 1px solid rgba(226, 232, 240, 0.5); + } + + .dark .glass { + background: rgba(30, 41, 59, 0.7); + border: 1px solid rgba(51, 65, 85, 0.5); + } + + .glass-strong { + @apply backdrop-blur-2xl; + background: rgba(255, 255, 255, 0.85); + border: 1px solid rgba(226, 232, 240, 0.6); + } + + .dark .glass-strong { + background: rgba(15, 23, 42, 0.85); + border: 1px solid rgba(51, 65, 85, 0.6); + } + + /* Gradient Backgrounds */ + .gradient-bg-mesh { + background: + radial-gradient( + ellipse at 20% 20%, + rgba(59, 130, 246, 0.15) 0%, + transparent 50% + ), + radial-gradient( + ellipse at 80% 80%, + rgba(139, 92, 246, 0.15) 0%, + transparent 50% + ), + radial-gradient( + ellipse at 50% 50%, + rgba(20, 184, 166, 0.1) 0%, + transparent 60% + ); + } + + .dark .gradient-bg-mesh { + background: + radial-gradient( + ellipse at 20% 20%, + rgba(59, 130, 246, 0.2) 0%, + transparent 50% + ), + radial-gradient( + ellipse at 80% 80%, + rgba(139, 92, 246, 0.2) 0%, + transparent 50% + ), + radial-gradient( + ellipse at 50% 50%, + rgba(6, 182, 212, 0.15) 0%, + transparent 60% + ); + } + + /* Button Gradients */ + .btn-gradient { + background: linear-gradient(135deg, #2563eb 0%, #7c3aed 100%); + transition: all 0.3s ease; + } + + .btn-gradient:hover { + background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); + transform: scale(1.05); + box-shadow: 0 0 40px rgba(59, 130, 246, 0.4); + } + + /* Glow Effects */ + .glow-blue { + box-shadow: 0 0 40px rgba(59, 130, 246, 0.4); + } + + .glow-violet { + box-shadow: 0 0 40px rgba(139, 92, 246, 0.4); + } + + .glow-teal { + box-shadow: 0 0 40px rgba(20, 184, 166, 0.4); + } + + /* Card Gradients */ + .card-gradient-blue { + background: linear-gradient( + 135deg, + rgba(37, 99, 235, 0.1) 0%, + rgba(139, 92, 246, 0.1) 100% + ); + } + + .card-gradient-teal { + background: linear-gradient( + 135deg, + rgba(20, 184, 166, 0.1) 0%, + rgba(6, 182, 212, 0.1) 100% + ); + } + + .card-gradient-violet { + background: linear-gradient( + 135deg, + rgba(139, 92, 246, 0.1) 0%, + rgba(168, 85, 247, 0.1) 100% + ); + } + + .card-gradient-amber { + background: linear-gradient( + 135deg, + rgba(245, 158, 11, 0.1) 0%, + rgba(249, 115, 22, 0.1) 100% + ); + } + + .card-gradient-emerald { + background: linear-gradient( + 135deg, + rgba(16, 185, 129, 0.1) 0%, + rgba(34, 197, 94, 0.1) 100% + ); + } + + /* Animated Border */ + .animated-border { + position: relative; + background: + linear-gradient(var(--background), var(--background)) padding-box, + linear-gradient( + 135deg, + hsl(var(--primary)), + hsl(var(--accent)), + hsl(var(--secondary)) + ) + border-box; + border: 1px solid transparent; + } + + /* Noise Texture */ + .noise-texture { + position: relative; + } + + .noise-texture::before { + content: ""; + position: absolute; + inset: 0; + background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E"); + opacity: 0.03; + pointer-events: none; + z-index: 1; + } + + /* Grid Pattern */ + .grid-pattern { + background-image: + linear-gradient(rgba(148, 163, 184, 0.1) 1px, transparent 1px), + linear-gradient(90deg, rgba(148, 163, 184, 0.1) 1px, transparent 1px); + background-size: 40px 40px; + } + + .dark .grid-pattern { + background-image: + linear-gradient(rgba(71, 85, 105, 0.2) 1px, transparent 1px), + linear-gradient(90deg, rgba(71, 85, 105, 0.2) 1px, transparent 1px); + } + + /* 3D Transform */ + .perspective-1000 { + perspective: 1000px; + } + + .transform-3d { + transform-style: preserve-3d; + } + + /* Hide scrollbar */ + .hide-scrollbar { + -ms-overflow-style: none; + scrollbar-width: none; + } + + .hide-scrollbar::-webkit-scrollbar { + display: none; + } +} + +/* Custom Animations */ +@keyframes float { + 0%, + 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-20px); + } +} + +@keyframes float-slow { + 0%, + 100% { + transform: translateY(0px) translateX(0px); + } + 50% { + transform: translateY(-15px) translateX(10px); + } +} + +@keyframes pulse-glow { + 0%, + 100% { + box-shadow: 0 0 20px rgba(59, 130, 246, 0.3); + } + 50% { + box-shadow: 0 0 40px rgba(59, 130, 246, 0.6); + } +} + +@keyframes gradient-shift { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +@keyframes shimmer { + 0% { + background-position: -200% 0; + } + 100% { + background-position: 200% 0; + } +} + +@keyframes spin-slow { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@keyframes bounce-subtle { + 0%, + 100% { + transform: translateY(0); + } + 50% { + transform: translateY(-5px); + } +} + +@keyframes slide-up { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-down { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes scale-in { + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes count-up { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes draw-line { + from { + stroke-dashoffset: 1000; + } + to { + stroke-dashoffset: 0; + } +} + +@keyframes particle-float { + 0%, + 100% { + transform: translateY(0) translateX(0); + opacity: 0.5; + } + 25% { + transform: translateY(-30px) translateX(10px); + opacity: 0.8; + } + 50% { + transform: translateY(-20px) translateX(-10px); + opacity: 0.6; + } + 75% { + transform: translateY(-40px) translateX(5px); + opacity: 0.9; + } +} + +@keyframes ring { + 0% { + transform: rotate(0deg); + } + 25% { + transform: rotate(15deg); + } + 50% { + transform: rotate(0deg); + } + 75% { + transform: rotate(-15deg); + } + 100% { + transform: rotate(0deg); + } +} + +/* Animation Classes */ +.animate-float { + animation: float 6s ease-in-out infinite; +} + +.animate-float-slow { + animation: float-slow 8s ease-in-out infinite; +} + +.animate-float-delayed { + animation: float 6s ease-in-out infinite; + animation-delay: 2s; +} + +.animate-pulse-glow { + animation: pulse-glow 3s ease-in-out infinite; +} + +.animate-gradient-shift { + background-size: 200% 200%; + animation: gradient-shift 8s ease infinite; +} + +.animate-shimmer { + background: linear-gradient( + 90deg, + transparent, + rgba(255, 255, 255, 0.2), + transparent + ); + background-size: 200% 100%; + animation: shimmer 2s infinite; +} + +.animate-spin-slow { + animation: spin-slow 20s linear infinite; +} + +.animate-bounce-subtle { + animation: bounce-subtle 2s ease-in-out infinite; +} + +.animate-slide-up { + animation: slide-up 0.6s ease-out forwards; +} + +.animate-slide-down { + animation: slide-down 0.6s ease-out forwards; +} + +.animate-fade-in { + animation: fade-in 0.5s ease-out forwards; +} + +.animate-scale-in { + animation: scale-in 0.5s ease-out forwards; +} + +.animate-particle-float { + animation: particle-float 10s ease-in-out infinite; +} + +.animate-ring { + animation: ring 0.5s ease-in-out; +} + +/* Stagger Animation Delays */ +.stagger-1 { + animation-delay: 0.1s; +} +.stagger-2 { + animation-delay: 0.2s; +} +.stagger-3 { + animation-delay: 0.3s; +} +.stagger-4 { + animation-delay: 0.4s; +} +.stagger-5 { + animation-delay: 0.5s; +} +.stagger-6 { + animation-delay: 0.6s; +} +.stagger-7 { + animation-delay: 0.7s; +} +.stagger-8 { + animation-delay: 0.8s; +} + +/* Scroll Animation Classes */ +.scroll-animate { + opacity: 0; + transform: translateY(30px); + transition: + opacity 0.6s ease-out, + transform 0.6s ease-out; +} + +.scroll-animate.visible { + opacity: 1; + transform: translateY(0); +} + +.scroll-animate-left { + opacity: 0; + transform: translateX(-50px); + transition: + opacity 0.6s ease-out, + transform 0.6s ease-out; +} + +.scroll-animate-left.visible { + opacity: 1; + transform: translateX(0); +} + +.scroll-animate-right { + opacity: 0; + transform: translateX(50px); + transition: + opacity 0.6s ease-out, + transform 0.6s ease-out; +} + +.scroll-animate-right.visible { + opacity: 1; + transform: translateX(0); +} + +.scroll-animate-scale { + opacity: 0; + transform: scale(0.9); + transition: + opacity 0.6s ease-out, + transform 0.6s ease-out; +} + +.scroll-animate-scale.visible { + opacity: 1; + transform: scale(1); +} + +/* Hover Effects */ +.hover-lift { + transition: + transform 0.3s ease, + box-shadow 0.3s ease; +} + +.hover-lift:hover { + transform: translateY(-4px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); +} + +.dark .hover-lift:hover { + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); +} + +.hover-scale { + transition: transform 0.3s ease; +} + +.hover-scale:hover { + transform: scale(1.05); +} + +.hover-glow { + transition: box-shadow 0.3s ease; +} + +.hover-glow:hover { + box-shadow: 0 0 30px rgba(59, 130, 246, 0.4); +} + +/* Focus States */ +.focus-ring:focus-visible { + outline: none; + ring: 2px; + ring-color: hsl(var(--ring)); + ring-offset: 2px; + ring-offset-color: hsl(var(--background)); +} + +/* Reduced Motion */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } + + .scroll-animate, + .scroll-animate-left, + .scroll-animate-right, + .scroll-animate-scale { + opacity: 1; + transform: none; + } } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..ba3b658 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import { Providers } from "./provider"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -23,11 +24,11 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + - {children} + {children} ); diff --git a/app/page.tsx b/app/page.tsx index 295f8fd..b3437d5 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,159 @@ -import Image from "next/image"; +"use client"; + +import { useEffect, useState } from "react"; +import { Navbar } from "@/components/views/Home/Navbar"; +import { Hero } from "@/components/views/Home/Hero"; +import { Features } from "@/components/views/Home/Features"; +import { HowItWorks } from "@/components/views/Home/HowItWorks"; +import { Stats } from "@/components/views/Home/Stats"; +import { Footer } from "@/components/views/Home/Footer"; +import { Sparkles } from "lucide-react"; + +//Loading Screen +function LoadingScreen({ onComplete }: { onComplete: () => void }) { + const [progress, setProgress] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + clearInterval(interval); + setTimeout(onComplete, 300); + return 100; + } + return prev + Math.random() * 15; + }); + }, 100); + + return () => clearInterval(interval); + }, [onComplete]); -export default function Home() { return ( -
-
- Next.js logo +
+
+ -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
- -
+
+ +

+ Smart-Admin Copilot +

+ +
+
+
+ +

+ Loading amazing experience... +

+
+ ); +} + +//Scroll Progress +function ScrollProgressBar() { + const [progress, setProgress] = useState(0); + + useEffect(() => { + const handleScroll = () => { + const scrollTop = window.scrollY; + const docHeight = + document.documentElement.scrollHeight - window.innerHeight; + const scrollPercent = (scrollTop / docHeight) * 100; + setProgress(scrollPercent); + }; + + window.addEventListener("scroll", handleScroll, { passive: true }); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + return ( +
+
+
+ ); +} + +//Back To Top +function BackToTop() { + const [isVisible, setIsVisible] = useState(false); + + useEffect(() => { + const handleScroll = () => { + setIsVisible(window.scrollY > 500); + }; + + window.addEventListener("scroll", handleScroll, { passive: true }); + return () => window.removeEventListener("scroll", handleScroll); + }, []); + + const scrollToTop = () => { + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + return ( + + ); +} + +//Main Page +export default function Home() { + const [isLoading, setIsLoading] = useState(true); + + const handleLoadingComplete = () => { + setIsLoading(false); + }; + + if (isLoading) { + return ; + } + + return ( +
+ + + + +
+ + + + +
+
+ +
); } diff --git a/app/provider.tsx b/app/provider.tsx new file mode 100644 index 0000000..82d3d90 --- /dev/null +++ b/app/provider.tsx @@ -0,0 +1,11 @@ +"use client"; +import { ThemeProvider } from "next-themes"; +import { ReactNode } from "react"; + +export function Providers({ children }: { children: ReactNode }) { + return ( + + {children} + + ); +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..7b3464b --- /dev/null +++ b/components.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx new file mode 100644 index 0000000..2f55a32 --- /dev/null +++ b/components/ui/accordion.tsx @@ -0,0 +1,57 @@ +"use client" + +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/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..57760f2 --- /dev/null +++ b/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +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/components/ui/alert.tsx b/components/ui/alert.tsx new file mode 100644 index 0000000..5afd41d --- /dev/null +++ b/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 px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + 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/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..d6a5226 --- /dev/null +++ b/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"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/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..e87d62b --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "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", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/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) =>