+
+
+
+
+
+
+ {/* Sidebar Inset para contener el área de trabajo y la barra de navegación superior */}
+
+
+ {/* Cabecera Superior con Trigger y Breadcrumbs */}
+
+
+
+
+
+ {getBreadcrumbs()}
+
+
+
+ Taller Central • Activo
+
+
+
+ {/* Contenido Dinámico de las Páginas del Dashboard */}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/components/layout/PublicLayout.jsx b/frontend/src/components/layout/PublicLayout.jsx
new file mode 100644
index 0000000..97b95d4
--- /dev/null
+++ b/frontend/src/components/layout/PublicLayout.jsx
@@ -0,0 +1,93 @@
+import { Link, Outlet } from "react-router-dom";
+import { Separator } from "@/components/ui/separator";
+import { Button } from "@/components/ui/button";
+
+export default function PublicLayout() {
+ return (
+
+
+ {/* Navbar con Efecto de Desenfoque y Vidrio (Glassmorphism) */}
+
+
+
+ {/* Logo del Proyecto (RepairIT) */}
+
+
+ RepairIT
+
+
+
+ {/* Menú de Navegación de la Landing - Optimizado para Móviles */}
+
+
+
+
+
+ {/* Contenedor Principal (Hijo) con Efectos de Fondo Encapsulados */}
+
+ {/* Efectos de Fondo Decorativos */}
+
+
+
+
+
+ {/* Footer Tecnológico - Optimizado para Móviles */}
+
+
+
+ );
+}
diff --git a/frontend/src/components/navigation-bar.jsx b/frontend/src/components/navigation-bar.jsx
deleted file mode 100644
index 115501d..0000000
--- a/frontend/src/components/navigation-bar.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-"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 (
-
-
+ Resumen operativo del taller y control de stock de insumos técnicos.
+
+
+
+ {/* Grid de Estadísticas Rápidas mediante Cards de shadcn/ui */}
+
+
+ {/* Card 1: Órdenes Activas */}
+
+
+ Órdenes Activas
+
+
+
+ 12
+ 4 en diagnóstico, 8 en reparación
+
+
+
+ {/* Card 2: Entregados este Mes */}
+
+
+ Entregas del Mes
+
+
+
+ 45
+ +15% respecto al mes anterior
+
+
+
+ {/* Card 3: Repuestos en Stock */}
+
+
+ Items en Inventario
+
+
+
+ 84
+ Distribuidos en 14 categorías
+
+
+
+ {/* Card 4: Alertas de Stock */}
+
+
+ Alertas de Stock
+
+
+
+ 2
+ Requieren compra urgente
+
+
+
+
+
+
+
+ {/* Tabla de Órdenes Recientes mediante Card de shadcn */}
+
+
+
+ Ingresos Recientes
+
+ Actualizado hace 5m
+
+
+
+ Aquí se desplegará el listado interactivo con filtros rápidos de diagnóstico. Ya podés probar la pestaña de Órdenes para dar de alta dispositivos.
+
+
+
+
+
+ {/* Alertas de Stock Detalladas mediante Alert de shadcn/ui */}
+
+
+
+ Críticos de Inventario
+
+
+
+
+
+
+
+
+ Pasta Térmica Arctic MX-6
+ Bajo Stock
+
+
+ Mínimo requerido: 5 jeringas | Actual en stock: 2 jeringas.
+
+
+
+
+
+
+
+
+ Alcohol Isopropílico 1L
+ Bajo Stock
+
+
+ Mínimo requerido: 3 botellas | Actual en stock: 1 botella.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/admin/InventoryPage.jsx b/frontend/src/views/admin/InventoryPage.jsx
new file mode 100644
index 0000000..cb5104c
--- /dev/null
+++ b/frontend/src/views/admin/InventoryPage.jsx
@@ -0,0 +1,36 @@
+import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
+import { Button } from "@/components/ui/button";
+import { Package } from "lucide-react";
+import { toast } from "sonner";
+
+export default function InventoryPage() {
+ return (
+
+
+
+ Inventario de Insumos
+
+
+ Control de stock y alertas de reposición de componentes técnicos.
+
+
+
+
+
+
+
+
+
+
Módulo de Inventario en Desarrollo
+
+ En la siguiente iteración de la Fase I implementaremos el control cuantitativo de insumos, auditoría automática de repuestos y la simulación de alerta de bajo suministro.
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/admin/LoginPage.jsx b/frontend/src/views/admin/LoginPage.jsx
new file mode 100644
index 0000000..d855d63
--- /dev/null
+++ b/frontend/src/views/admin/LoginPage.jsx
@@ -0,0 +1,112 @@
+import { useState } from "react";
+import { useNavigate } from "react-router-dom";
+import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Button } from "@/components/ui/button";
+import { Checkbox } from "@/components/ui/checkbox";
+import { toast } from "sonner";
+
+export default function LoginPage() {
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [rememberMe, setRememberMe] = useState(false);
+ const navigate = useNavigate();
+
+ const handleLogin = (e) => {
+ e.preventDefault();
+ // Simular el inicio de sesión y redirigir al Dashboard
+ toast.success("¡Acceso concedido!", {
+ description: `Bienvenido de vuelta, técnico.`
+ });
+ navigate("/dashboard");
+ };
+
+ return (
+
+
+ {/* Background Glow */}
+
+
+
+
+ {/* Título y descripción */}
+
+
+ Acceso al Panel Técnico
+
+
+ Ingresa tus credenciales autorizadas para gestionar las órdenes y el inventario.
+
+
+ Alta de dispositivos ingresados y control de trazabilidad técnica del taller.
+
+
+
+ {/* Selector de Pestañas (Tabs) estilo shadcn */}
+
+
+
+
+
+
+ {activeTab === "registro" ? (
+
+ {/* Formulario de Ingreso de Dispositivo (Card shadcn) */}
+
+
+
+
+ Ingreso de Equipo
+
+
+ Registrá los datos del cliente y el dispositivo a reparar.
+
+
+
+
+
+
+ ) : (
+ /* Listado y Control de Órdenes Registradas (Card shadcn) */
+
+
+
+
+
+ Control de Trazabilidad
+
+ Total: {orders.length} Equipos
+
+
+ {orders.length > 0 ? (
+
+
+
+
+
Código
+
Cliente
+
Dispositivo
+
Fecha
+
Estado (Editar)
+
Acción
+
+
+
+ {orders.map((o) => (
+
+
{o.id}
+
+ {o.clientName}
+ {o.clientDni}
+
+
+ {o.deviceModel}
+ {o.deviceType}
+
+
{o.date}
+
{getStatusDropdown(o.id, o.status)}
+
+
+
+
+ ))}
+
+
+
+ ) : (
+
+
+ No hay órdenes de servicio cargadas. Ve a la pestaña de "Registrar Ingreso" para añadir un ingreso técnico.
+
+
+ )}
+
+
+
+ * Nota: Los registros agregados persisten únicamente en el estado local de la sesión de prueba de React.
+
+
+ )}
+
+
+ );
+}
diff --git a/frontend/src/views/admin/ProfilePage.jsx b/frontend/src/views/admin/ProfilePage.jsx
new file mode 100644
index 0000000..28bdf1a
--- /dev/null
+++ b/frontend/src/views/admin/ProfilePage.jsx
@@ -0,0 +1,159 @@
+import { useState } from "react";
+import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { toast } from "sonner";
+import {
+ Store,
+ Save,
+ CheckCircle
+} from "lucide-react";
+
+export default function ProfilePage() {
+ const [venueName, setVenueName] = useState("RepairIT Tucumán Centro");
+ const [address, setAddress] = useState("San Martín 650, San Miguel de Tucumán");
+ const [phone, setPhone] = useState("+54 381 4223344");
+ const [email, setEmail] = useState("tucuman@repairit.cloud");
+
+ const handleSave = (e) => {
+ e.preventDefault();
+ toast.success("¡Configuración del Taller Guardada!", {
+ description: `Los datos de la sucursal "${venueName}" han sido actualizados con éxito.`,
+ });
+ };
+
+ return (
+
+ {/* Cabecera interna */}
+
+
+
+ Perfil de Taller (Venue)
+
+
+ Gestión del perfil del local y estado de la suscripción de la sucursal.
+
+
+
+
+
+ {/* Columna Izquierda (2/3): Datos principales del taller */}
+
+
+ {/* Card 1: Datos de la Sucursal */}
+
+
+
+ Datos del Establecimiento
+
+
+ Información de contacto que se mostrará en los recibos y el seguimiento del cliente.
+
+
+
+
+
+
+
+
+
+ {/* Columna Derecha (1/3): Suscripción */}
+
+
+ {/* Card 2: Suscripción */}
+
+
+ Suscripción
+
+
+
+
+ Plan Licenciado
+ Multi-Taller Pro
+
+
+ Vencimiento
+ 31/12/2026
+
+
+ Estado del Pago
+
+
+ Al Día
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/public/LandingPage.jsx b/frontend/src/views/public/LandingPage.jsx
new file mode 100644
index 0000000..f38dad4
--- /dev/null
+++ b/frontend/src/views/public/LandingPage.jsx
@@ -0,0 +1,487 @@
+import { useState, useEffect } from "react";
+import { useNavigate } from "react-router-dom";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Textarea } from "@/components/ui/textarea";
+import { Badge } from "@/components/ui/badge";
+import { Checkbox } from "@/components/ui/checkbox";
+import { Card, CardHeader, CardTitle, CardContent, CardDescription, CardFooter } from "@/components/ui/card";
+import { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from "@/components/ui/accordion";
+import { toast } from "sonner";
+
+export default function LandingPage() {
+ const [trackingId, setTrackingId] = useState("");
+ const navigate = useNavigate();
+ const [isMobile, setIsMobile] = useState(false);
+
+ useEffect(() => {
+ const handleResize = () => {
+ setIsMobile(window.innerWidth < 640);
+ };
+ handleResize();
+ window.addEventListener("resize", handleResize);
+ return () => window.removeEventListener("resize", handleResize);
+ }, []);
+
+ // Estado del formulario de contacto
+ const [contactName, setContactName] = useState("");
+ const [contactEmail, setContactEmail] = useState("");
+ const [contactMessage, setContactMessage] = useState("");
+ const [acceptTerms, setAcceptTerms] = useState(false);
+
+ const handleSearch = (e) => {
+ e.preventDefault();
+ if (trackingId.trim()) {
+ navigate(`/seguimiento/${trackingId.trim()}`);
+ }
+ };
+
+ const handleContactSubmit = (e) => {
+ e.preventDefault();
+ if (!acceptTerms) {
+ toast.error("Debe aceptar los términos de privacidad.");
+ return;
+ }
+
+ // Simulación del envío de mensaje de contacto
+ toast.success("¡Mensaje enviado con éxito!", {
+ description: `Gracias por contactarte, ${contactName}. Te responderemos a la brevedad.`,
+ });
+
+ // Resetear formulario
+ setContactName("");
+ setContactEmail("");
+ setContactMessage("");
+ setAcceptTerms(false);
+ };
+
+ return (
+
+ {/* Sección Hero sin cuadrícula y sin tag de servicio */}
+
+ {/* Glow Central */}
+
+
+
+
+ {/* Título Principal */}
+
+ Tu taller de servicio técnico, más{" "}
+
+ transparente
+ {" "}
+ que nunca
+
+
+ {/* Subtítulo */}
+
+ Hacemos el seguimiento de tu reparación simple. Consultá el estado de tu equipo en tiempo real y aprobá presupuestos con un solo clic, sin contraseñas.
+
+
+ {/* Buscador de NanoID (CTA) - Optimizado para móviles */}
+
+
+ {/* Ejemplo rápido para testing */}
+
+ ¿Querés probar? Ingresá setTrackingId("demo-id")} className="text-primary hover:underline cursor-pointer font-bold">demo-id para ver una reparación simulada.
+
+
+
+
+
+ {/* Sección de Características */}
+
+
+
+
+
+ ¿Por qué elegir RepairIT?
+
+
+ Nuestra plataforma está diseñada para ofrecerte una visión transparente en cada etapa de la reparación.
+
+
+
+
+
+ {/* Feature 1 con Card de shadcn */}
+
+
+
+
+
+ Trazabilidad Instantánea
+
+
+
+ Olvidate de las llamadas telefónicas. A través de tu enlace único podés ver si tu equipo está ingresado, diagnosticado o listo para retirar.
+
+
+
+
+ {/* Feature 2 con Card de shadcn */}
+
+
+
+
+
+ Aprobación Online
+
+
+
+ Visualizá el presupuesto de mano de obra y repuestos en detalle. Podés aprobar o rechazar la reparación desde tu misma interfaz de consulta.
+
+
+
+
+ {/* Feature 3 con Card de shadcn */}
+
+
+
+
+
+ Control de Insumos
+
+
+
+ El sistema audita el stock técnico y descuenta automáticamente los componentes utilizados, asegurando que siempre tengamos repuestos disponibles.
+
+
+
+
+
+
+
+
+
+ {/* Sección de Precios */}
+
+
+
+
+
+ Planes a tu Medida
+
+
+ Escogé el nivel de potencia y gestión que tu taller necesita para crecer.
+
+
+
+
+
+ {/* Tier 1: Inicial */}
+
+
+ Plan Inicial
+ Ideal para técnicos independientes que comienzan.
+
+
+
+ $1
+ / mes
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+ Todo lo que necesitás saber sobre RepairIT y nuestro sistema de trazabilidad.
+
+
+
+
+
+
+
+ ¿Cómo funciona el seguimiento de mi reparación?
+
+
+ Vas a recibir un código de seguimiento único al registrar tu equipo en nuestro taller. Solo tenes que ingresarlo en el buscador de la página de inicio para ver el estado de tu equipo en tiempo real, sin necesidad de crear una cuenta o recordar contraseñas.
+
+
+
+
+
+ ¿Puedo aprobar presupuestos en línea?
+
+
+ Sí. Cuando el técnico realice el diagnóstico y cargue el presupuesto de mano de obra e insumos, podrás visualizar el detalle en tu panel de seguimiento y aprobar o rechazar la reparación de forma digital con un solo clic.
+
+
+
+
+
+ ¿Tienen cargos ocultos adicionales?
+
+
+ No, para nada. La transparencia es nuestro pilar fundamental. Desglosamos cada centavo en el presupuesto de repuestos y mano de obra. Los planes de suscripción de taller también están fijados sin sorpresas ni letra chica.
+
+
+
+
+
+ ¿Mis datos personales y del dispositivo están seguros?
+
+
+ Absolutamente. Cumplimos rigurosamente con las normativas locales de protección de datos personales. Toda tu información y la de tus equipos está cifrada y almacenada en servidores de alta seguridad.
+
+
+
+
+
+ ¿Qué sucede si tengo más de una sucursal o taller?
+
+
+ Disponemos de un plan Multi-Sucursal especialmente diseñado para redes de soporte técnico. Te permite gestionar múltiples tenants o locaciones con perfiles independientes pero administrados centralmente.
+
+
+
+
+
+
+
+
+ {/* Sección de Contacto e Integración Typography */}
+
+
+
+
+ Ponete en Contacto
+
+
+ ¿Tenés alguna consulta técnica o comercial sobre nuestro servicio? Escribinos.
+
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/public/PrivacyPage.jsx b/frontend/src/views/public/PrivacyPage.jsx
new file mode 100644
index 0000000..b1a92a5
--- /dev/null
+++ b/frontend/src/views/public/PrivacyPage.jsx
@@ -0,0 +1,52 @@
+import { Link } from "react-router-dom";
+import { Separator } from "@/components/ui/separator";
+
+export default function PrivacyPage() {
+ return (
+
+
+
+ ← Volver al Inicio
+
+
+ Políticas de Privacidad
+
+
+ Última actualización: 9 de Junio de 2026
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
+
+
+
+
+ Lorem Ipsum Dolor
+
+
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
Ut enim ad minim veniam, quis nostrud.
+
Ullamco laboris nisi ut aliquip ex ea commodo.
+
Duis aute irure dolor in reprehenderit in voluptate.
+
Velit esse cillum dolore eu fugiat nulla pariatur.
+
+
+
+
+
+ Consectetur Adipiscing
+
+
+ Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec porta diam eu massa. Quisque diam lorem, interdum vitae, dapibus ac, scelerisque vitae, pede.
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/public/TermsPage.jsx b/frontend/src/views/public/TermsPage.jsx
new file mode 100644
index 0000000..cd498b8
--- /dev/null
+++ b/frontend/src/views/public/TermsPage.jsx
@@ -0,0 +1,52 @@
+import { Link } from "react-router-dom";
+import { Separator } from "@/components/ui/separator";
+
+export default function TermsPage() {
+ return (
+
+
+
+ ← Volver al Inicio
+
+
+ Términos de Servicio
+
+
+ Última actualización: 9 de Junio de 2026
+
+
+
+
+
+
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
+
+
+
+
+ Lorem Ipsum Dolor
+
+
+ Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+
+
Ut enim ad minim veniam, quis nostrud.
+
Ullamco laboris nisi ut aliquip ex ea commodo.
+
Duis aute irure dolor in reprehenderit in voluptate.
+
Velit esse cillum dolore eu fugiat nulla pariatur.
+
+
+
+
+
+ Consectetur Adipiscing
+
+
+ Curabitur pretium tincidunt lacus. Nulla gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in mauris eu nibh euismod gravida. Duis ac tellus et risus vulputate vehicula. Donec porta diam eu massa. Quisque diam lorem, interdum vitae, dapibus ac, scelerisque vitae, pede.
+
+
+
+
+ );
+}
diff --git a/frontend/src/views/public/TrackingPage.jsx b/frontend/src/views/public/TrackingPage.jsx
new file mode 100644
index 0000000..3c0f6a2
--- /dev/null
+++ b/frontend/src/views/public/TrackingPage.jsx
@@ -0,0 +1,434 @@
+import { useParams, Link } from "react-router-dom";
+import { useState, useEffect } from "react";
+import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
+import { Badge } from "@/components/ui/badge";
+import { Button } from "@/components/ui/button";
+import { Separator } from "@/components/ui/separator";
+import { Progress } from "@/components/ui/progress";
+import { Skeleton } from "@/components/ui/skeleton";
+import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/components/ui/alert-dialog";
+import { toast } from "sonner";
+
+// Lista de estados en orden secuencial
+const STEPS = [
+ { key: "ingresado", label: "Ingresado", desc: "Equipo recibido" },
+ { key: "diagnostico", label: "En Diagnóstico", desc: "Evaluando fallas" },
+ { key: "presupuestado", label: "Presupuestado", desc: "Costo listo" },
+ { key: "reparacion", label: "En Reparación", desc: "Técnico trabajando" },
+ { key: "listo", label: "Listo para Entregar", desc: "Pruebas superadas" },
+ { key: "entregado", label: "Entregado", desc: "Retirado por cliente" }
+];
+
+export default function TrackingPage() {
+ const { id } = useParams();
+ const [loading, setLoading] = useState(true);
+ const [order, setOrder] = useState(null);
+
+ useEffect(() => {
+ // Simular llamada a la API
+ const timer = setTimeout(() => {
+ if (id === "demo-id") {
+ setOrder({
+ id: "demo-id",
+ nanoId: "RT-2026-ASUS",
+ client: "Edgar Javier Ortiz",
+ dni: "44375912",
+ phone: "+54 381 1234567",
+ email: "edgar.ortiz@gmail.com",
+ device: "Notebook ASUS ROG Strix G15",
+ serial: "SN-98218-ASUS",
+ report: "Calentamiento excesivo (alcanza 95°C) y reinicios aleatorios durante sesiones de renderizado o juegos.",
+ currentStatus: "listo", // Empezar en presupuestado para poder probar el flujo de aprobación
+ dateIngress: "05/06/2026",
+ budget: {
+ items: [
+ { desc: "Mantenimiento térmico preventivo (limpieza + cambio de pasta térmica Arctic MX-6)", price: 15000 },
+ { desc: "Reemplazo de Cooler FAN original (lado izquierdo - fallando rodamientos)", price: 25000 }
+ ],
+ approved: false, // Iniciar sin aprobar para el testing interactivo
+ dateApproved: null
+ },
+ history: [
+ { date: "05/06/2026 10:30", text: "Equipo ingresado al sistema por recepción física." },
+ { date: "06/06/2026 14:15", text: "Diagnóstico finalizado. Se detectó cooler izquierdo trabado y pasta térmica original cristalizada." }
+ ]
+ });
+ } else {
+ // Orden genérica para cualquier otro ID
+ setOrder({
+ id: id,
+ nanoId: id.toUpperCase(),
+ client: "Cliente Registrado",
+ dni: "29.987.654",
+ phone: "+54 381 4567890",
+ email: "cliente.registrado@email.com",
+ device: "Dispositivo Tecnológico Genérico",
+ serial: "SN-PENDIENTE",
+ report: "Diagnóstico inicial pendiente de revisión.",
+ currentStatus: "diagnostico",
+ dateIngress: "08/06/2026",
+ budget: null,
+ history: [
+ { date: "08/06/2026 08:00", text: "Equipo ingresado. En espera de evaluación del departamento técnico." }
+ ]
+ });
+ }
+ setLoading(false);
+ }, 1200); // Demora para visualizar el skeleton de carga
+
+ return () => clearTimeout(timer);
+ }, [id]);
+
+ // Manejo de la aprobación del presupuesto en el cliente
+ const handleApproveBudget = () => {
+ if (!order) return;
+
+ // Simular aprobación
+ setOrder(prev => ({
+ ...prev,
+ currentStatus: "reparacion", // Pasa a reparación al aprobar
+ budget: {
+ ...prev.budget,
+ approved: true,
+ dateApproved: new Date().toLocaleDateString("es-AR")
+ },
+ history: [
+ ...prev.history,
+ {
+ date: new Date().toLocaleString("es-AR"),
+ text: "Presupuesto aprobado de forma online por el cliente. Inicio de trabajos técnicos autorizado."
+ }
+ ]
+ }));
+
+ toast.success("¡Presupuesto Aprobado!", {
+ description: "El departamento técnico ha sido notificado para iniciar la reparación."
+ });
+ };
+
+ // Obtener el índice del estado actual
+ const currentStepIndex = order ? STEPS.findIndex(s => s.key === order.currentStatus) : 0;
+ // Porcentaje del progreso (basado en el índice)
+ const progressPercent = order ? (currentStepIndex / (STEPS.length - 1)) * 100 : 0;
+
+ // Renderizador de Skeleton de carga
+ if (loading) {
+ return (
+
+
+
+
+
+
+
+
+ {/* Skeleton de la Línea de Tiempo */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+ {/* Cabecera del Seguimiento */}
+
+
+
+
+ ← Volver
+
+
+ Orden de Servicio
+
+
+ Seguimiento de Equipo: {order.nanoId}
+
+
+
+
+ {/* LINEA DE TIEMPO DEL ESTADO CON PROGRESS BAR DE SHADCN */}
+
+
+
+
+ {/* Componente Progress nativo de shadcn - Centrado al píxel */}
+
+
+ {/* Ficha de Presupuesto con Card de shadcn */}
+
+
+
+ Presupuesto del Servicio
+
+
+ {order.budget ? (
+
+ {/* Desglose */}
+
+ {order.budget.items.map((item, idx) => (
+
+
{item.desc}
+ ${item.price.toLocaleString("es-AR")}
+
+ ))}
+
+
+ {/* Total */}
+
+ Total Estimado
+
+ ${order.budget.items.reduce((sum, i) => sum + i.price, 0).toLocaleString("es-AR")}
+
+
+
+ {/* Estado de Aprobación mediante Alert de shadcn */}
+ {order.budget.approved ? (
+
+
+ Aprobado
+
+ Autorizado el {order.budget.dateApproved}
+
+
+ ) : (
+
+
+
+ Espera de Aprobación
+
+ Requiere autorización del cliente para iniciar tareas.
+
+
+
+ {/* Modal de Confirmación AlertDialog para Aprobar */}
+
+
+
+
+
+
+
+ ¿Confirmar aprobación del presupuesto?
+
+
+ Al aprobar, autorizás al taller a realizar el recambio de cooler y mantenimiento térmico especificado por un total de ${order.budget.items.reduce((sum, i) => sum + i.price, 0).toLocaleString("es-AR")}.
+
+
+
+ Cancelar
+
+ Confirmar Aprobación
+
+
+
+
+
+ )}
+
+ ) : (
+
+
+ El técnico aún no ha cargado el presupuesto detallado de mano de obra y repuestos.
+