Se agregaron 2 secciones al landing y se conectaron con la navbar. Agregue forms con modales para la carga de reparaciones y para contacto. Tambien se añadieron 2 paginas mas para luego añadir los terminos y condiciones y las politicas de privacidad. Tambien se definio el nombre del Proyecto como RepairIT

This commit is contained in:
Edgar Javier Ortiz 2026-04-24 02:40:30 -03:00
parent da9e934f37
commit 2e0ddcf23a
36 changed files with 8202 additions and 181 deletions

51
.gitignore vendored Normal file
View File

@ -0,0 +1,51 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
# Diagnostics
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
pids
*.pid
*.seed
*.pid.lock
# Dependency directories
node_modules/
jspm_packages/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# dotenv environment variables file
.env
.env.test
.env.local
.env.development.local
.env.test.local
.env.production.local
# Build output
dist
dist-ssr
build
out
# OS/Editor Files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
Thumbs.db

24
frontend/.gitignore vendored
View File

@ -1,24 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

25
frontend/components.json Normal file
View File

@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-nova",
"rsc": false,
"tsx": false,
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}

View File

@ -1,13 +1,15 @@
<!doctype html>
<html lang="en">
<html lang="en" class="dark scroll-smooth">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>frontend</title>
<title>RepairIT</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>

10
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -10,10 +10,27 @@
"preview": "vite preview"
},
"dependencies": {
"@fontsource-variable/geist": "^5.2.8",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^1.9.0",
"next": "^16.2.4",
"radix-ui": "^1.4.3",
"react": "^19.2.5",
"react-dom": "^19.2.5",
"tailwind-merge": "^3.5.0"
"react-router-dom": "^7.14.2",
"shadcn": "^4.4.0",
"tailwind-merge": "^3.5.0",
"tw-animate-css": "^1.4.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",

View File

@ -1,13 +1,18 @@
import React from 'react';
import Hero from './components/Hero';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { LandingPage } from './components/landing-page';
import TermsPage from './components/terms-page';
import PrivacyPage from './components/privacy-page';
function App() {
return (
<div className="min-h-screen bg-background text-text-main font-sans selection:bg-accent/30">
<main>
<Hero />
</main>
</div>
<Router>
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="/terms" element={<TermsPage />} />
<Route path="/privacy" element={<PrivacyPage />} />
</Routes>
</Router>
);
}

View File

@ -0,0 +1,72 @@
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Separator } from "@/components/ui/separator";
import { DialogClose } from "@/components/ui/dialog";
export default function ContactForm() {
return (
<div className="w-full px-6 py-8 sm:px-10">
<form className="w-full text-left" onSubmit={(e) => e.preventDefault()}>
<div className="mb-6">
<h3 className="text-2xl font-bold text-foreground">
Contactar a Ventas
</h3>
<p className="text-sm text-muted-foreground mt-1">
Dejanos tus datos y nos pondremos en contacto para ofrecerte el plan ideal para tu taller de reparación.
</p>
</div>
<Separator className="bg-border/50 mb-6" />
<div className="space-y-4 bg-surface/30 p-5 rounded-lg border border-border/50">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="contactName">Nombre Completo</Label>
<Input id="contactName" placeholder="Ej. Juan Pérez" className="bg-background" required />
</div>
<div className="space-y-2">
<Label htmlFor="workshopName">Nombre del Taller</Label>
<Input id="workshopName" placeholder="Ej. PC Fixers" className="bg-background" required />
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="contactEmail">Correo Electrónico</Label>
<Input id="contactEmail" type="email" placeholder="taller@ejemplo.com" className="bg-background" required />
</div>
<div className="space-y-2">
<Label htmlFor="contactPhone">Teléfono / WhatsApp</Label>
<Input id="contactPhone" type="tel" placeholder="+54 9 11 1234-5678" className="bg-background" />
</div>
</div>
<div className="space-y-2">
<Label htmlFor="contactMessage">¿En qué podemos ayudarte?</Label>
<Textarea
id="contactMessage"
placeholder="Contanos sobre tu taller, volumen de reparaciones o consultas sobre nuestros planes..."
className="bg-background min-h-[100px] resize-y"
required
/>
</div>
</div>
<Separator className="bg-border/50 my-6" />
<div className="flex flex-col sm:flex-row items-center justify-end gap-3 pb-2">
<DialogClose asChild>
<Button type="button" variant="ghost" className="w-full sm:w-auto">
Cancelar
</Button>
</DialogClose>
<Button type="submit" className="w-full sm:w-auto bg-brand hover:bg-brand-light text-white font-medium px-8">
Enviar Mensaje
</Button>
</div>
</form>
</div>
);
}

View File

@ -0,0 +1,29 @@
import React from 'react';
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
import ContactForm from './ContactForm';
export function ContactSection() {
return (
<section id="contacto" className="bg-background min-h-[60vh] flex flex-col justify-center py-16 relative z-10 border-t border-border/40 w-full">
<div className="w-full mx-auto max-w-4xl px-6 text-center">
<h2 className="text-4xl font-bold tracking-tight lg:text-5xl text-foreground mb-6">
Impulsá tu taller hoy
</h2>
<p className="text-lg text-muted-foreground mb-10 max-w-2xl mx-auto">
Ponete en contacto con nosotros para conocer más sobre nuestros planes. Descubrí cómo RepairIT puede ayudarte a escalar tu taller.
</p>
<Dialog>
<DialogTrigger asChild>
<Button size="lg" className="px-12 py-6 text-lg bg-brand hover:bg-brand-light text-white font-medium shadow-[0_0_20px_rgba(59,130,246,0.1)]">
Contactar a Ventas
</Button>
</DialogTrigger>
<DialogContent className="w-[95vw] sm:max-w-2xl bg-background border-border p-0 overflow-hidden rounded-xl">
<ContactForm />
</DialogContent>
</Dialog>
</div>
</section>
);
}

View File

@ -1,17 +1,24 @@
import React from 'react';
import { BGPattern } from './ui/bg-pattern';
import RepairForm from './form-layout';
import {
Dialog,
DialogContent,
DialogTrigger,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
const Hero = () => {
return (
<section className="relative overflow-hidden pt-32 pb-20 lg:pt-48 lg:pb-32">
{/* Background Pattern */}
<section id="inicio" className="relative overflow-hidden min-h-screen flex flex-col justify-center pt-16">
{/* Patron de fondo del hero */}
<BGPattern variant="grid" mask="fade-edges" fill="rgba(255, 255, 255, 0.05)" />
<div className="relative max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
<h1 className="text-5xl md:text-7xl font-bold tracking-tight mb-8">
Gestiona tu taller con <br className="hidden md:block" />
<span className="text-transparent bg-clip-text bg-gradient-to-r from-accent-light via-accent to-accent-dark">
<span className="text-transparent bg-clip-text bg-gradient-to-r from-brand-light via-brand to-brand-dark">
precisión absoluta
</span>
</h1>
@ -21,12 +28,16 @@ const Hero = () => {
</p>
<div className="flex flex-col sm:flex-row items-center justify-center gap-4">
<button className="w-full sm:w-auto px-8 py-4 bg-accent hover:bg-accent-light text-white rounded-lg font-medium transition-all shadow-[0_0_20px_rgba(59,130,246,0.3)] hover:shadow-[0_0_30px_rgba(96,165,250,0.5)]">
<Dialog>
<DialogTrigger asChild>
<Button size="lg" className="w-full sm:w-auto px-8 bg-brand hover:bg-brand-light text-white font-medium">
Comenzar Ahora
</button>
<button className="w-full sm:w-auto px-8 py-4 bg-surface hover:bg-surface-hover text-text-main rounded-lg font-medium transition-colors border border-surface-hover">
Ver Demo
</button>
</Button>
</DialogTrigger>
<DialogContent className="w-[95vw] sm:max-w-4xl lg:max-w-6xl bg-background border-border p-0 overflow-hidden rounded-xl">
<RepairForm />
</DialogContent>
</Dialog>
</div>
</div>
</section>

View File

@ -1,52 +0,0 @@
import React from 'react';
const Navbar = () => {
return (
<nav className="fixed top-0 left-0 right-0 z-50 bg-background/80 backdrop-blur-md border-b border-surface-hover">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
{/* Logo */}
<div className="flex-shrink-0 flex items-center gap-2 cursor-pointer">
<div className="w-8 h-8 bg-accent rounded-lg flex items-center justify-center shadow-[0_0_15px_rgba(59,130,246,0.5)]">
<span className="text-white font-bold text-xl leading-none">H</span>
</div>
<span className="font-bold text-xl tracking-tight text-text-main">
HardFix
</span>
</div>
{/* Desktop Menu */}
<div className="hidden md:block">
<div className="ml-10 flex items-baseline space-x-8">
<a href="#" className="text-text-main font-medium hover:text-accent transition-colors">Inicio</a>
<a href="#" className="text-text-muted hover:text-text-main transition-colors">Características</a>
<a href="#" className="text-text-muted hover:text-text-main transition-colors">Precios</a>
<a href="#" className="text-text-muted hover:text-text-main transition-colors">Testimonios</a>
</div>
</div>
{/* CTA Buttons */}
<div className="hidden md:flex items-center gap-4">
<button className="text-text-muted hover:text-text-main font-medium transition-colors">
Iniciar Sesión
</button>
<button className="px-4 py-2 bg-accent hover:bg-accent-light text-white rounded-md font-medium transition-all shadow-[0_0_10px_rgba(59,130,246,0.3)] hover:shadow-[0_0_20px_rgba(96,165,250,0.5)]">
Probar Gratis
</button>
</div>
{/* Mobile menu button */}
<div className="md:hidden flex items-center">
<button className="text-text-muted hover:text-text-main focus:outline-none">
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
</nav>
);
};
export default Navbar;

View File

@ -0,0 +1,76 @@
import { Card, CardContent, CardHeader } from '@/components/ui/card'
import { ClipboardCheck, Package, MessageSquare } from 'lucide-react'
export function Features() {
return (
<section id="caracteristicas" className="bg-zinc-50 min-h-screen flex flex-col justify-center py-16 dark:bg-transparent relative z-10 w-full">
<div className="w-full mx-auto max-w-5xl px-6">
<div className="text-center">
<h2 className="text-balance text-4xl font-bold tracking-tight lg:text-5xl text-foreground">
Todo lo que tu taller necesita
</h2>
<p className="mt-4 text-muted-foreground max-w-2xl mx-auto">
Herramientas diseñadas específicamente para agilizar la reparación de hardware y mejorar la experiencia de tus clientes.
</p>
</div>
<div
className="mx-auto mt-8 grid w-full max-w-sm grid-cols-1 gap-6 *:text-center md:max-w-full md:grid-cols-3 md:mt-16">
<Card className="group shadow-black-950/5 bg-surface/40 border-border/50 backdrop-blur-sm">
<CardHeader className="pb-3">
<CardDecorator>
<ClipboardCheck className="size-6 text-brand" aria-hidden />
</CardDecorator>
<h3 className="mt-6 font-semibold text-lg text-foreground">Trazabilidad Total</h3>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground leading-relaxed">Hacé un seguimiento detallado del estado de cada equipo, desde su ingreso hasta la entrega final al cliente.</p>
</CardContent>
</Card>
<Card className="group shadow-black-950/5 bg-surface/40 border-border/50 backdrop-blur-sm">
<CardHeader className="pb-3">
<CardDecorator>
<Package className="size-6 text-brand" aria-hidden />
</CardDecorator>
<h3 className="mt-6 font-semibold text-lg text-foreground">Control de Inventario</h3>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground leading-relaxed">Gestiona tus repuestos y componentes de forma eficiente para que nunca te falte lo esencial en una reparación.</p>
</CardContent>
</Card>
<Card className="group shadow-black-950/5 bg-surface/40 border-border/50 backdrop-blur-sm">
<CardHeader className="pb-3">
<CardDecorator>
<MessageSquare className="size-6 text-brand" aria-hidden />
</CardDecorator>
<h3 className="mt-6 font-semibold text-lg text-foreground">Comunicación Profesional</h3>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground leading-relaxed">Mantené a tus clientes informados con reportes claros y presupuestos detallados directamente desde la plataforma.</p>
</CardContent>
</Card>
</div>
</div>
</section>
);
}
const CardDecorator = ({
children
}) => (
<div
aria-hidden
className="relative mx-auto size-36 [mask-image:radial-gradient(ellipse_50%_50%_at_50%_50%,#000_70%,transparent_100%)]">
<div
className="absolute inset-0 [--border:black] dark:[--border:white] bg-[linear-gradient(to_right,var(--border)_1px,transparent_1px),linear-gradient(to_bottom,var(--border)_1px,transparent_1px)] bg-[size:24px_24px] opacity-10" />
<div
className="bg-background absolute inset-0 m-auto flex size-12 items-center justify-center border-t border-l">{children}</div>
</div>
)

View File

@ -0,0 +1,47 @@
import React from 'react';
import { Link } from 'react-router-dom';
const links = [
{ title: 'Inicio', href: '#inicio' },
{ title: 'Características', href: '#caracteristicas' },
{ title: 'Contacto', href: '#contacto' },
];
export function FooterSection() {
return (
<footer className="py-12 border-t border-border/40 bg-background z-10 relative">
<div className="mx-auto max-w-5xl px-6">
<div className="flex items-center justify-center mb-8">
<span className="text-2xl font-bold tracking-tight text-foreground">
Repair<span className="text-brand">IT</span>
</span>
</div>
<div className="mt-8 mb-4 flex flex-wrap justify-center gap-6 text-sm">
{links.map((link, index) => (
<a
key={index}
href={link.href}
className="text-muted-foreground hover:text-foreground transition-colors duration-150">
{link.title}
</a>
))}
</div>
<div className="mt-4 flex flex-col items-center gap-4">
<span className="text-muted-foreground block text-center text-sm">
© {new Date().getFullYear()} RepairIT. Todos los derechos reservados.
</span>
<div className="flex flex-wrap justify-center gap-6 text-xs text-text-muted/60">
<Link to="/terms" className="hover:text-foreground transition-colors">
Términos y Condiciones
</Link>
<Link to="/privacy" className="hover:text-foreground transition-colors">
Privacidad
</Link>
</div>
</div>
</div>
</footer>
);
}

View File

@ -0,0 +1,150 @@
"use client";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { DialogClose } from "@/components/ui/dialog";
export default function RepairForm() {
const [deviceType, setDeviceType] = useState("laptop");
return (
<div className="w-full max-h-[90vh] overflow-y-auto px-6 py-8 sm:px-10">
<form className="w-full text-left" onSubmit={(e) => e.preventDefault()}>
<div className="mb-6">
<h3 className="text-2xl font-bold text-foreground">
Registrar Nueva Reparación
</h3>
<p className="text-sm text-muted-foreground mt-1">
Completa los datos del cliente y los detalles técnicos del equipo para generar la orden de servicio.
</p>
</div>
<Separator className="bg-border/50 mb-6" />
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
{/* Columna Izquierda: Información Personal */}
<div className="space-y-6">
<div>
<h4 className="text-lg font-semibold text-foreground">Información Personal</h4>
<p className="text-sm text-muted-foreground">Datos de contacto del cliente.</p>
</div>
<div className="space-y-4 bg-surface/30 p-5 rounded-lg border border-border/50">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="firstName">Nombre</Label>
<Input id="firstName" placeholder="Ej. Juan" className="bg-background" required />
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Apellido</Label>
<Input id="lastName" placeholder="Ej. Pérez" className="bg-background" required />
</div>
</div>
<div className="space-y-2">
<Label htmlFor="email">Correo Electrónico</Label>
<Input id="email" type="email" placeholder="juan@ejemplo.com" className="bg-background" required />
</div>
<div className="space-y-2">
<Label htmlFor="phone">Teléfono / WhatsApp</Label>
<Input id="phone" type="tel" placeholder="+54 9 11 1234-5678" className="bg-background" required />
</div>
</div>
</div>
{/* Columna Derecha: Detalles del Equipo */}
<div className="space-y-6">
<div>
<h4 className="text-lg font-semibold text-foreground">Detalles del Equipo</h4>
<p className="text-sm text-muted-foreground">Especificaciones y fallo reportado.</p>
</div>
<div className="space-y-4 bg-surface/30 p-5 rounded-lg border border-border/50">
<div className="space-y-2">
<Label htmlFor="deviceType">Tipo de Equipo</Label>
<Select value={deviceType} onValueChange={setDeviceType}>
<SelectTrigger id="deviceType" className="w-full bg-background">
<SelectValue placeholder="Selecciona el tipo" />
</SelectTrigger>
<SelectContent>
<SelectItem value="laptop">Notebook</SelectItem>
<SelectItem value="pc">PC de Escritorio</SelectItem>
</SelectContent>
</Select>
</div>
{deviceType === "laptop" ? (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="brand">Marca</Label>
<Input id="brand" placeholder="Ej. Lenovo, HP..." className="bg-background" required />
</div>
<div className="space-y-2">
<Label htmlFor="model">Modelo</Label>
<Input id="model" placeholder="Ej. ThinkPad T490" className="bg-background" required />
</div>
</div>
) : (
<div className="space-y-2">
<Label htmlFor="specs">Componentes Principales</Label>
<Textarea
id="specs"
placeholder="Ej. Mother ASUS B450, Ryzen 5, 16GB RAM..."
className="bg-background min-h-[60px] resize-y"
required
/>
</div>
)}
<div className="space-y-2">
<Label htmlFor="issue">Descripción del Fallo</Label>
<Textarea
id="issue"
placeholder="Describinos detalladamente el problema..."
className="bg-background min-h-[80px] resize-y"
required
/>
</div>
<div className="space-y-2">
<div className="flex items-center justify-between">
<Label htmlFor="password">Contraseña del equipo</Label>
<span className="text-xs text-muted-foreground">Opcional</span>
</div>
<Input id="password" type="text" placeholder="Pin o contraseña" className="bg-background" />
</div>
</div>
</div>
</div>
<Separator className="bg-border/50 my-6" />
{/* Acciones */}
<div className="flex flex-col sm:flex-row items-center justify-end gap-3 pb-2">
<DialogClose asChild>
<Button type="button" variant="ghost" className="w-full sm:w-auto">
Cancelar
</Button>
</DialogClose>
<Button type="submit" className="w-full sm:w-auto bg-brand hover:bg-brand-light text-white font-medium px-8">
Registrar Reparación
</Button>
</div>
</form>
</div>
);
}

View File

@ -0,0 +1,20 @@
import React from 'react';
import Hero from './Hero';
import { NavigationBar } from './navigation-bar';
import { Features } from './features';
import { ContactSection } from './ContactSection';
import { FooterSection } from './footer';
export function LandingPage() {
return (
<div className="min-h-screen bg-background text-text-main font-sans selection:bg-brand/30 pt-16">
<NavigationBar />
<main>
<Hero />
<Features />
<ContactSection />
</main>
<FooterSection />
</div>
);
}

View File

@ -0,0 +1,70 @@
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
navigationMenuTriggerStyle,
} from "@/components/ui/navigation-menu";
import { Link } from 'react-router-dom';
import { Button } from "@/components/ui/button";
export function NavigationBar({ isSimple = false }) {
return (
<header className="fixed top-0 z-50 w-full border-b border-border/40 bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className={`container mx-auto h-16 max-w-7xl items-center px-4 sm:px-6 lg:px-8 ${isSimple ? 'flex justify-center' : 'grid grid-cols-3'}`}>
{/* Logo */}
<div className={`flex items-center ${isSimple ? '' : 'justify-self-start'}`}>
<Link to="/" className="text-xl font-bold tracking-tight text-foreground">
Repair<span className="text-brand">IT</span>
</Link>
</div>
{!isSimple && (
<>
{/* Enlaces centrales (Escritorio únicamente) */}
<div className="hidden md:flex justify-self-center">
<NavigationMenu>
<NavigationMenuList>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<a href="/#inicio" className={cn(navigationMenuTriggerStyle(), "bg-transparent text-muted-foreground hover:text-foreground")}>
Inicio
</a>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<a href="/#caracteristicas" className={cn(navigationMenuTriggerStyle(), "bg-transparent text-muted-foreground hover:text-foreground")}>
Características
</a>
</NavigationMenuLink>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink asChild>
<a href="/#contacto" className={cn(navigationMenuTriggerStyle(), "bg-transparent text-muted-foreground hover:text-foreground")}>
Contacto
</a>
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenuList>
</NavigationMenu>
</div>
{/* Botones de acción */}
<div className="flex items-center gap-4 justify-self-end">
<Button variant="ghost" className="hidden sm:inline-flex text-muted-foreground hover:text-foreground">
Iniciar Sesión
</Button>
</div>
</>
)}
</div>
</header>
);
}

View File

@ -0,0 +1,24 @@
import React from 'react';
import { NavigationBar } from './navigation-bar';
import { FooterSection } from './footer';
const PrivacyPage = () => {
return (
<div className="min-h-screen bg-background text-text-main font-sans pt-16">
<NavigationBar isSimple={true} />
<main className="max-w-4xl mx-auto px-6 py-20">
<section className="mb-16">
<h1 className="text-4xl font-bold mb-8 text-foreground">Política de Privacidad</h1>
<div className="space-y-6 text-text-muted leading-relaxed">
<p>
En RepairIT, nos tomamos muy en serio la privacidad de tus datos y los de tus clientes.
</p>
</div>
</section>
</main>
<FooterSection />
</div>
);
};
export default PrivacyPage;

View File

@ -0,0 +1,24 @@
import React from 'react';
import { NavigationBar } from './navigation-bar';
import { FooterSection } from './footer';
const TermsPage = () => {
return (
<div className="min-h-screen bg-background text-text-main font-sans pt-16">
<NavigationBar isSimple={true} />
<main className="max-w-4xl mx-auto px-6 py-20">
<section className="mb-16">
<h1 className="text-4xl font-bold mb-8 text-foreground">Términos y Condiciones</h1>
<div className="space-y-6 text-text-muted leading-relaxed">
<p>
Bienvenido a RepairIT. Al utilizar nuestra plataforma, aceptas cumplir con los siguientes términos y condiciones.
</p>
</div>
</section>
</main>
<FooterSection />
</div>
);
};
export default TermsPage;

View File

@ -0,0 +1,34 @@
import * as React from "react"
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-full 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 hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
},
},
defaultVariants: {
variant: "default",
},
}
)
function Badge({
className,
variant,
...props
}) {
return (<div className={cn(badgeVariants({ variant }), className)} {...props} />);
}
export { Badge, badgeVariants }

View File

@ -0,0 +1,47 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props} />
);
})
Button.displayName = "Button"
export { Button, buttonVariants }

View File

@ -0,0 +1,50 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Card = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}
{...props} />
))
Card.displayName = "Card"
const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props} />
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("text-2xl font-semibold leading-none tracking-tight", className)}
{...props} />
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props} />
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props} />
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@ -0,0 +1,24 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}>
<CheckboxPrimitive.Indicator className={cn("flex items-center justify-center text-current")}>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }

View File

@ -0,0 +1,117 @@
import { cn } from "@/lib/utils";
import React from "react";
const CpuArchitecture = ({
className,
width = "100%",
height = "100%",
text = "R-IT",
showCpuConnections = true,
animateText = true,
}) => {
return (
<svg
className={cn("text-muted", className)}
width={width}
height={height}
// viewBox="0 0 200 100" // Original
// Chip is at x=85, y=40, width=30, height=20.
// Pins stick out about 10 units in each direction.
viewBox="70 25 60 50">
{/* CPU Box */}
<g>
{/* Cpu connections (Pins) */}
{showCpuConnections && (
<g fill="url(#cpu-connection-gradient)">
{/* Top pins */}
<rect x="92" y="37" width="2.5" height="5" rx="0.7" />
<rect x="100" y="37" width="2.5" height="5" rx="0.7" />
<rect x="108" y="37" width="2.5" height="5" rx="0.7" />
<rect x="116" y="37" width="2.5" height="5" rx="0.7" />
{/* Bottom pins */}
<rect x="92" y="58" width="2.5" height="5" rx="0.7" />
<rect x="100" y="58" width="2.5" height="5" rx="0.7" />
<rect x="108" y="58" width="2.5" height="5" rx="0.7" />
<rect x="116" y="58" width="2.5" height="5" rx="0.7" />
{/* Left pins */}
<rect x="82" y="45" width="5" height="2.5" rx="0.7" />
<rect x="82" y="52.5" width="5" height="2.5" rx="0.7" />
{/* Right pins */}
<rect x="128" y="45" width="5" height="2.5" rx="0.7" />
<rect x="128" y="52.5" width="5" height="2.5" rx="0.7" />
</g>
)}
{/* Main CPU Rectangle */}
<rect
x="85"
y="40"
width="45"
height="20"
rx="2"
fill="#181818"
filter="url(#cpu-light-shadow)" />
{/* CPU Text */}
<text
x="107.5"
y="52.5"
fontSize="7.5"
textAnchor="middle"
fill={animateText ? "url(#cpu-text-gradient)" : "white"}
fontWeight="600"
letterSpacing="0.05em">
{text}
</text>
</g>
<defs>
<filter id="cpu-light-shadow" x="-50%" y="-50%" width="200%" height="200%">
<feDropShadow dx="1.5" dy="1.5" stdDeviation="1" floodColor="black" floodOpacity="0.3" />
</filter>
{/* Cpu connection gradient */}
<linearGradient id="cpu-connection-gradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#888888" />
<stop offset="100%" stopColor="#333333" />
</linearGradient>
{/* Add CPU Text Gradient */}
<linearGradient id="cpu-text-gradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#60a5fa">
<animate
attributeName="offset"
values="-2; -1; 0"
dur="3s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1" />
</stop>
<stop offset="25%" stopColor="white">
<animate
attributeName="offset"
values="-1; 0; 1"
dur="3s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1" />
</stop>
<stop offset="50%" stopColor="#60a5fa">
<animate
attributeName="offset"
values="0; 1; 2;"
dur="3s"
repeatCount="indefinite"
calcMode="spline"
keyTimes="0; 0.5; 1"
keySplines="0.4 0 0.2 1; 0.4 0 0.2 1" />
</stop>
</linearGradient>
</defs>
</svg>
);
};
export { CpuArchitecture };

View File

@ -0,0 +1,151 @@
import * as React from "react"
import { Dialog as DialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import { XIcon } from "lucide-react"
function Dialog({
...props
}) {
return <DialogPrimitive.Root data-slot="dialog" {...props} />;
}
function DialogTrigger({
...props
}) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />;
}
function DialogPortal({
...props
}) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />;
}
function DialogClose({
...props
}) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />;
}
function DialogOverlay({
className,
...props
}) {
return (
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className={cn(
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
)}
{...props} />
);
}
function DialogContent({
className,
children,
showCloseButton = true,
...props
}) {
return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className
)}
{...props}>
{children}
{showCloseButton && (
<DialogPrimitive.Close data-slot="dialog-close" asChild>
<Button variant="ghost" className="absolute top-2 right-2" size="icon-sm">
<XIcon />
<span className="sr-only">Close</span>
</Button>
</DialogPrimitive.Close>
)}
</DialogPrimitive.Content>
</DialogPortal>
);
}
function DialogHeader({
className,
...props
}) {
return (
<div
data-slot="dialog-header"
className={cn("flex flex-col gap-2", className)}
{...props} />
);
}
function DialogFooter({
className,
showCloseButton = false,
children,
...props
}) {
return (
<div
data-slot="dialog-footer"
className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
className
)}
{...props}>
{children}
{showCloseButton && (
<DialogPrimitive.Close asChild>
<Button variant="outline">Close</Button>
</DialogPrimitive.Close>
)}
</div>
);
}
function DialogTitle({
className,
...props
}) {
return (
<DialogPrimitive.Title
data-slot="dialog-title"
className={cn("font-heading text-base leading-none font-medium", className)}
{...props} />
);
}
function DialogDescription({
className,
...props
}) {
return (
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn(
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className
)}
{...props} />
);
}
export {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogPortal,
DialogTitle,
DialogTrigger,
}

