feat(RecipeDetail): implementada funcionalidad para ver una receta en detalle

parent eae49825
Showing with 65 additions and 89 deletions
<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.ingredients" :key="index" class="mb-1">
<li v-for="(ing, index) in processedIngredients" :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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment