feat(CreateRecipe): añadida funcionalidad para crear recetas

parent e0aaa80e
Showing with 155 additions and 72 deletions
...@@ -5,85 +5,94 @@ ...@@ -5,85 +5,94 @@
<div class="recipe-form-card p-4 rounded shadow-sm"> <div class="recipe-form-card p-4 rounded shadow-sm">
<h2 class="text-center mb-4 text-primary-custom fw-bold">Nueva Receta</h2> <h2 class="text-center mb-4 text-primary-custom fw-bold">Nueva Receta</h2>
<div class="mb-4"> <div v-if="errorMsgs.length > 0" class="alert alert-danger" role="alert">
<label for="inputName" class="form-label fw-bold">Nombre de la receta</label> <ul>
<input type="text" class="form-control" id="inputName"> <li v-for="msg in errorMsgs" :key="msg">{{ msg }}</li>
</div> </ul>
<div class="mb-4">
<label for="inputDescription" class="form-label fw-bold">Descripción de la receta</label>
<textarea class="form-control" id="inputDescription" rows="5"></textarea>
</div>
<div class="mb-4">
<label for="formFile" class="form-label fw-bold">Imagen de la receta</label>
<input class="form-control" type="file" id="formFile" accept=".jpg, .jpeg, .png, .gif">
</div> </div>
<div class="mt-5 mb-4"> <form @submit.prevent="handleCreation">
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="mb-4">
<label class="form-label fw-bold">Ingredientes</label> <label for="inputName" class="form-label fw-bold">Nombre de la receta</label>
<button @click="addIngredient" class="btn btn-outline btn-sm"> <input type="text" class="form-control" id="inputName" v-model="name">
<i class="bi bi-plus"></i> Añadir </div>
</button> <div class="mb-4">
<label for="inputDescription" class="form-label fw-bold">Descripción de la receta</label>
<textarea class="form-control" id="inputDescription" rows="5" v-model="description"></textarea>
</div> </div>
<div class="ingredient-table-container p-4 border rounded"> <div class="mb-4">
<div class="row header-row fw-bold mb-2 text-center"> <label for="formFile" class="form-label fw-bold">Imagen de la receta</label>
<div class="col-3">Cantidad</div> <input class="form-control" type="file" id="formFile" accept=".jpg, .jpeg, .png, .gif" @change="handleImageUpload">
<div class="col-3">Unidad</div> </div>
<div class="col-4">Ingrediente</div>
<div class="col-2"></div> <!-- Ingredientes -->
<div class="mt-5 mb-4">
<div class="d-flex justify-content-between align-items-center mb-2">
<label class="form-label fw-bold">Ingredientes</label>
<button type="button" @click="addIngredient" class="btn btn-outline btn-sm">
<i class="bi bi-plus"></i> Añadir
</button>
</div> </div>
<div v-for="ingredient in ingredients" :key="ingredient.id" class="row align-items-center mb-2"> <div class="ingredient-table-container p-4 border rounded">
<div class="col-3"> <div class="row header-row fw-bold mb-2 text-center">
<input type="number" class="form-control" v-model="ingredient.quantity" placeholder="Ej: 2"> <div class="col-3">Cantidad</div>
</div> <div class="col-3">Unidad</div>
<div class="col-3"> <div class="col-4">Ingrediente</div>
<input type="text" class="form-control" v-model="ingredient.unit" placeholder="Ej: tazas"> <div class="col-2"></div>
</div> </div>
<div class="col-4"> <div v-for="ingredient in ingredients" :key="ingredient.id" class="row align-items-center mb-2">
<input type="text" class="form-control" v-model="ingredient.ingredient" placeholder="Ej: harina de trigo"> <div class="col-3">
</div> <input type="number" class="form-control" v-model="ingredient.quantity" placeholder="Ej: 2">
<div class="col-2 d-flex justify-content-center"> </div>
<button @click="removeIngredient(ingredient.id)" class="btn btn-outline-danger btn-sm"> <div class="col-3">
<i class="bi bi-trash"></i> <input type="text" class="form-control" v-model="ingredient.unitOfMeasure" placeholder="Ej: tazas">
</button> </div>
<div class="col-4">
<input type="text" class="form-control" v-model="ingredient.name" placeholder="Ej: harina de trigo">
</div>
<div class="col-2 d-flex justify-content-center">
<button type="button" @click="removeIngredient(ingredient.id)" class="btn btn-outline-danger btn-sm">
<i class="bi bi-trash"></i>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="mt-5 mb-4"> <!-- Pasos -->
<div class="d-flex justify-content-between align-items-center mb-2"> <div class="mt-5 mb-4">
<label class="form-label fw-bold">Pasos de preparación</label> <div class="d-flex justify-content-between align-items-center mb-2">
<button @click="addStep" class="btn btn-outline btn-sm"> <label class="form-label fw-bold">Pasos de preparación</label>
<i class="bi bi-plus"></i> Añadir <button type="button" @click="addStep" class="btn btn-outline btn-sm">
</button> <i class="bi bi-plus"></i> Añadir
</div> </button>
<div class="steps-table-container p-4 border rounded">
<div class="row header-row fw-bold mb-2 text-center">
<div class="col-1">#</div>
<div class="col-9">Descripción</div>
<div class="col-2"></div>
</div> </div>
<div v-for="(step, index) in steps" :key="index" class="row align-items-center mb-2"> <div class="steps-table-container p-4 border rounded">
<div class="col-1 text-center"> <div class="row header-row fw-bold mb-2 text-center">
<span>{{ index + 1 }}</span> <div class="col-1">#</div>
<div class="col-9">Descripción</div>
<div class="col-2"></div>
</div> </div>
<div class="col-9"> <div v-for="(step, index) in steps" :key="index" class="row align-items-center mb-2">
<textarea class="form-control" v-model="step.description" rows="1"></textarea> <div class="col-1 text-center">
</div> <span>{{ index + 1 }}</span>
<div class="col-2 d-flex justify-content-center"> </div>
<button @click="removeStep(step.number)" class="btn btn-outline-danger btn-sm"> <div class="col-9">
<i class="bi bi-trash"></i> <textarea class="form-control" v-model="step.description" rows="1"></textarea>
</button> </div>
<div class="col-2 d-flex justify-content-center">
<button type="button" @click="removeStep(step.number)" class="btn btn-outline-danger btn-sm">
<i class="bi bi-trash"></i>
</button>
</div>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary-custom px-4">Crear Receta</button>
</div>
<div class="d-flex justify-content-end mt-4">
<button type="submit" class="btn btn-primary-custom px-4">Crear Receta</button>
</div>
</form>
</div> </div>
</div> </div>
</div> </div>
...@@ -91,18 +100,86 @@ ...@@ -91,18 +100,86 @@
</template> </template>
<script setup> <script setup>
import { useRecipeStore } from '@/stores/recipeStore';
import { ref } from 'vue'; import { ref } from 'vue';
import { useRouter } from 'vue-router';
const recipeStore = useRecipeStore();
const router = useRouter();
// Datos de receta
const name = ref('');
const description = ref('');
const picture = ref(null);
const ingredients = ref([ const ingredients = ref([
{ id: 1, quantity: '', unit: '', ingredient: '' } { id: 1, name: '', quantity: '', unitOfMeasure: '' }
]); ]);
const nextId = ref(2); const steps = ref([
{ number:1, description: '' }
]);
const errorMsgs = ref([]);
// Función para crear la receta
async function handleCreation() {
errorMsgs.value = [];
try {
// Formato para subir archivos
const formData = new FormData();
formData.append('name', name.value);
formData.append('description', description.value);
// Solo agrega la imagen si existe
if (picture.value) {
formData.append('picture', picture.value);
}
// Filtra y adjunta solo los ingredientes que no tienen campos obligatorios vacíos
const filledIngredients = ingredients.value.filter(ing =>
ing.name && ing.quantity
);
filledIngredients.forEach((ingredient, index) => {
formData.append(`ingredients[${index}].name`, ingredient.name);
formData.append(`ingredients[${index}].quantity`, ingredient.quantity);
formData.append(`ingredients[${index}].unitOfMeasure`, ingredient.unitOfMeasure);
});
// Filtra y re-indexa los pasos antes de enviarlos
const filledSteps = steps.value.filter(step => step.description);
filledSteps.forEach((step, index) => {
// Re-calcula el número de paso antes de enviarlo
formData.append(`steps[${index}].number`, index + 1);
formData.append(`steps[${index}].description`, step.description);
});
await recipeStore.create(formData);
router.push('/recipes');
} catch (error) {
// Limpia la imagen y el campo del formulario
picture.value = null;
document.getElementById('formFile').value = '';
// Verifica que la respuesta de error existe y tiene el mensaje
if (error.response && error.response.status === 401) {
errorMsgs.value = ['Error de autenticación. Por favor, inicia sesión.'];
} else if (error.response && error.response.data && error.response.data.errorMsg) {
// Divide la cadena de mensajes y la asigna al array
errorMsgs.value = error.response.data.errorMsg.split('; ');
} else {
errorMsgs.value = ['Ocurrió un error inesperado. Inténtalo de nuevo.'];
}
}
};
// Funciones para añadir/eliminar ingredientes
const addIngredient = () => { const addIngredient = () => {
const nextId = ingredients.value.length > 0 ? Math.max(...ingredients.value.map(i => i.id)) + 1 : 1;
ingredients.value.push({ ingredients.value.push({
id: nextId.value++, id: nextId,
quantity: '', quantity: '',
unit: '', unitOfMeasure: '',
ingredient: '' ingredient: ''
}); });
}; };
...@@ -111,12 +188,10 @@ const removeIngredient = (id) => { ...@@ -111,12 +188,10 @@ const removeIngredient = (id) => {
ingredients.value = ingredients.value.filter(item => item.id !== id); ingredients.value = ingredients.value.filter(item => item.id !== id);
}; };
const steps = ref([ // Funciones para añadir/eliminar ingredientes
{ description: '' }
]);
const addStep = () => { const addStep = () => {
steps.value.push({ steps.value.push({
number: steps.value.length + 1,
description: '' description: ''
}); });
}; };
...@@ -124,6 +199,14 @@ const addStep = () => { ...@@ -124,6 +199,14 @@ const addStep = () => {
const removeStep = (index) => { const removeStep = (index) => {
steps.value.splice(index, 1); steps.value.splice(index, 1);
}; };
// Función para importar imagen
const handleImageUpload = (event) => {
const file = event.target.files[0];
if (file) {
picture.value = file; // Guarda el objeto File directamente
}
};
</script> </script>
<style scoped> <style scoped>
......
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