View File

@ -0,0 +1,19 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
return (
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props} />
);
})
Input.displayName = "Input"
export { Input }

View File

@ -0,0 +1,16 @@
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils"
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)
const Label = React.forwardRef(({ className, ...props }, ref) => (
<LabelPrimitive.Root ref={ref} className={cn(labelVariants(), className)} {...props} />
))
Label.displayName = LabelPrimitive.Root.displayName
export { Label }

View File

@ -0,0 +1,104 @@
import * as React from "react"
import { ChevronDownIcon } from "@radix-ui/react-icons"
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const NavigationMenu = React.forwardRef(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Root
ref={ref}
className={cn(
"relative z-10 flex max-w-max flex-1 items-center justify-center",
className
)}
{...props}>
{children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
))
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
const NavigationMenuList = React.forwardRef(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.List
ref={ref}
className={cn(
"group flex flex-1 list-none items-center justify-center space-x-1",
className
)}
{...props} />
))
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
const NavigationMenuItem = NavigationMenuPrimitive.Item
const navigationMenuTriggerStyle = cva(
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
)
const NavigationMenuTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
<NavigationMenuPrimitive.Trigger
ref={ref}
className={cn(navigationMenuTriggerStyle(), "group", className)}
{...props}>
{children}{" "}
<ChevronDownIcon
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
aria-hidden="true" />
</NavigationMenuPrimitive.Trigger>
))
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
const NavigationMenuContent = React.forwardRef(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Content
ref={ref}
className={cn(
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
className
)}
{...props} />
))
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
const NavigationMenuLink = NavigationMenuPrimitive.Link
const NavigationMenuViewport = React.forwardRef(({ className, ...props }, ref) => (
<div className={cn("absolute left-0 top-full flex justify-center")}>
<NavigationMenuPrimitive.Viewport
className={cn(
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
className
)}
ref={ref}
{...props} />
</div>
))
NavigationMenuViewport.displayName =
NavigationMenuPrimitive.Viewport.displayName
const NavigationMenuIndicator = React.forwardRef(({ className, ...props }, ref) => (
<NavigationMenuPrimitive.Indicator
ref={ref}
className={cn(
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
className
)}
{...props}>
<div
className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator>
))
NavigationMenuIndicator.displayName =
NavigationMenuPrimitive.Indicator.displayName
export {
navigationMenuTriggerStyle,
NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator,
NavigationMenuViewport,
}

View File

@ -0,0 +1,25 @@
import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover"
import { cn } from "@/lib/utils"
const Popover = PopoverPrimitive.Root
const PopoverTrigger = PopoverPrimitive.Trigger
const PopoverContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
ref={ref}
align={align}
sideOffset={sideOffset}
className={cn(
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
className
)}
{...props} />
</PopoverPrimitive.Portal>
))
PopoverContent.displayName = PopoverPrimitive.Content.displayName
export { Popover, PopoverTrigger, PopoverContent }

View File

@ -0,0 +1,29 @@
import * as React from "react"
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
import { Circle } from "lucide-react"
import { cn } from "@/lib/utils"
const RadioGroup = React.forwardRef(({ className, ...props }, ref) => {
return (<RadioGroupPrimitive.Root className={cn("grid gap-2", className)} {...props} ref={ref} />);
})
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
const RadioGroupItem = React.forwardRef(({ className, ...props }, ref) => {
return (
<RadioGroupPrimitive.Item
ref={ref}
className={cn(
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="h-2.5 w-2.5 fill-current text-current" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
})
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
export { RadioGroup, RadioGroupItem }

View File

@ -0,0 +1,122 @@
"use client"
import * as React from "react"
import * as SelectPrimitive from "@radix-ui/react-select"
import { Check, ChevronDown, ChevronUp } from "lucide-react"
import { cn } from "@/lib/utils"
const Select = SelectPrimitive.Root
const SelectGroup = SelectPrimitive.Group
const SelectValue = SelectPrimitive.Value
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
{...props}>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
))
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
const SelectScrollUpButton = React.forwardRef(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollUpButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}>
<ChevronUp className="h-4 w-4" />
</SelectPrimitive.ScrollUpButton>
))
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
const SelectScrollDownButton = React.forwardRef(({ className, ...props }, ref) => (
<SelectPrimitive.ScrollDownButton
ref={ref}
className={cn("flex cursor-default items-center justify-center py-1", className)}
{...props}>
<ChevronDown className="h-4 w-4" />
</SelectPrimitive.ScrollDownButton>
))
SelectScrollDownButton.displayName =
SelectPrimitive.ScrollDownButton.displayName
const SelectContent = React.forwardRef(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className
)}
position={position}
{...props}>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
className={cn("p-1", position === "popper" &&
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]")}>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
))
SelectContent.displayName = SelectPrimitive.Content.displayName
const SelectLabel = React.forwardRef(({ className, ...props }, ref) => (
<SelectPrimitive.Label
ref={ref}
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
{...props} />
))
SelectLabel.displayName = SelectPrimitive.Label.displayName
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
)}
{...props}>
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
))
SelectItem.displayName = SelectPrimitive.Item.displayName
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => (
<SelectPrimitive.Separator
ref={ref}
className={cn("-mx-1 my-1 h-px bg-muted", className)}
{...props} />
))
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
export {
Select,
SelectGroup,
SelectValue,
SelectTrigger,
SelectContent,
SelectLabel,
SelectItem,
SelectSeparator,
SelectScrollUpButton,
SelectScrollDownButton,
}

