Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Alba María Álvarez
/
front_recipes
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
522e2e4c
authored
Sep 02, 2025
by
Alba María Álvarez
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
feat(RecipeDetail): implementada funcionalidad para ver una receta en detalle
parent
eae49825
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
65 additions
and
89 deletions
src/views/RecipeDetail.vue
src/views/RecipeDetail.vue
View file @
522e2e4c
<
template
>
<div
class=
"container my-5"
v-if=
"recipe
"
>
<div
v-if=
"recipe"
class=
"container my-5
"
>
<div
class=
"row justify-content-center"
>
<div
class=
"col-lg-10 col-md-11"
>
<div
class=
"card h-100 card-custom"
>
<div
class=
"card-body p-5"
>
<div
class=
"d-flex justify-content-end mb-4"
>
<div
class=
"d-flex justify-content-between align-items-center mb-4"
>
<span
v-if=
"recipe.favorite"
class=
"badge badge-custom px-3 py-2 shadow-sm"
>
<i
class=
"bi bi-heart-fill"
></i>
Favorita
</span>
<div
v-if=
"mode === 'view'"
class=
"d-flex gap-2"
>
<button
@
click=
"editRecipe"
class=
"btn btn-outline-custom"
>
<i
class=
"bi bi-pencil-square"
></i>
Editar
...
...
@@ -13,6 +18,7 @@
<i
class=
"bi bi-trash"
></i>
Eliminar
</button>
</div>
<div
v-else
class=
"d-flex gap-2"
>
<button
@
click=
"saveChanges"
class=
"btn btn-primary-custom"
>
<i
class=
"bi bi-save"
></i>
Guardar
...
...
@@ -28,25 +34,25 @@
<input
v-else
type=
"text"
v-model=
"recipe.name"
class=
"form-control form-control-lg text-center mb-2"
/>
<div
class=
"d-flex justify-content-center align-items-center flex-wrap gap-3 mb-4"
>
<p
v-if=
"mode === 'view'"
class=
"text-muted small mb-0"
>
Creado el
**
{{
formattedDate
}}
**
<p
v-if=
"mode === 'view'
&& recipe.createdAt != null
"
class=
"text-muted small mb-0"
>
Creado el
{{
formattedDate
}}
</p>
<button
class=
"btn btn-sm"
>
<i
:class=
"recipe.favorite ? 'bi-star-fill' : 'bi-star'"
></i>
</button>
</div>
<div
class=
"mb-4 mt-3"
>
<img
v-if=
"mode === 'view'"
:src=
"recipe.image"
class=
"img-fluid rounded shadow-sm"
style=
"max-height: 500px; max-width: 100%; object-fit: cover;"
alt=
"Imagen de la receta"
/>
<div
v-else
class=
"text-center d-flex flex-column align-items-center"
>
<label
class=
"form-label fw-bold"
>
Cambiar imagen
</label>
<input
type=
"file"
class=
"form-control"
/>
<div
class=
"mb-4 mt-3 text-center"
>
<div
class=
"image-container mx-auto"
>
<template
v-if=
"mode === 'view'"
>
<img
:src=
"recipe.picture ? recipe.picture : defaultImage"
class=
"img-fluid rounded shadow-sm"
alt=
"Imagen de la receta"
/>
</
template
>
<div
v-else
class=
"d-flex flex-column align-items-center"
>
<label
class=
"form-label fw-bold"
>
Cambiar imagen
</label>
<input
type=
"file"
class=
"form-control"
/>
</div>
</div>
</div>
<p
v-if=
"mode === 'view'"
class=
"text-muted"
>
{{ recipe.description }}
</p>
<textarea
v-else
...
...
@@ -64,9 +70,9 @@
<div
class=
"mb-4"
>
<h5
class=
"fw-bold"
>
Ingredientes
</h5>
<ul
v-if=
"mode === 'view'"
class=
"list-unstyled"
>
<li
v-for=
"(ing, index) in
recipe.i
ngredients"
:key=
"index"
class=
"mb-1"
>
<li
v-for=
"(ing, index) in
processedI
ngredients"
:key=
"index"
class=
"mb-1"
>
<i
class=
"bi bi-check-circle-fill me-2 text-success"
></i>
{{
ing
.
quantity
}}
{{
ing
.
unit
}}
de
{{
ing
.
ingredient
}}
{{ ing.quantity }} {{ ing.unit }}
{{ ing.connective }} {{ ing.name
}}
</li>
</ul>
<div
v-else
class=
"ingredient-form-section"
>
...
...
@@ -120,6 +126,7 @@
import
{
useRecipeStore
}
from
'@/stores/recipeStore'
;
import
{
computed
,
onMounted
,
ref
}
from
'vue'
;
import
{
useRoute
}
from
'vue-router'
;
import
defaultImage
from
'@/assets/default-recipe.jpg'
;
// Estado de la vista: 'view' o 'edit'
const
mode
=
ref
(
'view'
);
...
...
@@ -131,81 +138,33 @@ const route = useRoute();
const
recipe
=
computed
(()
=>
recipeStore
.
recipe
);
onMounted
(
async
()
=>
{
// Corregir la forma de acceder al ID de la ruta
const
recipeId
=
route
.
params
.
id
;
await
recipeStore
.
readDetail
(
recipeId
);
});
//
Formatear la fecha para una visualización profesional
//
Propiedad computada para formatear la fecha
const
formattedDate
=
computed
(()
=>
{
if
(
recipe
.
value
&&
recipe
.
value
.
createdAt
)
{
const
options
=
{
year
:
'numeric'
,
month
:
'long'
,
day
:
'numeric'
};
return
new
Date
(
recipe
.
value
.
createdAt
).
toLocaleDateString
(
'es-ES'
,
options
);
}
return
'Fecha no disponible'
;
const
options
=
{
year
:
'numeric'
,
month
:
'long'
,
day
:
'numeric'
};
return
new
Date
(
recipe
.
value
.
createdAt
).
toLocaleDateString
(
'es-ES'
,
options
);
});
// Datos de la receta de ejemplo
// const recipe = ref({
// name: 'Tarta de Manzana Clásica',
// image: '/src/assets/default-recipe.jpg',
// description: 'Una deliciosa tarta de manzana con un toque de canela y una corteza dorada y crujiente.',
// ingredients: [
// { quantity: 2, unit: 'tazas', ingredient: 'harina de trigo' },
// { quantity: 1, unit: 'taza', ingredient: 'azúcar' },
// { quantity: 4, unit: 'unidades', ingredient: 'manzanas' }
// ],
// steps: [
// { description: 'Precalentar el horno a 180°C.' },
// { description: 'Mezclar la harina y el azúcar en un bol.' },
// { description: 'Pelar y cortar las manzanas en rodajas finas.' },
// { description: 'Hornear por 45 minutos.' }
// ]
// });
// Función para el textarea
const
autoGrow
=
(
event
)
=>
{
const
textarea
=
event
.
target
;
textarea
.
style
.
height
=
'auto'
;
// Resetea la altura
textarea
.
style
.
height
=
`
${
textarea
.
scrollHeight
}
px`
;
// Ajusta a la altura del contenido
};
// Lógica de los botones
const
editRecipe
=
()
=>
{
mode
.
value
=
'edit'
;
nextTick
(()
=>
{
// Selecciona todos los textareas y aplica la función
document
.
querySelectorAll
(
'textarea'
).
forEach
(
textarea
=>
{
autoGrow
({
target
:
textarea
});
});
});
};
const
saveChanges
=
()
=>
{
console
.
log
(
'Guardando cambios:'
,
recipe
.
value
);
mode
.
value
=
'view'
;
};
const
deleteRecipe
=
()
=>
{
console
.
log
(
'Eliminando la receta:'
,
recipe
.
value
.
name
);
};
const
addStep
=
()
=>
{
recipe
.
value
.
steps
.
push
({
description
:
''
});
nextTick
(()
=>
{
// Aplica el auto-crecimiento al nuevo textarea
const
newTextarea
=
document
.
querySelector
(
'.steps-form-section textarea:last-child'
);
if
(
newTextarea
)
{
autoGrow
({
target
:
newTextarea
});
}
// Propiedad computada para procesar los ingredientes
const
processedIngredients
=
computed
(()
=>
{
if
(
!
recipe
.
value
||
!
recipe
.
value
.
ingredients
)
{
return
[];
}
return
recipe
.
value
.
ingredients
.
map
(
ing
=>
{
const
unit
=
ing
.
unitOfMeasure
?
ing
.
unitOfMeasure
.
toLowerCase
()
:
''
;
const
name
=
ing
.
name
?
ing
.
name
.
toLowerCase
()
:
''
;
const
connective
=
ing
.
unitOfMeasure
?
'de'
:
''
;
return
{
...
ing
,
unit
,
name
,
connective
};
});
};
const
removeStep
=
(
index
)
=>
{
recipe
.
value
.
steps
.
splice
(
index
,
1
);
};
});
</
script
>
<
style
scoped
>
...
...
@@ -213,6 +172,7 @@ const removeStep = (index) => {
border-color
:
#793E6C
;
box-shadow
:
0
0
1rem
rgba
(
121
,
62
,
108
,
0.1
);
border-radius
:
1rem
;
max-width
:
800px
;
}
.text-primary-custom
{
...
...
@@ -256,10 +216,6 @@ h5 {
color
:
#2C0C21
;
}
.img-fluid
{
border-radius
:
0.5rem
;
}
/* Modo de edición (ingredientes y pasos horizontalmente) */
.ingredient-form-section
.input-group
,
.steps-form-section
.input-group
{
...
...
@@ -270,4 +226,23 @@ textarea {
resize
:
none
;
overflow
:
hidden
;
}
/* Estilos imagen */
.image-container
{
width
:
400px
;
height
:
400px
;
overflow
:
hidden
;
}
.image-container
img
{
width
:
100%
;
height
:
100%
;
object-fit
:
contain
;
}
/* Estilos span favorito */
.badge-custom
{
background-color
:
#793E6C
;
color
:
white
;
}
</
style
>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment