Skip to content

Commit 1e8b24a

Browse files
authored
Merge pull request #24 from 4GeeksAcademy/ft0011-vistaMascotas
Ft0011 vista mascotas
2 parents a39ef54 + 597cd9b commit 1e8b24a

File tree

6 files changed

+358
-34
lines changed

6 files changed

+358
-34
lines changed

migrations/versions/e6383597b0ac_.py renamed to migrations/versions/3388104734f6_.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""empty message
22
3-
Revision ID: e6383597b0ac
3+
Revision ID: 3388104734f6
44
Revises:
5-
Create Date: 2025-03-16 17:29:18.018147
5+
Create Date: 2025-03-17 11:09:47.021713
66
77
"""
88
from alembic import op
99
import sqlalchemy as sa
1010

1111

1212
# revision identifiers, used by Alembic.
13-
revision = 'e6383597b0ac'
13+
revision = '3388104734f6'
1414
down_revision = None
1515
branch_labels = None
1616
depends_on = None

src/api/routes.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,36 @@ def get_pet(pet_id):
9090
@api.route('/foods/suggestions/<int:pet_id>', methods=['GET'])
9191
@jwt_required()
9292
def get_pet_suggestions(pet_id):
93-
pet = Pet.query.get(pet_id).serialize()
94-
# Problema: Un animal puede tener varias patologias en su campo, habría que coger este campo y tratarlo,
93+
pet = Pet.query.filter_by(id=pet_id).first()
94+
print(pet)
95+
if pet is None:
96+
return jsonify({"msg": "Pet no exist"}), 404
97+
# pet = Pet.query.get(pet_id).serialize()
98+
# Problema: Un animal puede tener varias patologias en su campo, habría que co
99+
# ger este campo y tratarlo,
95100
# separar las patologias en una lista y hacer la query para cada patologia.
96101
# Solucion simple: limitar a 1 patologia cada animal por ahora
97-
#if para pet# anymal_type == perro, animal size #si no no hace falta size
98-
if pet["animal_type"] == "perro":
99-
food_suggestions = db.session.execute(select(Food).where(and_(Food.animal_type==pet["animal_type"]),
100-
Food.size==pet["size"],
101-
Food.age==pet["age"],
102-
Food.pathologies==pet["pathologies"])).all()
102+
# if para pet# anymal_type == perro, animal size #si no no hace falta size
103+
if pet.animal_type == "perro":
104+
food_suggestions = db.session.execute(select(Food).where(and_(Food.animal_type==pet.animal_type),
105+
Food.size==pet.size,
106+
Food.age==pet.age,
107+
Food.pathologies==pet.pathologies)).all()
103108
else:
104-
food_suggestions = db.session.execute(select(Food).where(Food.animal_type==pet["animal_type"]),
105-
Food.age==pet["age"],
106-
Food.pathologies==pet["pathologies"]).all()
109+
food_suggestions = db.session.execute(select(Food).where(Food.animal_type==pet.animal_type),
110+
Food.age==pet.age,
111+
Food.pathologies==pet.pathologies).all()
107112
if not food_suggestions :
108-
return "no suggestions found", 404
113+
return "no suggestions found", 404
109114
return [food[0].serialize() for food in food_suggestions], 200
115+
return jsonify("okey"), 200
116+
117+
118+
119+
120+
121+
122+
110123

111124

112125
# #obtener sugerencias de comida según mascota
@@ -347,16 +360,16 @@ def create_pet():
347360
current_user_email = get_jwt_identity()
348361
user = User().query.filter_by(email=current_user_email).first()
349362

350-
if not user:
363+
if not user:
351364
return jsonify({"msg": "usuario no encontrado"}), 400
352365

353366
new_pet = Pet(
354367
name=data["name"],
355-
size= None,
356-
breed= None,
368+
size= data["size"],
369+
breed= data["breed"],
357370
age=data["age"],
358371
animal_type=data["animal_type"],
359-
pathologies= None,
372+
pathologies= data["pathologies"],
360373
url=data.get("url"), # Asegúrate de que se está obteniendo correctamente
361374
user_id=user.id
362375
)
@@ -424,7 +437,7 @@ def update_user():
424437
})
425438

426439

427-
@api.route('/pets/<int:pet_id>', methods=['PUT'])
440+
@api.route('/pet/<int:pet_id>', methods=['PUT'])
428441
@jwt_required()
429442
def new_pet(pet_id):
430443
data = request.get_json()
@@ -502,7 +515,7 @@ def delete_user():
502515
@jwt_required()
503516
def delete_pet(pet_id):
504517

505-
pet = Pet.query.get(pet_id).first()
518+
pet = Pet.query.get(pet_id)
506519

507520
# Eliminar la mascota de la base de datos
508521
db.session.delete(pet)
@@ -513,7 +526,6 @@ def delete_pet(pet_id):
513526
'message': f'Pet {pet.name} with id {pet.id} has been deleted successfully.'
514527
}), 200
515528

516-
517529
@api.route('/search', methods=['GET'])
518530
def search_product():
519531
data = request.get_json()

src/front/js/layout.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import injectContext from "./store/appContext";
1212

1313
import { Navbar } from "./component/navbar";
1414
import { Footer } from "./component/footer";
15-
15+
import { VistaMascota } from "./pages/vistaMascota";
1616
import { VistaProducto } from "./pages/VistaProducto";
1717

1818
import { LoginSignup } from "./pages/loginSignup";
@@ -52,6 +52,7 @@ const PageWithNavbar = () => {
5252
<Route element={<Home />} path="/" />
5353
<Route element={<VistaProducto />} path="/vista-producto/:id" />
5454
<Route element={<LoginSignup />} path="/loginSignup" />
55+
<Route element={<VistaMascota />} path="/pets/:id" />
5556
<Route element={<PerfilUsuario />} path="/perfilUsuario" />
5657
<Route element={<Demo />} path="/demo" />
5758
<Route element={<Single />} path="/single/:theid" />

src/front/js/pages/perfilUsuario.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const PerfilUsuario = () => {
7070
<div
7171
key={index}
7272
className="text-center cursor-pointer"
73-
onClick={() => navigate(`/mascota/${pet.id}`)}
73+
onClick={() => navigate(`/pets/${pet.id}`)}
7474
style={{ cursor: "pointer" }}
7575
>
7676
<img

src/front/js/pages/vistaMascota.js

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import React, { useEffect, useState, useContext } from 'react';
2+
import { useParams, useNavigate } from 'react-router-dom';
3+
import { Context } from "../store/appContext";
4+
import { Modal, Button, Form } from 'react-bootstrap';
5+
import "../../styles/home.css";
6+
7+
export const VistaMascota = () => {
8+
const { id } = useParams();
9+
const navigate = useNavigate();
10+
const { actions } = useContext(Context);
11+
12+
const [petDetails, setPetDetails] = useState(null);
13+
const [loading, setLoading] = useState(true);
14+
const [showModal, setShowModal] = useState(false);
15+
const [editedPet, setEditedPet] = useState({
16+
name: "",
17+
animal_type: "",
18+
breed: "",
19+
size: "",
20+
age: "",
21+
pathologies: ""
22+
});
23+
const [foodSuggestions, setFoodSuggestions] = useState([]);
24+
25+
const getPetDetails = async (id) => {
26+
try {
27+
const resp = await fetch(`${process.env.BACKEND_URL}/api/pets/${id}`);
28+
if (resp.status === 404) {
29+
setPetDetails(null);
30+
} else {
31+
const petData = await resp.json();
32+
setPetDetails(petData);
33+
setEditedPet({
34+
name: petData.name || "",
35+
animal_type: petData.animal_type || "",
36+
breed: petData.breed || "",
37+
size: petData.size || "",
38+
age: petData.age || "",
39+
pathologies: petData.pathologies || ""
40+
});
41+
}
42+
} catch (error) {
43+
console.error("Error al obtener los detalles de la mascota", error);
44+
setPetDetails(null);
45+
} finally {
46+
setLoading(false);
47+
}
48+
};
49+
50+
useEffect(() => {
51+
getPetDetails(id);
52+
}, [id]);
53+
54+
useEffect(() => {
55+
if (petDetails) {
56+
actions.getFoodSuggestions(id).then(data => {
57+
setFoodSuggestions(data);
58+
});
59+
}
60+
}, [petDetails, id, actions]);
61+
62+
useEffect(() => {
63+
if (!loading && petDetails === null) {
64+
navigate("/not-found");
65+
}
66+
}, [loading, petDetails, navigate]);
67+
68+
const handleDelete = async () => {
69+
try {
70+
await actions.deletePet(id);
71+
} catch (error) {
72+
console.error("Error al eliminar la mascota (se ignora):", error);
73+
}
74+
navigate("/perfilUsuario");
75+
};
76+
77+
const handleEdit = async () => {
78+
try {
79+
await actions.editPet(id, editedPet);
80+
} catch (error) {
81+
console.error("Error al editar la mascota (se ignora):", error);
82+
}
83+
setShowModal(false);
84+
navigate("/perfilUsuario");
85+
};
86+
87+
if (loading) return <div className="text-center mt-5">Cargando...</div>;
88+
if (!petDetails) return null;
89+
90+
return (
91+
<div className="container mt-5">
92+
<h2>Detalles de la Mascota</h2>
93+
<div className="row">
94+
<div className="col-md-6">
95+
<div className="card">
96+
<img
97+
src={petDetails.url || '/default-image.jpg'}
98+
alt={petDetails.name}
99+
className="card-img-top"
100+
style={{ width: '100%', height: 'auto' }}
101+
/>
102+
<div className="card-body">
103+
<h5 className="card-title">{petDetails.name}</h5>
104+
<p className="card-text"><strong>Especie:</strong> {petDetails.animal_type}</p>
105+
<p className="card-text"><strong>Raza:</strong> {petDetails.breed}</p>
106+
<p className="card-text"><strong>Tamaño:</strong> {petDetails.size}</p>
107+
<p className="card-text"><strong>Edad:</strong> {petDetails.age}</p>
108+
<p className="card-text"><strong>Patologías:</strong> {petDetails.pathologies}</p>
109+
<Button variant="warning" onClick={() => setShowModal(true)} className="me-2">
110+
✏️ Editar
111+
</Button>
112+
<Button variant="danger" onClick={handleDelete}>
113+
🗑️ Eliminar
114+
</Button>
115+
</div>
116+
</div>
117+
</div>
118+
</div>
119+
120+
<div className="row mt-4">
121+
<div className="col-md-12">
122+
<h2>Comida Recomendada</h2>
123+
<div className="d-flex flex-wrap">
124+
{foodSuggestions.length > 0 ? (
125+
foodSuggestions.map((food, index) => (
126+
<div className="card m-2" style={{ width: "18rem" }} key={index}>
127+
<img
128+
src={food.url || "/default-food.jpg"}
129+
className="card-img-top"
130+
alt={food.name}
131+
/>
132+
<div className="card-body">
133+
<h5 className="card-title">{food.name}</h5>
134+
{food.description && <p className="card-text">{food.description}</p>}
135+
</div>
136+
</div>
137+
))
138+
) : (
139+
<p>No se encontraron sugerencias de comida.</p>
140+
)}
141+
</div>
142+
</div>
143+
</div>
144+
145+
{/* Modal para editar mascota */}
146+
<Modal show={showModal} onHide={() => setShowModal(false)}>
147+
<Modal.Header closeButton>
148+
<Modal.Title>Editar Mascota</Modal.Title>
149+
</Modal.Header>
150+
<Modal.Body>
151+
<Form>
152+
<Form.Group className="mb-3">
153+
<Form.Label>Nombre</Form.Label>
154+
<Form.Control
155+
type="text"
156+
value={editedPet.name || ""}
157+
onChange={(e) => setEditedPet({ ...editedPet, name: e.target.value })}
158+
/>
159+
</Form.Group>
160+
161+
<Form.Group className="mb-3">
162+
<Form.Label>Especie</Form.Label>
163+
<Form.Select
164+
value={editedPet.animal_type || ""}
165+
onChange={(e) => setEditedPet({ ...editedPet, animal_type: e.target.value })}
166+
>
167+
<option value="">Selecciona una especie</option>
168+
<option value="perro">Canina</option>
169+
<option value="gato">Felina</option>
170+
<option value="exótico">Exótico</option>
171+
</Form.Select>
172+
</Form.Group>
173+
174+
<Form.Group className="mb-3">
175+
<Form.Label>Raza</Form.Label>
176+
<Form.Control
177+
type="text"
178+
value={editedPet.breed || ""}
179+
onChange={(e) => setEditedPet({ ...editedPet, breed: e.target.value })}
180+
/>
181+
</Form.Group>
182+
183+
<Form.Group className="mb-3">
184+
<Form.Label>Tamaño</Form.Label>
185+
<Form.Select
186+
value={editedPet.size || ""}
187+
onChange={(e) => setEditedPet({ ...editedPet, size: e.target.value })}
188+
>
189+
<option value="">Selecciona una opción</option>
190+
<option value="razaPequeña">Pequeño (0-10kg)</option>
191+
<option value="razaMediana">Mediano (10-25kg)</option>
192+
<option value="razaGrande">Grande (+25Kg)</option>
193+
</Form.Select>
194+
</Form.Group>
195+
196+
<Form.Group className="mb-3">
197+
<Form.Label>Etapa Vital</Form.Label>
198+
<Form.Select
199+
value={editedPet.age || ""}
200+
onChange={(e) => setEditedPet({ ...editedPet, age: e.target.value })}
201+
>
202+
<option value="">Selecciona una opción</option>
203+
<option value="cachorro">Cachorro (0-1 año)</option>
204+
<option value="adulto">Adulto (1-7 años)</option>
205+
<option value="senior">Senior (+7 años)</option>
206+
</Form.Select>
207+
</Form.Group>
208+
209+
<Form.Group className="mb-3">
210+
<Form.Label>Patologías</Form.Label>
211+
<Form.Select
212+
value={editedPet.pathologies || ""}
213+
onChange={(e) => setEditedPet({ ...editedPet, pathologies: e.target.value })}
214+
>
215+
<option value="">Selecciona una opción</option>
216+
<option value="ninguna">Sin patologias</option>
217+
<option value="diabetico">Diabetes</option>
218+
<option value="renal">Insuficiencia renal</option>
219+
<option value="urinarioStruvita">Urinario Struvita</option>
220+
<option value="urinarioOxalatos">Urinario Oxalatos</option>
221+
<option value="escorbuto">Escorbuto</option>
222+
<option value="obesidad">Obesidad</option>
223+
<option value="hipoalergénico">Hipoalergénico</option>
224+
</Form.Select>
225+
</Form.Group>
226+
</Form>
227+
</Modal.Body>
228+
<Modal.Footer>
229+
<Button variant="primary" onClick={handleEdit}>
230+
Guardar Cambios
231+
</Button>
232+
<Button variant="secondary" onClick={() => setShowModal(false)}>
233+
Cancelar
234+
</Button>
235+
</Modal.Footer>
236+
</Modal>
237+
</div>
238+
);
239+
};

0 commit comments

Comments
 (0)