View File

@ -0,0 +1,23 @@
import * as React from "react"
import * as SeparatorPrimitive from "@radix-ui/react-separator"
import { cn } from "@/lib/utils"
const Separator = React.forwardRef((
{ className, orientation = "horizontal", decorative = true, ...props },
ref
) => (
<SeparatorPrimitive.Root
ref={ref}
decorative={decorative}
orientation={orientation}
className={cn(
"shrink-0 bg-border",
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
className
)}
{...props} />
))
Separator.displayName = SeparatorPrimitive.Root.displayName
export { Separator }

View File

@ -0,0 +1,18 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Textarea = React.forwardRef(({ className, ...props }, ref) => {
return (
<textarea
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
ref={ref}
{...props} />
);
})
Textarea.displayName = "Textarea"
export { Textarea }

View File

@ -1,9 +1,14 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "@fontsource-variable/geist";
@custom-variant dark (&:is(.dark *));
@theme {
--color-accent-light: #60a5fa; /* Blue 400 */
--color-accent: #3b82f6; /* Blue 500 */
--color-accent-dark: #2563eb; /* Blue 600 */
--color-brand-light: #60a5fa; /* Blue 400 */
--color-brand: #3b82f6; /* Blue 500 */
--color-brand-dark: #2563eb; /* Blue 600 */
--color-background: #09090b; /* Zinc 950 (Almost Black) */
--color-surface: #18181b; /* Zinc 900 */
--color-surface-hover: #27272a;/* Zinc 800 */
@ -16,5 +21,124 @@
background-color: var(--color-background);
color: var(--color-text-main);
font-family: 'Inter', system-ui, -apple-system, sans-serif;
@apply bg-background text-foreground;
}
* {
@apply border-border outline-ring/50;
}
html {
@apply font-sans;
}
}
@theme inline {
--font-heading: var(--font-sans);
--font-sans: 'Geist Variable', sans-serif;
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-foreground: var(--foreground);
--color-background: var(--background);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--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.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--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);
}

View File

@ -1,6 +1,6 @@
import { clsx } from "clsx"
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge"
export function cn(...inputs) {
return twMerge(clsx(inputs))
return twMerge(clsx(inputs));
}

View File

@ -1,6 +1,7 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from "path"
// https://vite.dev/config/
export default defineConfig({
@ -8,4 +9,9 @@ export default defineConfig({
tailwindcss(),
react()
],
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
})