Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {AuthProvider} from "./context/AuthContext";
import {Toaster} from "react-hot-toast";
import type {JSX} from "react";
import {AnimalsProvider} from "./context/AnimalsContext.tsx";
import { AdoptionsProvider } from "./context/AdoptionsContext";

const ProtectedLayout = ({children}: { children: JSX.Element }) => (
<div className="flex h-screen bg-gray-100">
Expand All @@ -34,32 +35,34 @@ export default function App() {
return (
<AuthProvider>
<AnimalsProvider>
<Toaster position="top-right" reverseOrder={false}/>
<AdoptionsProvider>
<Toaster position="top-right" reverseOrder={false}/>

<Router>
<Routes>
<Route path="/login" element={<Login/>}/>
<Router>
<Routes>
<Route path="/login" element={<Login/>}/>

<Route path="/" element={<ProtectedRoute element={<Dashboard/>}/>}/>
<Route path="/dashboard" element={<ProtectedRoute element={<Dashboard/>}/>}/>
<Route path="/" element={<ProtectedRoute element={<Dashboard/>}/>}/>
<Route path="/dashboard" element={<ProtectedRoute element={<Dashboard/>}/>}/>

<Route path="/animals" element={<ProtectedRoute element={<AnimalRegister/>}/>}/>
<Route path="/animalList" element={<ProtectedRoute element={<AnimalList/>}/>}/>
<Route path="/animals" element={<ProtectedRoute element={<AnimalRegister/>}/>}/>
<Route path="/animalList" element={<ProtectedRoute element={<AnimalList/>}/>}/>

<Route path="/staff" element={<ProtectedRoute element={<StaffRegister/>}/>}/>
<Route path="/staffList" element={<ProtectedRoute element={<StaffList/>}/>}/>
<Route path="/staff" element={<ProtectedRoute element={<StaffRegister/>}/>}/>
<Route path="/staffList" element={<ProtectedRoute element={<StaffList/>}/>}/>

<Route path="/Adopter" element={<ProtectedRoute element={<AdopterRegister/>}/>}/>
<Route path="/AdopterList" element={<ProtectedRoute element={<AdopterList/>}/>}/>
<Route path="/Adopter" element={<ProtectedRoute element={<AdopterRegister/>}/>}/>
<Route path="/AdopterList" element={<ProtectedRoute element={<AdopterList/>}/>}/>

<Route path="/vaccines" element={<ProtectedRoute element={<VaccineRegister/>}/>}/>
<Route path="/vaccines" element={<ProtectedRoute element={<VaccineRegister/>}/>}/>

<Route path="/adoptions" element={<ProtectedRoute element={<AdoptionRegister/>}/>}/>
<Route path="/AdoptionList" element={<ProtectedRoute element={<AdoptionList/>}/>}/>
<Route path="/adoptions" element={<ProtectedRoute element={<AdoptionRegister/>}/>}/>
<Route path="/AdoptionList" element={<ProtectedRoute element={<AdoptionList/>}/>}/>

<Route path="*" element={<ProtectedRoute element={<Dashboard/>}/>}/>
</Routes>
</Router>
<Route path="*" element={<ProtectedRoute element={<Dashboard/>}/>}/>
</Routes>
</Router>
</AdoptionsProvider>
</AnimalsProvider>
</AuthProvider>
);
Expand Down
48 changes: 48 additions & 0 deletions src/context/AdoptionsContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { createContext, useContext, useEffect, useState, type ReactNode } from "react";
import { collection, onSnapshot } from "firebase/firestore";
import { db } from "../lib/firebase";

export type Adoption = {
id: string;
adopterId: string;
animalId: string;
employeeId: string;
status: string;
notes?: string;
adoptionDate?: string;
};

type AdoptionsContextType = {
adoptions: Adoption[];
};

const AdoptionsContext = createContext<AdoptionsContextType | undefined>(undefined);

export const AdoptionsProvider = ({ children }: { children: ReactNode }) => {
const [adoptions, setAdoptions] = useState<Adoption[]>([]);

useEffect(() => {
const adoptionsCol = collection(db, "adoptions");
const unsubscribe = onSnapshot(adoptionsCol, (snapshot) => {
const formatted = snapshot.docs.map((doc) => ({
id: doc.id,
...(doc.data() as Omit<Adoption, "id">),
}));
setAdoptions(formatted);
});

return () => unsubscribe();
}, []);

return (
<AdoptionsContext.Provider value={{ adoptions }}>
{children}
</AdoptionsContext.Provider>
);
};

export const useAdoptions = () => {
const context = useContext(AdoptionsContext);
if (!context) throw new Error("useAdoptions deve ser usado dentro de um AdoptionsProvider");
return context;
};
44 changes: 34 additions & 10 deletions src/pages/Animal/AnimalList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,28 @@ export default function AnimalList() {

const calcularIdade = (birthDate?: string) => {
if (!birthDate) return "Não informado";

const nascimento = new Date(birthDate);
const hoje = new Date();
let idade = hoje.getFullYear() - nascimento.getFullYear();
const m = hoje.getMonth() - nascimento.getMonth();
if (m < 0 || (m === 0 && hoje.getDate() < nascimento.getDate())) idade--;
return `${idade} ano${idade !== 1 ? "s" : ""}`;

let anos = hoje.getFullYear() - nascimento.getFullYear();
let meses = hoje.getMonth() - nascimento.getMonth();
let dias = hoje.getDate() - nascimento.getDate();

if (dias < 0) {
meses--;
const ultimoDiaMesAnterior = new Date(hoje.getFullYear(), hoje.getMonth(), 0).getDate();
dias += ultimoDiaMesAnterior;
}

if (meses < 0) {
anos--;
meses += 12;
}

if (anos > 0) return `${anos} ano${anos !== 1 ? "s" : ""}`;
if (meses > 0) return `${meses} mês${meses !== 1 ? "es" : ""}`;
return `${dias} dia${dias !== 1 ? "s" : ""}`;
};

const inputStyle = (error?: boolean) =>
Expand Down Expand Up @@ -161,7 +177,6 @@ export default function AnimalList() {
</div>
)}

{/* Modal de Edição */}
<AnimatePresence>
{editForm && (
<motion.div
Expand Down Expand Up @@ -309,10 +324,19 @@ export default function AnimalList() {
)}
</AnimatePresence>

{/* Modal de Exclusão */}
{deleteAnimal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 animate-fadeIn">
<div className="bg-white rounded-2xl shadow-xl w-full max-w-md p-6 animate-scaleIn">
<motion.div
className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<motion.div
className="bg-white rounded-2xl shadow-xl w-full max-w-md p-6 relative overflow-hidden"
initial={{ scale: 0.9 }}
animate={{ scale: 1 }}
exit={{ scale: 0.9 }}
>
<h2 className="text-lg font-bold text-gray-800 mb-4">Confirmar exclusão</h2>
<p className="text-gray-600 mb-6">
Deseja realmente excluir <strong>{deleteAnimal.name}</strong>?
Expand All @@ -331,8 +355,8 @@ export default function AnimalList() {
Excluir
</button>
</div>
</div>
</div>
</motion.div>
</motion.div>
)}
</div>
);
Expand Down
4 changes: 3 additions & 1 deletion src/pages/Animal/AnimalRegister.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ export default function AnimalRegister() {
const validate = () => {
const newErrors: { [key: string]: string } = {};
Object.entries(formData).forEach(([key, value]) => {
if (!value) newErrors[key] = "Campo obrigatório";
if (key !== "notes" && !value) {
newErrors[key] = "Campo obrigatório";
}
});
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
Expand Down
Loading