-
Hello Rigo!!
diff --git a/src/front/pages/MedicalAppointmentDetail.jsx b/src/front/pages/MedicalAppointmentDetail.jsx
new file mode 100644
index 0000000000..c4e8813f2c
--- /dev/null
+++ b/src/front/pages/MedicalAppointmentDetail.jsx
@@ -0,0 +1,216 @@
+import React, { useState, useEffect } from "react";
+import { useParams, Link, useNavigate } from "react-router-dom";
+
+export const MedicalAppointmentDetail = () => {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const [loading, setLoading] = useState(true);
+ const [users, setUsers] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [veterinarians, setVeterinarians] = useState([]);
+
+ const [appointment, setAppointment] = useState({
+ user_id: "",
+ pet_id: "",
+ veterinarian_id: "",
+ date: "",
+ hour: "",
+ comments: ""
+ });
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const [resAppt, resUsers, resPets, resVets] = await Promise.all([
+ fetch(`${backendUrl}/api/medical-appointments/${id}`),
+ fetch(`${backendUrl}/api/user`),
+ fetch(`${backendUrl}/api/pets`),
+ fetch(`${backendUrl}/api/veterinarians`)
+ ]);
+
+ if (resUsers.ok) setUsers(await resUsers.json());
+ if (resPets.ok) setPets(await resPets.json());
+ if (resVets.ok) setVeterinarians(await resVets.json());
+
+ if (resAppt.ok) {
+ const data = await resAppt.json();
+ setAppointment({
+ user_id: data.user_id || data.idUser || "",
+ pet_id: data.pet_id || data.idPet || "",
+ veterinarian_id: data.veterinarian_id || data.idVeterinarian || "",
+ date: data.date || "",
+ hour: data.hour || "",
+ comments: data.comments || ""
+ });
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [id, backendUrl]);
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setAppointment({
+ ...appointment,
+ [name]: value
+ });
+ };
+
+ const handleSave = async (e) => {
+ e.preventDefault();
+
+ const payload = {
+ user_id: appointment.user_id ? Number(appointment.user_id) : null,
+ idUser: appointment.user_id ? Number(appointment.user_id) : null,
+ pet_id: appointment.pet_id ? Number(appointment.pet_id) : null,
+ idPet: appointment.pet_id ? Number(appointment.pet_id) : null,
+ veterinarian_id: appointment.veterinarian_id ? Number(appointment.veterinarian_id) : null,
+ idVeterinarian: appointment.veterinarian_id ? Number(appointment.veterinarian_id) : null,
+ date: appointment.date,
+ hour: appointment.hour,
+ comments: appointment.comments
+ };
+
+ try {
+ const response = await fetch(`${backendUrl}/api/medical-appointments/${id}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(payload)
+ });
+
+ if (response.ok) {
+ alert("Medical appointment updated successfully!");
+ navigate("/medapps");
+ } else {
+ const errorData = await response.json();
+ console.error("Error del servidor:", errorData);
+ alert("Failed to update medical appointment");
+ }
+ } catch (error) {
+ console.error("Error updating medical appointment:", error);
+ }
+ };
+
+ if (loading) {
+ return (
+
+
Loading medical appointment details...
+
+ );
+ }
+
+ return (
+
+
+ ← Back to Medical Appointments List
+
+
+
+
Edit Medical Appointment
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/MedicalAppointmentView.jsx b/src/front/pages/MedicalAppointmentView.jsx
new file mode 100644
index 0000000000..da202e7d67
--- /dev/null
+++ b/src/front/pages/MedicalAppointmentView.jsx
@@ -0,0 +1,158 @@
+import React, { useState, useEffect } from "react";
+import { useParams, Link } from "react-router-dom";
+
+export const MedicalAppointmentView = () => {
+ const { id } = useParams();
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const [loading, setLoading] = useState(true);
+ const [users, setUsers] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [veterinarians, setVeterinarians] = useState([]);
+
+ const [appointment, setAppointment] = useState({
+ user_id: "",
+ pet_id: "",
+ veterinarian_id: "",
+ date: "",
+ hour: "",
+ comments: ""
+ });
+
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const [resAppt, resUsers, resPets, resVets] = await Promise.all([
+ fetch(`${backendUrl}/api/medical-appointments/${id}`),
+ fetch(`${backendUrl}/api/user`),
+ fetch(`${backendUrl}/api/pets`),
+ fetch(`${backendUrl}/api/veterinarians`)
+ ]);
+
+ if (resUsers.ok) setUsers(await resUsers.json());
+ if (resPets.ok) setPets(await resPets.json());
+ if (resVets.ok) setVeterinarians(await resVets.json());
+
+ if (resAppt.ok) {
+ const data = await resAppt.json();
+ setAppointment({
+ user_id: data.user_id || data.idUser || "",
+ pet_id: data.pet_id || data.idPet || "",
+ veterinarian_id: data.veterinarian_id || data.idVeterinarian || "",
+ date: data.date || "",
+ hour: data.hour || "",
+ comments: data.comments || ""
+ });
+ }
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [id, backendUrl]);
+
+ if (loading) {
+ return (
+
+
Loading medical appointment details...
+
+ );
+ }
+
+ return (
+
+
+ ← Back to Medical Appointments List
+
+
+
+
View Medical Appointment
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/MedicalAppointmentsList.jsx b/src/front/pages/MedicalAppointmentsList.jsx
new file mode 100644
index 0000000000..0a0c6f4693
--- /dev/null
+++ b/src/front/pages/MedicalAppointmentsList.jsx
@@ -0,0 +1,89 @@
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import { MedicalAppointmentCard } from "../components/MedicalAppointmentCard";
+
+export const MedicalAppointmentsList = () => {
+ const [appointments, setAppointments] = useState([]);
+ const [users, setUsers] = useState([]);
+ const [pets, setPets] = useState([]);
+ const [veterinarians, setVeterinarians] = useState([]);
+
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const fetchAppointments = async () => {
+ try {
+ const response = await fetch(`${backendUrl}/api/medical-appointments`);
+ if (response.ok) {
+ const data = await response.json();
+ setAppointments(data);
+ }
+ } catch (error) {
+ console.error("Error fetching medical appointments:", error);
+ }
+ };
+
+ const fetchRelatedData = async () => {
+ try {
+ const [resUsers, resPets, resVeterinarians] = await Promise.all([
+ fetch(`${backendUrl}/api/user`),
+ fetch(`${backendUrl}/api/pets`),
+ fetch(`${backendUrl}/api/veterinarians`)
+ ]);
+
+ if (resUsers.ok) setUsers(await resUsers.json());
+ if (resPets.ok) setPets(await resPets.json());
+ if (resVeterinarians.ok) setVeterinarians(await resVeterinarians.json());
+ } catch (error) {
+ console.error("Error fetching related data:", error);
+ }
+ };
+
+ useEffect(() => {
+ fetchAppointments();
+ fetchRelatedData();
+ }, []);
+
+ const handleDelete = async (id) => {
+ if (!window.confirm("Are you sure you want to delete this medical appointment?")) return;
+
+ try {
+ const response = await fetch(`${backendUrl}/api/medical-appointments/${id}`, {
+ method: "DELETE"
+ });
+
+ if (response.ok) {
+ fetchAppointments();
+ }
+ } catch (error) {
+ console.error("Error deleting medical appointment:", error);
+ }
+ };
+
+ return (
+
+
+
Medical Appointments List
+
+ + Create Medical Appointment
+
+
+
+ {appointments.length === 0 ? (
+
No medical appointments registered yet.
+ ) : (
+
+ {appointments.map((appointment) => (
+
+ ))}
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/PetDetail.jsx b/src/front/pages/PetDetail.jsx
new file mode 100644
index 0000000000..5dc8f4845a
--- /dev/null
+++ b/src/front/pages/PetDetail.jsx
@@ -0,0 +1,284 @@
+import React, { useState, useEffect } from "react";
+import { useParams, Link, useNavigate } from "react-router-dom";
+
+export const PetDetail = () => {
+ const { id } = useParams();
+ const navigate = useNavigate();
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const [loading, setLoading] = useState(true);
+ const [users, setUsers] = useState([]);
+ const [shelters, setShelters] = useState([]);
+ const [breeds, setBreeds] = useState([]);
+
+ const [pet, setPet] = useState({
+ name: "",
+ genre: "male",
+ birthDate: "",
+ castrated: false,
+ chipNumber: "",
+ color: "",
+ photoUrl: "",
+ size: "medium",
+ user_id: "",
+ shelter_id: "",
+ breed_id: ""
+ });
+
+ useEffect(() => {
+ const fetchPetData = async () => {
+ try {
+ const [resUsers, resShelters, resBreeds, resPet] = await Promise.all([
+ fetch(`${backendUrl}/api/user`),
+ fetch(`${backendUrl}/api/shelter`),
+ fetch(`${backendUrl}/api/breed`),
+ fetch(`${backendUrl}/api/pets/${id}`)
+ ]);
+
+ if (resUsers.ok) setUsers(await resUsers.json());
+ if (resShelters.ok) setShelters(await resShelters.json());
+ if (resBreeds.ok) setBreeds(await resBreeds.json());
+
+ if (resPet.ok) {
+ const data = await resPet.json();
+ const uId = data.user_id || data.idUser || "";
+ const sId = data.shelter_id || data.idShelter || "";
+ const bId = data.breed_id || data.idBreed || "";
+
+ setPet({
+ ...data,
+ birthDate: data.birthDate || "",
+ chipNumber: data.chipNumber || "",
+ photoUrl: data.photoUrl || "",
+ user_id: uId ? String(uId) : "",
+ shelter_id: sId ? String(sId) : "",
+ breed_id: bId ? String(bId) : ""
+ });
+ }
+ } catch (error) {
+ console.error("Error fetching pet details:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPetData();
+ }, [id, backendUrl]);
+
+ const handleChange = (e) => {
+ const { name, value, type, checked } = e.target;
+ setPet({
+ ...pet,
+ [name]: type === "checkbox" ? checked : value
+ });
+ };
+
+ const handleSave = async (e) => {
+ e.preventDefault();
+
+ const payload = {
+ name: pet.name,
+ genre: pet.genre,
+ size: pet.size,
+ birthDate: pet.birthDate,
+ color: pet.color,
+ chipNumber: pet.chipNumber,
+ photoUrl: pet.photoUrl,
+ castrated: pet.castrated,
+ user_id: pet.user_id ? Number(pet.user_id) : null,
+ shelter_id: pet.shelter_id ? Number(pet.shelter_id) : null,
+ breed_id: pet.breed_id ? Number(pet.breed_id) : null
+ };
+
+ try {
+ const response = await fetch(`${backendUrl}/api/pet-detail/${id}`, {
+ method: "PUT",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify(payload)
+ });
+
+ if (response.ok) {
+ alert("Pet updated successfully!");
+ navigate("/pets");
+ } else {
+ const errorData = await response.json();
+ console.error("Error del servidor:", errorData);
+ alert("Failed to update pet");
+ }
+ } catch (error) {
+ console.error("Error updating pet:", error);
+ }
+ };
+
+ if (loading) {
+ return (
+
+
Loading pet details...
+
+ );
+ }
+
+ return (
+
+
+ ← Back to Pets List
+
+
+
+
Edit Pet
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/PetView.jsx b/src/front/pages/PetView.jsx
new file mode 100644
index 0000000000..905f920dc8
--- /dev/null
+++ b/src/front/pages/PetView.jsx
@@ -0,0 +1,102 @@
+import React, { useState, useEffect } from "react";
+import { useParams, Link } from "react-router-dom";
+
+export const PetView = () => {
+ const { id } = useParams();
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const [loading, setLoading] = useState(true);
+ const [pet, setPet] = useState(null);
+ const [users, setUsers] = useState([]);
+ const [shelters, setShelters] = useState([]);
+ const [breeds, setBreeds] = useState([]);
+
+ useEffect(() => {
+ const fetchPetData = async () => {
+ try {
+ const [resUsers, resShelters, resBreeds, resPet] = await Promise.all([
+ fetch(`${backendUrl}/api/user`),
+ fetch(`${backendUrl}/api/shelter`),
+ fetch(`${backendUrl}/api/breed`),
+ fetch(`${backendUrl}/api/pets/${id}`)
+ ]);
+
+ if (resUsers.ok) setUsers(await resUsers.json());
+ if (resShelters.ok) setShelters(await resShelters.json());
+ if (resBreeds.ok) setBreeds(await resBreeds.json());
+
+ if (resPet.ok) {
+ const data = await resPet.json();
+ setPet(data);
+ }
+ } catch (error) {
+ console.error("Error fetching pet details:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchPetData();
+ }, [id, backendUrl]);
+
+ if (loading) {
+ return (
+
+
Loading pet details...
+
+ );
+ }
+
+ if (!pet) {
+ return (
+
+
Pet not found.
+
+ ← Back to Pets List
+
+
+ );
+ }
+
+ const breedName = breeds.find(b => b.id === pet.breed_id)?.breedName || "Not specified";
+ const userName = users.find(u => u.id === pet.user_id)?.name || "Not assigned";
+ const shelterName = shelters.find(s => s.id === pet.shelter_id)?.name || "Not assigned";
+
+ return (
+
+
+ ← Back to Pets List
+
+
+
+
+ {pet.photoUrl && (
+
+

+
+ )}
+
+
+
{pet.name}
+
+
Breed: {breedName}
+
User: {userName}
+
Shelter: {shelterName}
+
Gender: {pet.genre}
+
Size: {pet.size}
+
Color: {pet.color}
+
Birth Date: {pet.birthDate || "Not provided"}
+
Chip Number: {pet.chipNumber || "No chip"}
+
Castrated: {pet.castrated ? "Yes" : "No"}
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/PetsList.jsx b/src/front/pages/PetsList.jsx
new file mode 100644
index 0000000000..8f210a8c31
--- /dev/null
+++ b/src/front/pages/PetsList.jsx
@@ -0,0 +1,77 @@
+import React, { useState, useEffect } from "react";
+import { Link } from "react-router-dom";
+import { PetCard } from "../components/PetCard";
+
+export const PetsList = () => {
+ const [pets, setPets] = useState([]);
+ const [breeds, setBreeds] = useState([]);
+ const [shelters, setShelters] = useState([]);
+ const [users, setUsers] = useState([]);
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+
+ const fetchData = async () => {
+ try {
+ const [resPets, resBreeds, resShelters, resUsers] = await Promise.all([
+ fetch(`${backendUrl}/api/pets`),
+ fetch(`${backendUrl}/api/breed`),
+ fetch(`${backendUrl}/api/shelter`),
+ fetch(`${backendUrl}/api/user`)
+ ]);
+
+ if (resPets.ok) setPets(await resPets.json());
+ if (resBreeds.ok) setBreeds(await resBreeds.json());
+ if (resShelters.ok) setShelters(await resShelters.json());
+ if (resUsers.ok) setUsers(await resUsers.json());
+ } catch (error) {
+ console.error("Error fetching data:", error);
+ }
+ };
+
+ useEffect(() => {
+ fetchData();
+ }, []);
+
+ const handleDelete = async (id) => {
+ if (!window.confirm("Are you sure you want to delete this pet?")) return;
+
+ try {
+ const response = await fetch(`${backendUrl}/api/pets/${id}`, {
+ method: "DELETE"
+ });
+
+ if (response.ok) {
+ fetchData();
+ }
+ } catch (error) {
+ console.error("Error deleting pet:", error);
+ }
+ };
+
+ return (
+
+
+
Pets List
+
+ + Create Pet
+
+
+
+ {pets.length === 0 ? (
+
No pets registered yet.
+ ) : (
+
+ {pets.map((pet) => (
+
+ ))}
+
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/Shelter.jsx b/src/front/pages/Shelter.jsx
new file mode 100644
index 0000000000..9846661182
--- /dev/null
+++ b/src/front/pages/Shelter.jsx
@@ -0,0 +1,82 @@
+import { Link, useActionData } from "react-router-dom";
+import { useEffect, useState } from "react";
+import rigoImageUrl from "../assets/img/rigo-baby.jpg";
+import useGlobalReducer from "../hooks/useGlobalReducer.jsx";
+import { CardShelter } from "../components/CardShelter.jsx";
+import { useNavigate } from "react-router-dom"
+
+
+export const Shelter = () => {
+
+const [shelter, setShelter] = useState([]);
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+ const navigate = useNavigate()
+
+ const fetchShelter = async () => {
+ try {
+ const response = await fetch(`${backendUrl}/api/shelter`);
+ if (response.ok) {
+ const data = await response.json();
+ setShelter(data);
+ }
+ } catch (error) {
+ console.error("Error fetching shelters:", error);
+ }
+ };
+
+const handleDeleteShelter = (id) => {
+ setShelter(prev => prev.filter(item => item.id !== id));
+};
+
+
+ useEffect(() => {
+ fetchShelter();
+ }, []);
+
+
+
+ return (
+ <>
+
+
+
+
+{/*
+
Refugios
+
+ */}
+
+
Refugios
+
+
+
+ {shelter.map((shelter, index) => (
+
+
+
+
+ ))}
+
+
+
+
+
+
+ Back home
+
+
+ >
+ );
+};
\ No newline at end of file
diff --git a/src/front/pages/ShelterView.jsx b/src/front/pages/ShelterView.jsx
new file mode 100644
index 0000000000..3954e68f37
--- /dev/null
+++ b/src/front/pages/ShelterView.jsx
@@ -0,0 +1,78 @@
+import { Link, useActionData, useParams } from "react-router-dom";
+import { useState, useEffect } from "react";
+import rigoImageUrl from "../assets/img/rigo-baby.jpg";
+import useGlobalReducer from "../hooks/useGlobalReducer.jsx";
+import React from "react";
+
+export const ShelterView = (props) => {
+ const { id } = useParams();
+ const backendUrl = import.meta.env.VITE_BACKEND_URL;
+ const [shelter, setShelter] = useState(null);
+
+ useEffect(() => {
+ const getShelter = async () => {
+ try {
+ const res = await fetch(`${backendUrl}/api/shelter/${id}`);
+ if (!res.ok) return;
+ const data = await res.json();
+ setShelter(data);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+ if (id) getShelter();
+ }, [id]);
+
+ const handleDelete = async () => {
+ try {
+ const response = await fetch(`${backendUrl}/api/shelter/${id}`, { method: "DELETE" });
+ if (!response.ok) {
+ console.error("Error al eliminar el refugio");
+ return;
+ }
+ if (props.onDelete) props.onDelete(id);
+ } catch (error) {
+ console.error("Error:", error);
+ }
+ };
+
+ if (!shelter) return
Cargando refugio...
;
+
+/* return (
+
+

+
+
{shelter.name}
+
+