Skip to content

Commit 4afddc0

Browse files
committed
Añadir funcionalidad para eliminar recetas y actualizar las instrucciones de estilo y CSS
1 parent 0775280 commit 4afddc0

File tree

5 files changed

+390
-0
lines changed

5 files changed

+390
-0
lines changed

.github/copilot-instructions.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Instrucciones para GitHub Copilot
2+
3+
## Contexto del Proyecto
4+
5+
Esta es una aplicación de recetas construida con **Node.js**, **Express**, **Handlebars** y **SQLite**. La aplicación permite crear, listar, editar y eliminar recetas.
6+
7+
## Stack Tecnológico
8+
9+
- **Backend**: Node.js con Express 5.x
10+
- **Vistas**: Handlebars (express-handlebars)
11+
- **Base de Datos**: SQLite3 con el módulo sqlite
12+
- **Pruebas**: Jest y Supertest
13+
- **Desarrollo**: Nodemon para recarga automática
14+
15+
## Estructura del Proyecto
16+
17+
```
18+
/workspaces/node-recipe-app/
19+
├── src/
20+
│ ├── routes.js # Manejadores de rutas Express
21+
│ └── database/
22+
│ ├── connection.js # Configuración de conexión a SQLite
23+
│ ├── index.js # Exportaciones de módulos de BD
24+
│ ├── schema.js # Definición de tablas
25+
│ └── seedData.js # Datos iniciales
26+
├── views/ # Plantillas Handlebars
27+
├── __tests__/ # Pruebas con Jest
28+
└── public/ # Archivos estáticos (CSS)
29+
```
30+
31+
## Convenciones de Código
32+
33+
### JavaScript
34+
- Usar **async/await** para operaciones asíncronas
35+
- Usar **const** para declaraciones que no se reasignan
36+
- Usar **tabs** para indentación (configuración actual del proyecto)
37+
- Nombres descriptivos para variables y funciones
38+
39+
### Rutas Express
40+
- Usar verbos HTTP apropiados: GET para leer, POST para crear/modificar/eliminar
41+
- Redirigir después de operaciones POST: `res.redirect('/ruta')`
42+
- Renderizar vistas con `res.render('nombre-vista', { datos })`
43+
44+
### Base de Datos
45+
- Usar **queries parametrizadas** para prevenir inyección SQL
46+
- Ejemplo: `db.run('DELETE FROM recipes WHERE id = ?', [recipeId])`
47+
- Siempre obtener la conexión con `await getDbConnection()`
48+
49+
### Handlebars
50+
- Usar helpers personalizados cuando sea necesario (ej: `split`, `newline`)
51+
- Mantener la lógica fuera de las vistas
52+
- Usar formularios HTML con `method="POST"` para operaciones de escritura
53+
54+
## Patrones de Testing
55+
56+
### Configuración de Pruebas
57+
- Usar base de datos de prueba separada (test-database.js)
58+
- Mockear el módulo de base de datos en pruebas
59+
- Inicializar y cerrar la BD en `beforeEach` y `afterEach`
60+
61+
### Estructura de Tests
62+
```javascript
63+
test('descripción del test', async () => {
64+
// Arrange: preparar datos
65+
// Act: ejecutar acción
66+
// Assert: verificar resultado
67+
});
68+
```
69+
70+
### Verificaciones
71+
- Verificar códigos de estado HTTP (200, 302 para redirects)
72+
- Verificar que los datos se persisten en la base de datos
73+
- Incluir casos edge (IDs inexistentes, valores inválidos)
74+
75+
## Buenas Prácticas
76+
77+
1. **Confirmaciones de Usuario**: Usar `confirm()` JavaScript para acciones destructivas (eliminar)
78+
2. **Validación**: Requerir campos obligatorios en formularios con `required`
79+
3. **Seguridad**: Nunca interpolar directamente valores de usuario en queries SQL
80+
4. **Accesibilidad**: Usar labels apropiados en formularios
81+
5. **Separación de Responsabilidades**:
82+
- Rutas: lógica de negocio
83+
- Vistas: presentación
84+
- Base de datos: persistencia
85+
86+
## Scripts Disponibles
87+
88+
- `npm start`: Iniciar aplicación en producción
89+
- `npm run dev`: Iniciar con nodemon (recarga automática)
90+
- `npm test`: Ejecutar todas las pruebas
91+
- `npm run test:watch`: Ejecutar pruebas en modo watch
92+
93+
## Datos de Ejemplo
94+
95+
La aplicación incluye 3 recetas de ejemplo:
96+
- Spaghetti Carbonara
97+
- Chocolate Chip Cookies
98+
- Caesar Salad
99+
100+
Los ingredientes y métodos usan `\n` para separar líneas.
101+
102+
## Consideraciones Especiales
103+
104+
- La aplicación crea la base de datos automáticamente en el primer inicio
105+
- Los seed data incluyen lógica de migración para formatear datos antiguos
106+
- El puerto por defecto es 3000
107+
- Las vistas usan un layout principal en `views/layouts/main.hbs`
108+
109+
## Cuando Agregues Nuevas Funcionalidades
110+
111+
1. ✅ Implementar la ruta en `src/routes.js`
112+
2. ✅ Actualizar/crear las vistas necesarias en `views/`
113+
3. ✅ Agregar pruebas en `__tests__/routes.test.js`
114+
4. ✅ Ejecutar `npm test` para verificar que todo funciona
115+
5. ✅ Considerar actualizaciones del schema si afecta la BD
116+
117+
## Idioma
118+
119+
- Mensajes al usuario: **Español**
120+
- Código y comentarios: **Inglés** (convención del proyecto)
121+
- Documentación: **Español** (para este archivo)
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Instrucciones de Estilo y CSS
2+
3+
## Paleta de Colores del Proyecto
4+
5+
### Colores Principales
6+
- **Azul primario**: `#3498db` - Botones principales, elementos interactivos
7+
- **Azul oscuro**: `#2c3e50` - Header, footer, títulos
8+
- **Azul hover**: `#2980b9` - Estado hover de botones primarios
9+
- **Gris secundario**: `#95a5a6` - Botones secundarios
10+
- **Gris texto**: `#7f8c8d` - Texto secundario, descripciones
11+
- **Rojo peligro**: `#e74c3c` - Botones de eliminar, mensajes de error
12+
- **Fondo claro**: `#f8f9fa` - Fondo de la página
13+
- **Blanco**: `#ffffff` - Tarjetas, formularios, contenido
14+
15+
### Colores de Soporte
16+
- **Gris borde**: `#ddd` - Bordes de inputs
17+
- **Gris claro**: `#ecf0f1` - Separadores, bordes sutiles
18+
- **Hover secundario**: `#7f8c8d` - Hover de botones secundarios
19+
- **Hover peligro**: `#c0392b` - Hover de botones de eliminar
20+
- **Hover navegación**: `#34495e` - Hover de links en navegación
21+
22+
## Estructura de Layout
23+
24+
### Contenedor Principal
25+
- Ancho máximo: `1200px`
26+
- Centrado con `margin: 0 auto`
27+
- Padding lateral: `2rem` (1rem en móvil)
28+
29+
### Espaciado
30+
- Margen entre secciones: `2rem`
31+
- Padding de tarjetas: `2rem` (1.5rem en cards pequeños)
32+
- Gap entre elementos flex/grid: `1rem` - `2rem`
33+
34+
## Componentes de UI
35+
36+
### Botones (`.btn`)
37+
```css
38+
padding: 0.5rem 1rem
39+
border-radius: 4px
40+
font-size: 1rem
41+
transition: background-color 0.3s
42+
```
43+
44+
**Variantes disponibles:**
45+
- `.btn-primary` - Azul (`#3498db`)
46+
- `.btn-secondary` - Gris (`#95a5a6`)
47+
- `.btn-danger` - Rojo (`#e74c3c`)
48+
- `.btn-sm` - Versión pequeña con padding reducido
49+
50+
### Tarjetas (`.recipe-card`)
51+
```css
52+
background: white
53+
padding: 1.5rem
54+
border-radius: 8px
55+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1)
56+
transition: transform 0.2s
57+
```
58+
59+
- Hover: `transform: translateY(-2px)`
60+
61+
### Formularios
62+
63+
**Grupos de formulario (`.form-group`):**
64+
- Margen inferior: `1rem`
65+
- Labels en negrita, color `#2c3e50`
66+
- Inputs con border `1px solid #ddd`
67+
- Border radius: `4px`
68+
- Padding: `0.5rem`
69+
70+
**Acciones de formulario (`.form-actions`):**
71+
- Display flex con gap de `1rem`
72+
- Se usa para agrupar botones de envío y cancelación
73+
74+
### Navegación
75+
76+
**Header:**
77+
- Background: `#2c3e50`
78+
- Color texto: `white`
79+
- Box shadow: `0 2px 4px rgba(0, 0, 0, 0.1)`
80+
- Padding vertical: `1rem`
81+
82+
**Links de navegación:**
83+
- Color: `white`
84+
- Hover: background `#34495e`
85+
- Padding: `0.5rem 1rem`
86+
- Border radius: `4px`
87+
88+
## Sistema de Grid
89+
90+
### Grid de Recetas (`.recipes-grid`)
91+
```css
92+
display: grid
93+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr))
94+
gap: 1.5rem
95+
```
96+
97+
### Grid de Features (`.home-features`)
98+
```css
99+
display: grid
100+
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr))
101+
gap: 2rem
102+
```
103+
104+
## Elementos Específicos
105+
106+
### Pasos de Instrucciones (`.instruction-step`)
107+
- Display flex con step number circular
108+
- Step number: círculo azul (`#3498db`) de `1.5rem`
109+
- Margen derecho del número: `1rem`
110+
111+
### Ingredientes (`.ingredient`)
112+
- Padding vertical: `0.5rem`
113+
- Border bottom: `1px solid #ecf0f1`
114+
- Último elemento sin borde
115+
116+
### Secciones de Receta (`.recipe-section`)
117+
- Margen inferior: `2rem`
118+
- Títulos h2 con borde inferior de `2px solid #ecf0f1`
119+
120+
## Responsive Design
121+
122+
### Breakpoint Principal: `768px`
123+
124+
**Cambios en móvil:**
125+
- Navegación: `flex-direction: column`
126+
- Header de receta: elementos en columna
127+
- Padding principal: `0 1rem` (reducido)
128+
- Título home: `2rem` (reducido de 3rem)
129+
130+
## Tipografía
131+
132+
### Fuente Principal
133+
```css
134+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
135+
line-height: 1.6
136+
```
137+
138+
### Tamaños de Fuente
139+
- Título principal (home): `3rem` (2rem en móvil)
140+
- H1: heredado con color `#2c3e50`
141+
- Navegación título: `1.5rem`
142+
- Texto descripción: `1.2rem`
143+
- Texto normal: `1rem`
144+
- Texto pequeño (preview): `0.9rem`
145+
- Step number: `0.8rem`
146+
147+
## Efectos y Transiciones
148+
149+
### Transiciones Estándar
150+
- Botones: `background-color 0.3s`
151+
- Tarjetas: `transform 0.2s`
152+
153+
### Sombras
154+
- Tarjetas/contenido: `0 2px 4px rgba(0, 0, 0, 0.1)`
155+
- Header: `0 2px 4px rgba(0, 0, 0, 0.1)`
156+
157+
### Hover Effects
158+
- Botones: cambio de color de fondo
159+
- Tarjetas: elevación con `translateY(-2px)`
160+
- Links de navegación: cambio de background
161+
162+
## Convenciones de Nombres de Clase
163+
164+
### Patrón BEM Simplificado
165+
- **Contenedores**: `.home-container`, `.recipes-container`, `.recipe-container`
166+
- **Headers**: `.home-header`, `.recipes-header`, `.recipe-header`
167+
- **Acciones**: `.home-actions`, `.recipe-actions`, `.form-actions`
168+
- **Elementos específicos**: `.recipe-card`, `.recipe-section`, `.ingredient`, etc.
169+
170+
### Modificadores
171+
- Tamaño: `.btn-sm`
172+
- Variante: `.btn-primary`, `.btn-secondary`, `.btn-danger`
173+
- Estado: se maneja con pseudo-clases (`:hover`)
174+
175+
## Buenas Prácticas
176+
177+
1. **Consistencia**: Usar las clases existentes antes de crear nuevas
178+
2. **Espaciado**: Mantener el sistema de espaciado basado en `rem`
179+
3. **Colores**: Usar siempre la paleta definida
180+
4. **Responsive**: Probar en breakpoint de 768px
181+
5. **Accesibilidad**: Mantener contraste adecuado para texto
182+
6. **Semántica**: Usar etiquetas HTML apropiadas antes de depender solo de CSS
183+
184+
## Integración con Handlebars
185+
186+
Al agregar nuevos elementos en las vistas `.hbs`, seguir estos patrones:
187+
188+
```handlebars
189+
<!-- Contenedor principal -->
190+
<div class="nombre-container">
191+
<!-- Header de sección -->
192+
<div class="nombre-header">
193+
<h1>Título</h1>
194+
<div class="nombre-actions">
195+
<button class="btn btn-primary">Acción</button>
196+
</div>
197+
</div>
198+
199+
<!-- Contenido -->
200+
<div class="nombre-content">
201+
<!-- Contenido aquí -->
202+
</div>
203+
</div>
204+
```
205+
206+
## Clases de Utilidad Disponibles
207+
208+
No hay clases de utilidad tipo Tailwind. El proyecto usa CSS tradicional con clases semánticas. Si necesitas agregar estilos nuevos, agrega una clase semántica en `public/style.css`.

__tests__/routes.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,44 @@ describe('Routes', () => {
6464
expect(recipe).toBeDefined();
6565
expect(recipe.title).toBe(newRecipe.title);
6666
});
67+
68+
test('POST /recipes/:id/delete should delete a recipe', async () => {
69+
// First create a recipe to delete
70+
const newRecipe = {
71+
title: 'Recipe to Delete',
72+
ingredients: 'Test ingredients',
73+
method: 'Test method'
74+
};
75+
76+
await db.run('INSERT INTO recipes (title, ingredients, method) VALUES (?, ?, ?)', [
77+
newRecipe.title,
78+
newRecipe.ingredients,
79+
newRecipe.method
80+
]);
81+
82+
// Get the created recipe
83+
const recipe = await db.get('SELECT * FROM recipes WHERE title = ?', [newRecipe.title]);
84+
expect(recipe).toBeDefined();
85+
86+
// Delete the recipe
87+
const response = await request(app)
88+
.post(`/recipes/${recipe.id}/delete`);
89+
90+
expect(response.status).toBe(302); // Redirect status
91+
expect(response.headers.location).toBe('/recipes');
92+
93+
// Verify recipe was deleted
94+
const deletedRecipe = await db.get('SELECT * FROM recipes WHERE id = ?', [recipe.id]);
95+
expect(deletedRecipe).toBeUndefined();
96+
});
97+
98+
test('POST /recipes/:id/delete with non-existent id should not fail', async () => {
99+
const nonExistentId = 99999;
100+
101+
const response = await request(app)
102+
.post(`/recipes/${nonExistentId}/delete`);
103+
104+
expect(response.status).toBe(302); // Redirect status
105+
expect(response.headers.location).toBe('/recipes');
106+
});
67107
});

src/routes.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,18 @@ router.post('/recipes/:id/edit', async (req, res) => {
4040
res.redirect(`/recipes/${recipeId}`)
4141
})
4242

43+
router.post('/recipes/:id/delete', async (req, res) => {
44+
const db = await getDbConnection()
45+
const recipeId = req.params.id
46+
await db.run('DELETE FROM recipes WHERE id = ?', [recipeId])
47+
res.redirect('/recipes')
48+
})
49+
50+
// Get a random recipe
51+
router.get('/recipes/random', async (req, res) => {
52+
const db = await getDbConnection()
53+
const recipe = await db.get('SELECT * FROM recipes ORDER BY title LIMIT 1')
54+
res.render('recipe', { recipe })
55+
})
56+
4357
module.exports = router

0 commit comments

Comments
 (0)