create-huenei-frontend 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-huenei-frontend.js +2 -0
- package/dist/index.js +99 -0
- package/dist/templates/clean/.cursor/skills/architecture/SKILL.md +26 -0
- package/dist/templates/clean/.cursor/skills/components-shadcn/SKILL.md +23 -0
- package/dist/templates/clean/.cursor/skills/daisyui/SKILL.md +98 -0
- package/dist/templates/clean/.cursor/skills/routing/SKILL.md +32 -0
- package/dist/templates/clean/.cursor/skills/testing/SKILL.md +55 -0
- package/dist/templates/clean/.cursor/skills/tooling-and-scripts/SKILL.md +24 -0
- package/dist/templates/clean/.cursor/skills/ui-and-styling/SKILL.md +32 -0
- package/dist/templates/clean/.cursor/skills/vibe-and-ia/SKILL.md +23 -0
- package/dist/templates/clean/.prettierrc +6 -0
- package/dist/templates/clean/.tanstack/tmp/c28007bb-2319511cf318e28129c8d8eb723db5fd +9 -0
- package/dist/templates/clean/.tanstack/tmp/c28007bb-bcef89e9e4593777e1c15ec4b4f32eea +9 -0
- package/dist/templates/clean/.vscode/extensions.json +7 -0
- package/dist/templates/clean/README.md +75 -0
- package/dist/templates/clean/components.json +22 -0
- package/dist/templates/clean/eslint.config.js +31 -0
- package/dist/templates/clean/index.html +13 -0
- package/dist/templates/clean/package.json +62 -0
- package/dist/templates/clean/pnpm-lock.yaml +4487 -0
- package/dist/templates/clean/pnpm-workspace.yaml +2 -0
- package/dist/templates/clean/src/assets/react.svg +1 -0
- package/dist/templates/clean/src/components/ui/badge.tsx +37 -0
- package/dist/templates/clean/src/components/ui/breadcrumb.tsx +106 -0
- package/dist/templates/clean/src/components/ui/button.tsx +58 -0
- package/dist/templates/clean/src/components/ui/card.tsx +72 -0
- package/dist/templates/clean/src/components/ui/form.tsx +165 -0
- package/dist/templates/clean/src/components/ui/input.tsx +22 -0
- package/dist/templates/clean/src/components/ui/label.tsx +24 -0
- package/dist/templates/clean/src/components/ui/password-input.tsx +41 -0
- package/dist/templates/clean/src/components/ui/separator.tsx +28 -0
- package/dist/templates/clean/src/components/ui/tabs.tsx +62 -0
- package/dist/templates/clean/src/components/ui/textarea.tsx +21 -0
- package/dist/templates/clean/src/index.css +104 -0
- package/dist/templates/clean/src/lib/queryClient.ts +11 -0
- package/dist/templates/clean/src/lib/utils.ts +6 -0
- package/dist/templates/clean/src/main.tsx +33 -0
- package/dist/templates/clean/src/routeTree.gen.ts +77 -0
- package/dist/templates/clean/src/router.tsx +13 -0
- package/dist/templates/clean/src/routes/__root.tsx +82 -0
- package/dist/templates/clean/src/routes/index.tsx +187 -0
- package/dist/templates/clean/src/routes/otra-ruta.tsx +319 -0
- package/dist/templates/clean/src/test/setup.ts +1 -0
- package/dist/templates/clean/tsconfig.app.json +34 -0
- package/dist/templates/clean/tsconfig.json +10 -0
- package/dist/templates/clean/tsconfig.node.json +26 -0
- package/dist/templates/clean/vite.config.ts +22 -0
- package/dist/templates/clean/vitest.config.ts +9 -0
- package/dist/templates/help/.cursor/skills/architecture/SKILL.md +26 -0
- package/dist/templates/help/.cursor/skills/components-shadcn/SKILL.md +23 -0
- package/dist/templates/help/.cursor/skills/daisyui/SKILL.md +98 -0
- package/dist/templates/help/.cursor/skills/routing/SKILL.md +32 -0
- package/dist/templates/help/.cursor/skills/testing/SKILL.md +55 -0
- package/dist/templates/help/.cursor/skills/tooling-and-scripts/SKILL.md +24 -0
- package/dist/templates/help/.cursor/skills/ui-and-styling/SKILL.md +32 -0
- package/dist/templates/help/.cursor/skills/vibe-and-ia/SKILL.md +23 -0
- package/dist/templates/help/.prettierrc +6 -0
- package/dist/templates/help/.tanstack/tmp/c28007bb-2319511cf318e28129c8d8eb723db5fd +9 -0
- package/dist/templates/help/.tanstack/tmp/c28007bb-bcef89e9e4593777e1c15ec4b4f32eea +9 -0
- package/dist/templates/help/.vscode/extensions.json +7 -0
- package/dist/templates/help/README.md +75 -0
- package/dist/templates/help/components.json +22 -0
- package/dist/templates/help/eslint.config.js +31 -0
- package/dist/templates/help/index.html +13 -0
- package/dist/templates/help/package.json +62 -0
- package/dist/templates/help/pnpm-lock.yaml +4487 -0
- package/dist/templates/help/pnpm-workspace.yaml +2 -0
- package/dist/templates/help/src/assets/react.svg +1 -0
- package/dist/templates/help/src/components/documentacion/entorno.tsx +99 -0
- package/dist/templates/help/src/components/documentacion/estructura.tsx +100 -0
- package/dist/templates/help/src/components/documentacion/figma-mcp.tsx +200 -0
- package/dist/templates/help/src/components/documentacion/inicio.tsx +69 -0
- package/dist/templates/help/src/components/documentacion/instalacion.tsx +72 -0
- package/dist/templates/help/src/components/documentacion/personalizacion.tsx +163 -0
- package/dist/templates/help/src/components/documentacion/rutas.tsx +138 -0
- package/dist/templates/help/src/components/documentacion/stack.tsx +401 -0
- package/dist/templates/help/src/components/ui/badge.tsx +37 -0
- package/dist/templates/help/src/components/ui/breadcrumb.tsx +106 -0
- package/dist/templates/help/src/components/ui/button.tsx +58 -0
- package/dist/templates/help/src/components/ui/card.tsx +72 -0
- package/dist/templates/help/src/components/ui/form.tsx +165 -0
- package/dist/templates/help/src/components/ui/input.tsx +22 -0
- package/dist/templates/help/src/components/ui/label.tsx +24 -0
- package/dist/templates/help/src/components/ui/password-input.tsx +41 -0
- package/dist/templates/help/src/components/ui/separator.tsx +28 -0
- package/dist/templates/help/src/components/ui/tabs.tsx +62 -0
- package/dist/templates/help/src/components/ui/textarea.tsx +21 -0
- package/dist/templates/help/src/data/proyectos.json +56 -0
- package/dist/templates/help/src/index.css +104 -0
- package/dist/templates/help/src/lib/queryClient.ts +11 -0
- package/dist/templates/help/src/lib/utils.ts +6 -0
- package/dist/templates/help/src/main.tsx +33 -0
- package/dist/templates/help/src/routeTree.gen.ts +241 -0
- package/dist/templates/help/src/router.tsx +13 -0
- package/dist/templates/help/src/routes/__root.tsx +92 -0
- package/dist/templates/help/src/routes/auth/iniciar-sesion.tsx +139 -0
- package/dist/templates/help/src/routes/auth/registro.tsx +205 -0
- package/dist/templates/help/src/routes/contacto.tsx +60 -0
- package/dist/templates/help/src/routes/dashbord.tsx +9 -0
- package/dist/templates/help/src/routes/documentacion.tsx +80 -0
- package/dist/templates/help/src/routes/index.test.tsx +42 -0
- package/dist/templates/help/src/routes/index.tsx +211 -0
- package/dist/templates/help/src/routes/perfil.tsx +9 -0
- package/dist/templates/help/src/routes/proyectos.$proyectoId.tsx +156 -0
- package/dist/templates/help/src/routes/proyectos.tsx +93 -0
- package/dist/templates/help/src/test/setup.ts +1 -0
- package/dist/templates/help/tsconfig.app.json +34 -0
- package/dist/templates/help/tsconfig.json +10 -0
- package/dist/templates/help/tsconfig.node.json +26 -0
- package/dist/templates/help/vite.config.ts +22 -0
- package/dist/templates/help/vitest.config.ts +9 -0
- package/dist/test-cli.js +48 -0
- package/package.json +25 -0
- package/templates/clean/.cursor/skills/architecture/SKILL.md +26 -0
- package/templates/clean/.cursor/skills/components-shadcn/SKILL.md +23 -0
- package/templates/clean/.cursor/skills/daisyui/SKILL.md +98 -0
- package/templates/clean/.cursor/skills/routing/SKILL.md +32 -0
- package/templates/clean/.cursor/skills/testing/SKILL.md +55 -0
- package/templates/clean/.cursor/skills/tooling-and-scripts/SKILL.md +24 -0
- package/templates/clean/.cursor/skills/ui-and-styling/SKILL.md +32 -0
- package/templates/clean/.cursor/skills/vibe-and-ia/SKILL.md +23 -0
- package/templates/clean/.prettierrc +6 -0
- package/templates/clean/.tanstack/tmp/c28007bb-2319511cf318e28129c8d8eb723db5fd +9 -0
- package/templates/clean/.tanstack/tmp/c28007bb-bcef89e9e4593777e1c15ec4b4f32eea +9 -0
- package/templates/clean/.vscode/extensions.json +7 -0
- package/templates/clean/README.md +75 -0
- package/templates/clean/components.json +22 -0
- package/templates/clean/eslint.config.js +31 -0
- package/templates/clean/index.html +13 -0
- package/templates/clean/package.json +62 -0
- package/templates/clean/pnpm-lock.yaml +4487 -0
- package/templates/clean/pnpm-workspace.yaml +2 -0
- package/templates/clean/src/assets/react.svg +1 -0
- package/templates/clean/src/components/ui/badge.tsx +37 -0
- package/templates/clean/src/components/ui/breadcrumb.tsx +106 -0
- package/templates/clean/src/components/ui/button.tsx +58 -0
- package/templates/clean/src/components/ui/card.tsx +72 -0
- package/templates/clean/src/components/ui/form.tsx +165 -0
- package/templates/clean/src/components/ui/input.tsx +22 -0
- package/templates/clean/src/components/ui/label.tsx +24 -0
- package/templates/clean/src/components/ui/password-input.tsx +41 -0
- package/templates/clean/src/components/ui/separator.tsx +28 -0
- package/templates/clean/src/components/ui/tabs.tsx +62 -0
- package/templates/clean/src/components/ui/textarea.tsx +21 -0
- package/templates/clean/src/index.css +104 -0
- package/templates/clean/src/lib/queryClient.ts +11 -0
- package/templates/clean/src/lib/utils.ts +6 -0
- package/templates/clean/src/main.tsx +33 -0
- package/templates/clean/src/routeTree.gen.ts +77 -0
- package/templates/clean/src/router.tsx +13 -0
- package/templates/clean/src/routes/__root.tsx +82 -0
- package/templates/clean/src/routes/index.tsx +187 -0
- package/templates/clean/src/routes/otra-ruta.tsx +319 -0
- package/templates/clean/src/test/setup.ts +1 -0
- package/templates/clean/tsconfig.app.json +34 -0
- package/templates/clean/tsconfig.json +10 -0
- package/templates/clean/tsconfig.node.json +26 -0
- package/templates/clean/vite.config.ts +22 -0
- package/templates/clean/vitest.config.ts +9 -0
- package/templates/help/.cursor/skills/architecture/SKILL.md +26 -0
- package/templates/help/.cursor/skills/components-shadcn/SKILL.md +23 -0
- package/templates/help/.cursor/skills/daisyui/SKILL.md +98 -0
- package/templates/help/.cursor/skills/routing/SKILL.md +32 -0
- package/templates/help/.cursor/skills/testing/SKILL.md +55 -0
- package/templates/help/.cursor/skills/tooling-and-scripts/SKILL.md +24 -0
- package/templates/help/.cursor/skills/ui-and-styling/SKILL.md +32 -0
- package/templates/help/.cursor/skills/vibe-and-ia/SKILL.md +23 -0
- package/templates/help/.prettierrc +6 -0
- package/templates/help/.tanstack/tmp/c28007bb-2319511cf318e28129c8d8eb723db5fd +9 -0
- package/templates/help/.tanstack/tmp/c28007bb-bcef89e9e4593777e1c15ec4b4f32eea +9 -0
- package/templates/help/.vscode/extensions.json +7 -0
- package/templates/help/README.md +75 -0
- package/templates/help/components.json +22 -0
- package/templates/help/eslint.config.js +31 -0
- package/templates/help/index.html +13 -0
- package/templates/help/package.json +62 -0
- package/templates/help/pnpm-lock.yaml +4487 -0
- package/templates/help/pnpm-workspace.yaml +2 -0
- package/templates/help/src/assets/react.svg +1 -0
- package/templates/help/src/components/documentacion/entorno.tsx +99 -0
- package/templates/help/src/components/documentacion/estructura.tsx +100 -0
- package/templates/help/src/components/documentacion/figma-mcp.tsx +200 -0
- package/templates/help/src/components/documentacion/inicio.tsx +69 -0
- package/templates/help/src/components/documentacion/instalacion.tsx +72 -0
- package/templates/help/src/components/documentacion/personalizacion.tsx +163 -0
- package/templates/help/src/components/documentacion/rutas.tsx +138 -0
- package/templates/help/src/components/documentacion/stack.tsx +401 -0
- package/templates/help/src/components/ui/badge.tsx +37 -0
- package/templates/help/src/components/ui/breadcrumb.tsx +106 -0
- package/templates/help/src/components/ui/button.tsx +58 -0
- package/templates/help/src/components/ui/card.tsx +72 -0
- package/templates/help/src/components/ui/form.tsx +165 -0
- package/templates/help/src/components/ui/input.tsx +22 -0
- package/templates/help/src/components/ui/label.tsx +24 -0
- package/templates/help/src/components/ui/password-input.tsx +41 -0
- package/templates/help/src/components/ui/separator.tsx +28 -0
- package/templates/help/src/components/ui/tabs.tsx +62 -0
- package/templates/help/src/components/ui/textarea.tsx +21 -0
- package/templates/help/src/data/proyectos.json +56 -0
- package/templates/help/src/index.css +104 -0
- package/templates/help/src/lib/queryClient.ts +11 -0
- package/templates/help/src/lib/utils.ts +6 -0
- package/templates/help/src/main.tsx +33 -0
- package/templates/help/src/routeTree.gen.ts +241 -0
- package/templates/help/src/router.tsx +13 -0
- package/templates/help/src/routes/__root.tsx +92 -0
- package/templates/help/src/routes/auth/iniciar-sesion.tsx +139 -0
- package/templates/help/src/routes/auth/registro.tsx +205 -0
- package/templates/help/src/routes/contacto.tsx +60 -0
- package/templates/help/src/routes/dashbord.tsx +9 -0
- package/templates/help/src/routes/documentacion.tsx +80 -0
- package/templates/help/src/routes/index.test.tsx +42 -0
- package/templates/help/src/routes/index.tsx +211 -0
- package/templates/help/src/routes/perfil.tsx +9 -0
- package/templates/help/src/routes/proyectos.$proyectoId.tsx +156 -0
- package/templates/help/src/routes/proyectos.tsx +93 -0
- package/templates/help/src/test/setup.ts +1 -0
- package/templates/help/tsconfig.app.json +34 -0
- package/templates/help/tsconfig.json +10 -0
- package/templates/help/tsconfig.node.json +26 -0
- package/templates/help/vite.config.ts +22 -0
- package/templates/help/vitest.config.ts +9 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { toast } from "react-toastify";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
4
|
+
import { Input } from "@/components/ui/input";
|
|
5
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
6
|
+
import { Label } from "@/components/ui/label";
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
|
|
9
|
+
export const Route = createFileRoute("/contacto")({
|
|
10
|
+
component: Contacto,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function Contacto() {
|
|
14
|
+
const handleSubmit = () => {
|
|
15
|
+
toast.success("Mensaje enviado correctamente", { autoClose: 2500 });
|
|
16
|
+
};
|
|
17
|
+
return (
|
|
18
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
19
|
+
<div className="text-center mb-16">
|
|
20
|
+
<h2 className="text-5xl font-bold text-base-content mb-6">Contacto</h2>
|
|
21
|
+
<p className="text-xl text-base-content/70 max-w-2xl mx-auto">
|
|
22
|
+
Ponte en contacto con el equipo de Huenei.
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div className="max-w-2xl mx-auto">
|
|
27
|
+
<Card>
|
|
28
|
+
<CardHeader>
|
|
29
|
+
<CardTitle>Envíanos un mensaje</CardTitle>
|
|
30
|
+
<CardDescription>
|
|
31
|
+
Completa el formulario y nos pondremos en contacto contigo lo antes posible.
|
|
32
|
+
</CardDescription>
|
|
33
|
+
</CardHeader>
|
|
34
|
+
<CardContent>
|
|
35
|
+
<form className="space-y-6">
|
|
36
|
+
<div className="space-y-2">
|
|
37
|
+
<Label htmlFor="nombre">Nombre</Label>
|
|
38
|
+
<Input type="text" id="nombre" name="nombre" placeholder="Tu nombre" />
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div className="space-y-2">
|
|
42
|
+
<Label htmlFor="email">Email</Label>
|
|
43
|
+
<Input type="email" id="email" name="email" placeholder="tu@email.com" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="space-y-2">
|
|
47
|
+
<Label htmlFor="mensaje">Mensaje</Label>
|
|
48
|
+
<Textarea id="mensaje" name="mensaje" rows={6} placeholder="Tu mensaje..." />
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<Button type="button" className="w-full" size="lg" onClick={handleSubmit}>
|
|
52
|
+
Enviar Mensaje
|
|
53
|
+
</Button>
|
|
54
|
+
</form>
|
|
55
|
+
</CardContent>
|
|
56
|
+
</Card>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
3
|
+
import { InicioTab } from "@/components/documentacion/inicio";
|
|
4
|
+
import { EntornoTab } from "@/components/documentacion/entorno";
|
|
5
|
+
import { FigmaMcpTab } from "@/components/documentacion/figma-mcp";
|
|
6
|
+
import { InstalacionTab } from "@/components/documentacion/instalacion";
|
|
7
|
+
import { EstructuraTab } from "@/components/documentacion/estructura";
|
|
8
|
+
import { StackTab } from "@/components/documentacion/stack";
|
|
9
|
+
import { RutasTab } from "@/components/documentacion/rutas";
|
|
10
|
+
import { PersonalizacionTab } from "@/components/documentacion/personalizacion";
|
|
11
|
+
|
|
12
|
+
export const Route = createFileRoute("/documentacion")({
|
|
13
|
+
component: Documentacion,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
function Documentacion() {
|
|
17
|
+
return (
|
|
18
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
19
|
+
<div className="text-center mb-16">
|
|
20
|
+
<h2 className="text-5xl font-bold text-slate-900 mb-6">Documentación</h2>
|
|
21
|
+
<p className="text-xl text-slate-600 max-w-2xl mx-auto">
|
|
22
|
+
Guía completa para utilizar y personalizar este template base de frontend.
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<Tabs defaultValue="inicio" className="w-full">
|
|
27
|
+
<TabsList className="grid w-full grid-cols-2 md:grid-cols-3 lg:grid-cols-8">
|
|
28
|
+
<TabsTrigger value="inicio">Inicio</TabsTrigger>
|
|
29
|
+
<TabsTrigger value="entorno">Entorno</TabsTrigger>
|
|
30
|
+
<TabsTrigger value="figma-mcp">Figma MCP</TabsTrigger>
|
|
31
|
+
<TabsTrigger value="instalacion">Instalación</TabsTrigger>
|
|
32
|
+
<TabsTrigger value="estructura">Estructura</TabsTrigger>
|
|
33
|
+
<TabsTrigger value="stack">Stack</TabsTrigger>
|
|
34
|
+
<TabsTrigger value="rutas">Rutas</TabsTrigger>
|
|
35
|
+
<TabsTrigger value="personalizacion">Personalización</TabsTrigger>
|
|
36
|
+
</TabsList>
|
|
37
|
+
|
|
38
|
+
{/* Sección: Inicio */}
|
|
39
|
+
<TabsContent value="inicio" className="space-y-6">
|
|
40
|
+
<InicioTab />
|
|
41
|
+
</TabsContent>
|
|
42
|
+
|
|
43
|
+
{/* Sección: Entorno de Desarrollo */}
|
|
44
|
+
<TabsContent value="entorno" className="space-y-6">
|
|
45
|
+
<EntornoTab />
|
|
46
|
+
</TabsContent>
|
|
47
|
+
|
|
48
|
+
{/* Sección: Figma MCP */}
|
|
49
|
+
<TabsContent value="figma-mcp" className="space-y-6">
|
|
50
|
+
<FigmaMcpTab />
|
|
51
|
+
</TabsContent>
|
|
52
|
+
|
|
53
|
+
{/* Sección: Instalación */}
|
|
54
|
+
<TabsContent value="instalacion" className="space-y-6">
|
|
55
|
+
<InstalacionTab />
|
|
56
|
+
</TabsContent>
|
|
57
|
+
|
|
58
|
+
{/* Sección: Estructura */}
|
|
59
|
+
<TabsContent value="estructura" className="space-y-6">
|
|
60
|
+
<EstructuraTab />
|
|
61
|
+
</TabsContent>
|
|
62
|
+
|
|
63
|
+
{/* Sección: Stack Tecnológico */}
|
|
64
|
+
<TabsContent value="stack" className="space-y-6">
|
|
65
|
+
<StackTab />
|
|
66
|
+
</TabsContent>
|
|
67
|
+
|
|
68
|
+
{/* Sección: Rutas */}
|
|
69
|
+
<TabsContent value="rutas" className="space-y-6">
|
|
70
|
+
<RutasTab />
|
|
71
|
+
</TabsContent>
|
|
72
|
+
|
|
73
|
+
{/* Sección: Personalización */}
|
|
74
|
+
<TabsContent value="personalizacion" className="space-y-6">
|
|
75
|
+
<PersonalizacionTab />
|
|
76
|
+
</TabsContent>
|
|
77
|
+
</Tabs>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { render, screen } from '@testing-library/react'
|
|
3
|
+
import { Route } from './index'
|
|
4
|
+
|
|
5
|
+
// Extraer el componente del Route para poder probarlo
|
|
6
|
+
const IndexComponent = Route.options.component
|
|
7
|
+
|
|
8
|
+
describe('Index Page', () => {
|
|
9
|
+
it('should render the main heading', () => {
|
|
10
|
+
render(<IndexComponent />)
|
|
11
|
+
|
|
12
|
+
const heading = screen.getByText('Template Base Frontend')
|
|
13
|
+
expect(heading).toBeInTheDocument()
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should render the description text', () => {
|
|
17
|
+
render(<IndexComponent />)
|
|
18
|
+
|
|
19
|
+
const description = screen.getByText(
|
|
20
|
+
/Plataforma de desarrollo base para proyectos de frontend de Huenei/
|
|
21
|
+
)
|
|
22
|
+
expect(description).toBeInTheDocument()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should render all feature cards', () => {
|
|
26
|
+
render(<IndexComponent />)
|
|
27
|
+
|
|
28
|
+
expect(screen.getByText('TypeScript')).toBeInTheDocument()
|
|
29
|
+
expect(screen.getByText('Tailwind CSS')).toBeInTheDocument()
|
|
30
|
+
expect(screen.getByText('Vite')).toBeInTheDocument()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should render the CTA section', () => {
|
|
34
|
+
render(<IndexComponent />)
|
|
35
|
+
|
|
36
|
+
const ctaHeading = screen.getByText('¿Listo para comenzar?')
|
|
37
|
+
expect(ctaHeading).toBeInTheDocument()
|
|
38
|
+
|
|
39
|
+
const ctaButton = screen.getByText('Comenzar Proyecto')
|
|
40
|
+
expect(ctaButton).toBeInTheDocument()
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
3
|
+
import { Button } from "@/components/ui/button";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import {
|
|
6
|
+
Code,
|
|
7
|
+
Palette,
|
|
8
|
+
Zap,
|
|
9
|
+
Database,
|
|
10
|
+
FileText,
|
|
11
|
+
Route as RouteIcon,
|
|
12
|
+
Settings,
|
|
13
|
+
BookOpen,
|
|
14
|
+
} from "lucide-react";
|
|
15
|
+
|
|
16
|
+
export const Route = createFileRoute("/")({
|
|
17
|
+
component: Index,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function Index() {
|
|
21
|
+
return (
|
|
22
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
23
|
+
<div className="text-center mb-16">
|
|
24
|
+
<h2 className="text-5xl font-bold text-base-content mb-6">Huenei Frontend</h2>
|
|
25
|
+
<p className="text-xl text-base-content/70 max-w-2xl mx-auto mb-6">
|
|
26
|
+
Plantilla estandarizada para los proyectos de frontend
|
|
27
|
+
</p>
|
|
28
|
+
<div className="flex flex-wrap justify-center gap-2 mb-6">
|
|
29
|
+
<Badge variant="outline">Vite</Badge>
|
|
30
|
+
<Badge variant="outline">React 19</Badge>
|
|
31
|
+
<Badge variant="outline">TypeScript</Badge>
|
|
32
|
+
<Badge variant="outline">Zod</Badge>
|
|
33
|
+
<Badge variant="outline">Tailwind CSS v4</Badge>
|
|
34
|
+
<Badge variant="outline">TanStack Query</Badge>
|
|
35
|
+
<Badge variant="outline">TanStack Router</Badge>
|
|
36
|
+
<Badge variant="outline">shadcn/ui</Badge>
|
|
37
|
+
<Badge variant="outline">Vitest</Badge>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{/* CTA Section */}
|
|
42
|
+
<Card className="bg-info border-0 text-primary-content mb-8">
|
|
43
|
+
<CardContent className="pt-8 pb-8 text-center">
|
|
44
|
+
<h3 className="text-3xl font-bold mb-4">¿Listo para comenzar?</h3>
|
|
45
|
+
<p className="text-primary-content/90 mb-6 max-w-2xl mx-auto">
|
|
46
|
+
Este template está listo para ser personalizado según las necesidades de tu proyecto.
|
|
47
|
+
Consulta la documentación completa para conocer todas las características y mejores
|
|
48
|
+
prácticas.
|
|
49
|
+
</p>
|
|
50
|
+
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
|
51
|
+
<Link to="/documentacion">
|
|
52
|
+
<Button
|
|
53
|
+
size="default"
|
|
54
|
+
variant="ghost"
|
|
55
|
+
className="bg-base-100 text-primary hover:bg-base-200 w-full sm:w-auto"
|
|
56
|
+
>
|
|
57
|
+
<BookOpen className="w-4 h-4 mr-2" />
|
|
58
|
+
Ver Documentación
|
|
59
|
+
</Button>
|
|
60
|
+
</Link>
|
|
61
|
+
</div>
|
|
62
|
+
</CardContent>
|
|
63
|
+
</Card>
|
|
64
|
+
|
|
65
|
+
{/* Features Grid */}
|
|
66
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-6 mb-16">
|
|
67
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
68
|
+
<CardHeader>
|
|
69
|
+
<div className="w-12 h-12 bg-primary/10 rounded-lg flex items-center justify-center mb-4">
|
|
70
|
+
<Zap className="w-6 h-6 text-primary" />
|
|
71
|
+
</div>
|
|
72
|
+
<CardTitle>Vite</CardTitle>
|
|
73
|
+
<CardDescription>
|
|
74
|
+
Build tool ultra-rápido con HMR instantáneo y build optimizado con rolldown.
|
|
75
|
+
</CardDescription>
|
|
76
|
+
</CardHeader>
|
|
77
|
+
</Card>
|
|
78
|
+
|
|
79
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
80
|
+
<CardHeader>
|
|
81
|
+
<div className="w-12 h-12 bg-accent/10 rounded-lg flex items-center justify-center mb-4">
|
|
82
|
+
<Code className="w-6 h-6 text-accent" />
|
|
83
|
+
</div>
|
|
84
|
+
<CardTitle>React 19 + TypeScript</CardTitle>
|
|
85
|
+
<CardDescription>
|
|
86
|
+
Framework moderno con React Compiler habilitado y type safety estricto.
|
|
87
|
+
</CardDescription>
|
|
88
|
+
</CardHeader>
|
|
89
|
+
</Card>
|
|
90
|
+
|
|
91
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
92
|
+
<CardHeader>
|
|
93
|
+
<div className="w-12 h-12 bg-info/10 rounded-lg flex items-center justify-center mb-4">
|
|
94
|
+
<Database className="w-6 h-6 text-info" />
|
|
95
|
+
</div>
|
|
96
|
+
<CardTitle>TanStack Query</CardTitle>
|
|
97
|
+
<CardDescription>
|
|
98
|
+
Manejo de estado del servidor, caché y sincronización de datos con DevTools.
|
|
99
|
+
</CardDescription>
|
|
100
|
+
</CardHeader>
|
|
101
|
+
</Card>
|
|
102
|
+
|
|
103
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
104
|
+
<CardHeader>
|
|
105
|
+
<div className="w-12 h-12 bg-success/10 rounded-lg flex items-center justify-center mb-4">
|
|
106
|
+
<RouteIcon className="w-6 h-6 text-success" />
|
|
107
|
+
</div>
|
|
108
|
+
<CardTitle>TanStack Router</CardTitle>
|
|
109
|
+
<CardDescription>
|
|
110
|
+
Enrutamiento type-safe con file-based routing y preload automático.
|
|
111
|
+
</CardDescription>
|
|
112
|
+
</CardHeader>
|
|
113
|
+
</Card>
|
|
114
|
+
|
|
115
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
116
|
+
<CardHeader>
|
|
117
|
+
<div className="w-12 h-12 bg-warning/10 rounded-lg flex items-center justify-center mb-4">
|
|
118
|
+
<Palette className="w-6 h-6 text-warning" />
|
|
119
|
+
</div>
|
|
120
|
+
<CardTitle>Tailwind CSS v4</CardTitle>
|
|
121
|
+
<CardDescription>
|
|
122
|
+
Framework CSS utility-first con configuración moderna usando @theme.
|
|
123
|
+
</CardDescription>
|
|
124
|
+
</CardHeader>
|
|
125
|
+
</Card>
|
|
126
|
+
|
|
127
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
128
|
+
<CardHeader>
|
|
129
|
+
<div className="w-12 h-12 bg-secondary/10 rounded-lg flex items-center justify-center mb-4">
|
|
130
|
+
<Code className="w-6 h-6 text-secondary" />
|
|
131
|
+
</div>
|
|
132
|
+
<CardTitle>shadcn/ui</CardTitle>
|
|
133
|
+
<CardDescription>
|
|
134
|
+
Componentes UI reutilizables construidos con Radix UI y Tailwind CSS.
|
|
135
|
+
</CardDescription>
|
|
136
|
+
</CardHeader>
|
|
137
|
+
</Card>
|
|
138
|
+
|
|
139
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
140
|
+
<CardHeader>
|
|
141
|
+
<div className="w-12 h-12 bg-error/10 rounded-lg flex items-center justify-center mb-4">
|
|
142
|
+
<FileText className="w-6 h-6 text-error" />
|
|
143
|
+
</div>
|
|
144
|
+
<CardTitle>Zod</CardTitle>
|
|
145
|
+
<CardDescription>
|
|
146
|
+
Validación de esquemas TypeScript-first con integración para React Hook Form.
|
|
147
|
+
</CardDescription>
|
|
148
|
+
</CardHeader>
|
|
149
|
+
</Card>
|
|
150
|
+
|
|
151
|
+
<Card className="hover:shadow-lg transition-shadow">
|
|
152
|
+
<CardHeader>
|
|
153
|
+
<div className="w-12 h-12 bg-neutral/10 rounded-lg flex items-center justify-center mb-4">
|
|
154
|
+
<Settings className="w-6 h-6 text-neutral" />
|
|
155
|
+
</div>
|
|
156
|
+
<CardTitle>Vitest</CardTitle>
|
|
157
|
+
<CardDescription>
|
|
158
|
+
Testing rápido con React Testing Library para componentes y lógica.
|
|
159
|
+
</CardDescription>
|
|
160
|
+
</CardHeader>
|
|
161
|
+
</Card>
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
{/* Quick Start Section */}
|
|
165
|
+
<div className="grid md:grid-cols-2 gap-6 mb-16">
|
|
166
|
+
<Card>
|
|
167
|
+
<CardHeader>
|
|
168
|
+
<CardTitle>Requisitos previos</CardTitle>
|
|
169
|
+
<CardDescription>Lo que necesitas para comenzar con este template</CardDescription>
|
|
170
|
+
</CardHeader>
|
|
171
|
+
<CardContent>
|
|
172
|
+
<ul className="list-disc list-inside space-y-2 text-base-content/70">
|
|
173
|
+
<li>Node.js 18+</li>
|
|
174
|
+
<li>pnpm</li>
|
|
175
|
+
<li>Conocimientos básicos de React y TypeScript</li>
|
|
176
|
+
<li>
|
|
177
|
+
<strong className="text-base-content">Cursor IDE</strong> (recomendado para mejor
|
|
178
|
+
experiencia de desarrollo)
|
|
179
|
+
</li>
|
|
180
|
+
</ul>
|
|
181
|
+
</CardContent>
|
|
182
|
+
</Card>
|
|
183
|
+
|
|
184
|
+
<Card>
|
|
185
|
+
<CardHeader>
|
|
186
|
+
<CardTitle>Comandos rápidos</CardTitle>
|
|
187
|
+
<CardDescription>Scripts principales para desarrollo y producción</CardDescription>
|
|
188
|
+
</CardHeader>
|
|
189
|
+
<CardContent className="space-y-3">
|
|
190
|
+
<h3 className="text-base font-semibold mb-3">Instalar dependencias</h3>
|
|
191
|
+
<div className="bg-base-200 rounded-lg p-3 font-mono text-sm text-base-content">
|
|
192
|
+
<code>pnpm install</code>
|
|
193
|
+
</div>
|
|
194
|
+
<h3 className="text-base font-semibold mb-3">Ejecutar en desarrollo</h3>
|
|
195
|
+
<div className="bg-base-200 rounded-lg p-3 font-mono text-sm text-base-content">
|
|
196
|
+
<code>pnpm dev</code>
|
|
197
|
+
</div>
|
|
198
|
+
<h3 className="text-base font-semibold mb-3">Build para producción</h3>
|
|
199
|
+
<div className="bg-base-200 rounded-lg p-3 font-mono text-sm text-base-content">
|
|
200
|
+
<code>pnpm build</code>
|
|
201
|
+
</div>
|
|
202
|
+
<h3 className="text-base font-semibold mb-3">Ejecutar tests</h3>
|
|
203
|
+
<div className="bg-base-200 rounded-lg p-3 font-mono text-sm text-base-content">
|
|
204
|
+
<code>pnpm test</code>
|
|
205
|
+
</div>
|
|
206
|
+
</CardContent>
|
|
207
|
+
</Card>
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
2
|
+
import proyectosData from "../data/proyectos.json";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import {
|
|
7
|
+
Breadcrumb,
|
|
8
|
+
BreadcrumbItem,
|
|
9
|
+
BreadcrumbLink,
|
|
10
|
+
BreadcrumbList,
|
|
11
|
+
BreadcrumbPage,
|
|
12
|
+
BreadcrumbSeparator,
|
|
13
|
+
} from "@/components/ui/breadcrumb";
|
|
14
|
+
import { Separator } from "@/components/ui/separator";
|
|
15
|
+
import { ArrowLeft } from "lucide-react";
|
|
16
|
+
|
|
17
|
+
export const Route = createFileRoute("/proyectos/$proyectoId")({
|
|
18
|
+
component: ProyectoDetalle,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
interface Proyecto {
|
|
22
|
+
id: number;
|
|
23
|
+
titulo: string;
|
|
24
|
+
descripcion: string;
|
|
25
|
+
tecnologias: string[];
|
|
26
|
+
imagen: string;
|
|
27
|
+
estado: string;
|
|
28
|
+
fecha: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function ProyectoDetalle() {
|
|
32
|
+
const { proyectoId } = Route.useParams();
|
|
33
|
+
const proyecto = proyectosData.find((p) => p.id === Number(proyectoId)) as Proyecto | undefined;
|
|
34
|
+
|
|
35
|
+
if (!proyecto) {
|
|
36
|
+
return (
|
|
37
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
38
|
+
<Card>
|
|
39
|
+
<CardContent className="pt-8 pb-8 text-center">
|
|
40
|
+
<h1 className="text-4xl font-bold text-base-content mb-4">Proyecto no encontrado</h1>
|
|
41
|
+
<p className="text-base-content/70 mb-8">El proyecto que buscas no existe.</p>
|
|
42
|
+
<Button asChild variant="outline">
|
|
43
|
+
<Link to="/proyectos">
|
|
44
|
+
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
45
|
+
Volver a Proyectos
|
|
46
|
+
</Link>
|
|
47
|
+
</Button>
|
|
48
|
+
</CardContent>
|
|
49
|
+
</Card>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
56
|
+
{/* Breadcrumb */}
|
|
57
|
+
<Breadcrumb className="mb-8">
|
|
58
|
+
<BreadcrumbList>
|
|
59
|
+
<BreadcrumbItem>
|
|
60
|
+
<BreadcrumbLink asChild>
|
|
61
|
+
<Link to="/proyectos">Proyectos</Link>
|
|
62
|
+
</BreadcrumbLink>
|
|
63
|
+
</BreadcrumbItem>
|
|
64
|
+
<BreadcrumbItem>
|
|
65
|
+
<BreadcrumbPage>{proyecto.titulo}</BreadcrumbPage>
|
|
66
|
+
</BreadcrumbItem>
|
|
67
|
+
</BreadcrumbList>
|
|
68
|
+
</Breadcrumb>
|
|
69
|
+
|
|
70
|
+
{/* Botón Volver */}
|
|
71
|
+
<Button asChild variant="ghost" className="mb-8">
|
|
72
|
+
<Link to="/proyectos">
|
|
73
|
+
<ArrowLeft className="mr-2 h-4 w-4" />
|
|
74
|
+
Volver a Proyectos
|
|
75
|
+
</Link>
|
|
76
|
+
</Button>
|
|
77
|
+
|
|
78
|
+
{/* Contenido Principal */}
|
|
79
|
+
<Card className="overflow-hidden">
|
|
80
|
+
{/* Información del Proyecto */}
|
|
81
|
+
<CardHeader>
|
|
82
|
+
<div className="h-96 bg-base-200 overflow-hidden">
|
|
83
|
+
<img
|
|
84
|
+
src={proyecto.imagen}
|
|
85
|
+
alt={proyecto.titulo}
|
|
86
|
+
className="w-full h-full object-cover rounded-md"
|
|
87
|
+
/>
|
|
88
|
+
</div>
|
|
89
|
+
<div className="flex items-center gap-4 mb-4">
|
|
90
|
+
<Badge
|
|
91
|
+
variant={proyecto.estado === "En Producción" ? "default" : "secondary"}
|
|
92
|
+
className={proyecto.estado === "En Producción" ? "badge-success" : "badge-warning"}
|
|
93
|
+
>
|
|
94
|
+
{proyecto.estado}
|
|
95
|
+
</Badge>
|
|
96
|
+
<span className="text-base-content/70 text-sm">
|
|
97
|
+
Fecha de inicio:{" "}
|
|
98
|
+
<span className="font-medium">
|
|
99
|
+
{new Date(proyecto.fecha).toLocaleDateString("es-ES", {
|
|
100
|
+
year: "numeric",
|
|
101
|
+
month: "long",
|
|
102
|
+
day: "numeric",
|
|
103
|
+
})}
|
|
104
|
+
</span>
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
<CardTitle className="text-4xl">{proyecto.titulo}</CardTitle>
|
|
108
|
+
</CardHeader>
|
|
109
|
+
|
|
110
|
+
<CardContent className="space-y-8">
|
|
111
|
+
{/* Descripción */}
|
|
112
|
+
<div>
|
|
113
|
+
<h2 className="text-2xl font-semibold text-base-content mb-4">Descripción</h2>
|
|
114
|
+
<CardDescription className="text-lg leading-relaxed">
|
|
115
|
+
{proyecto.descripcion}
|
|
116
|
+
</CardDescription>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
{/* Tecnologías */}
|
|
120
|
+
<div>
|
|
121
|
+
<h2 className="text-2xl font-semibold text-base-content mb-4">
|
|
122
|
+
Tecnologías Utilizadas
|
|
123
|
+
</h2>
|
|
124
|
+
<div className="flex flex-wrap gap-3">
|
|
125
|
+
{proyecto.tecnologias.map((tech, index) => (
|
|
126
|
+
<Badge key={index} variant="default" className="text-sm">
|
|
127
|
+
{tech}
|
|
128
|
+
</Badge>
|
|
129
|
+
))}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<Separator />
|
|
134
|
+
|
|
135
|
+
{/* Información Adicional */}
|
|
136
|
+
<div className="grid md:grid-cols-2 gap-6">
|
|
137
|
+
<div>
|
|
138
|
+
<h3 className="text-lg font-semibold text-base-content mb-2">Estado del Proyecto</h3>
|
|
139
|
+
<p className="text-base-content/70">{proyecto.estado}</p>
|
|
140
|
+
</div>
|
|
141
|
+
<div>
|
|
142
|
+
<h3 className="text-lg font-semibold text-base-content mb-2">Fecha de Inicio</h3>
|
|
143
|
+
<p className="text-base-content/70">
|
|
144
|
+
{new Date(proyecto.fecha).toLocaleDateString("es-ES", {
|
|
145
|
+
year: "numeric",
|
|
146
|
+
month: "long",
|
|
147
|
+
day: "numeric",
|
|
148
|
+
})}
|
|
149
|
+
</p>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</CardContent>
|
|
153
|
+
</Card>
|
|
154
|
+
</div>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { createFileRoute, Link, Outlet, useMatches } from "@tanstack/react-router";
|
|
2
|
+
import proyectosData from "../data/proyectos.json";
|
|
3
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute("/proyectos")({
|
|
7
|
+
component: Proyectos,
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
interface Proyecto {
|
|
11
|
+
id: number;
|
|
12
|
+
titulo: string;
|
|
13
|
+
descripcion: string;
|
|
14
|
+
tecnologias: string[];
|
|
15
|
+
imagen: string;
|
|
16
|
+
estado: string;
|
|
17
|
+
fecha: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function Proyectos() {
|
|
21
|
+
const proyectos = proyectosData as Proyecto[];
|
|
22
|
+
const matches = useMatches();
|
|
23
|
+
|
|
24
|
+
// Verificar si hay una ruta hija activa (proyectoId)
|
|
25
|
+
const hasChildRoute = matches.some((match) => match.routeId === "/proyectos/$proyectoId");
|
|
26
|
+
|
|
27
|
+
// Si hay una ruta hija activa, solo renderizar el Outlet
|
|
28
|
+
if (hasChildRoute) {
|
|
29
|
+
return <Outlet />;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Si no hay proyectoId, mostrar el listado
|
|
33
|
+
return (
|
|
34
|
+
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16">
|
|
35
|
+
<div className="text-center mb-16">
|
|
36
|
+
<h2 className="text-5xl font-bold text-base-content mb-6">Proyectos</h2>
|
|
37
|
+
<p className="text-xl text-base-content/70 max-w-2xl mx-auto">
|
|
38
|
+
Explora los proyectos desarrollados con este template base.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
43
|
+
{proyectos.map((proyecto) => (
|
|
44
|
+
<Link
|
|
45
|
+
key={proyecto.id}
|
|
46
|
+
to="/proyectos/$proyectoId"
|
|
47
|
+
params={{ proyectoId: proyecto.id.toString() }}
|
|
48
|
+
className="block"
|
|
49
|
+
>
|
|
50
|
+
<Card className="overflow-hidden hover:shadow-lg transition-shadow cursor-pointer h-full">
|
|
51
|
+
<CardHeader>
|
|
52
|
+
<div className="h-48 bg-base-200 overflow-hidden">
|
|
53
|
+
<img
|
|
54
|
+
src={proyecto.imagen}
|
|
55
|
+
alt={proyecto.titulo}
|
|
56
|
+
className="w-full h-full object-cover rounded-md"
|
|
57
|
+
/>
|
|
58
|
+
</div>
|
|
59
|
+
<div className="flex items-center justify-between mb-3">
|
|
60
|
+
<Badge
|
|
61
|
+
variant={proyecto.estado === "En Producción" ? "default" : "secondary"}
|
|
62
|
+
className={
|
|
63
|
+
proyecto.estado === "En Producción" ? "badge-success" : "badge-warning"
|
|
64
|
+
}
|
|
65
|
+
>
|
|
66
|
+
{proyecto.estado}
|
|
67
|
+
</Badge>
|
|
68
|
+
<span className="text-sm text-base-content/60">
|
|
69
|
+
{new Date(proyecto.fecha).toLocaleDateString("es-ES", {
|
|
70
|
+
year: "numeric",
|
|
71
|
+
month: "short",
|
|
72
|
+
})}
|
|
73
|
+
</span>
|
|
74
|
+
</div>
|
|
75
|
+
<CardTitle>{proyecto.titulo}</CardTitle>
|
|
76
|
+
<CardDescription className="line-clamp-3">{proyecto.descripcion}</CardDescription>
|
|
77
|
+
</CardHeader>
|
|
78
|
+
<CardContent>
|
|
79
|
+
<div className="flex flex-wrap gap-2">
|
|
80
|
+
{proyecto.tecnologias.map((tech, index) => (
|
|
81
|
+
<Badge key={index} variant="outline">
|
|
82
|
+
{tech}
|
|
83
|
+
</Badge>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
</CardContent>
|
|
87
|
+
</Card>
|
|
88
|
+
</Link>
|
|
89
|
+
))}
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|