Creazione di un'applicazione web che consente a medici (con competenze in fisioterapia) o personal trainer di gestire piani di esercizi fisici settimanali per i propri pazienti o clienti. L'applicazione permette la registrazione e l'autenticazione sicura degli utenti, la gestione dei dati anagrafici dei pazienti, e la definizione personalizzata degli esercizi per ciascun giorno della settimana.
Ogni esercizio è caratterizzato da informazioni come nome, descrizione, durata o numero di ripetizioni, livello di difficoltà ed eventuali note. È possibile suddividere gli esercizi per giorni della settimana e orari, oltre a salvare e modificare i piani in qualsiasi momento grazie all’integrazione con Firestore. È inoltre prevista la possibilità di aggiungere nuovi esercizi personalizzati al database.
La pagina di login rappresenta il punto di accesso per gli utenti registrati del progetto. È sviluppata con React, utilizzando il framework Next.js e utilizza Firebase Authentication per gestire l'autenticazione tramite email e password.
- Autenticazione con Firebase: Login con email e password tramite
signInWithEmailAndPassword. - Gestione degli errori: Visualizzazione del messaggio di errore in caso di credenziali non valide.
- Reindirizzamento post-login: In caso di login riuscito, l'utente viene reindirizzato alla pagina
/dashboard/schede. - Collegamento alla registrazione: Link diretto,
/register/, per accedere alla pagina di registrazione se l’utente non ha un account registrato.
- React / Next.js
- TypeScript
- Firebase Authentication
- Tailwind CSS
- Componenti Custom (UI):
Input,ButtoneImagesono componenti riutilizzabili per uniformare l’interfaccia.
email: contiene l'input dell'email inserita dall'utente.password: contiene la password inserita.error: contiene l'eventuale errore generato dal login.
- Previene il comportamento di default del form (
e.preventDefault()). - Chiamata a
signInWithEmailAndPassword()conemailepassword. - Se l'autenticazione va a buon fine, reindirizza alla dashboard.
- In caso contrario, aggiorna lo stato
errorper mostrare il messaggio di errore all’utente.
La pagina è centrata verticalmente e orizzontalmente. Contiene:
- Il logo del progetto.
- Un titolo e un link per la registrazione.
- Un form con:
- Input per email e password.
- Messaggio di errore, se presente.
- Pulsante di login.
La pagina di registrazione, /page.tsx, permette all'utente di registrarsi all'applicazione, utilizzando un componente di supporto /RegisterComponent.tsx.
- Registrazione tramite Firebase: Crea un nuovo utente usando
createUserWithEmailAndPassword. - Salvataggio del profilo utente su Firestore: Dopo la creazione dell'utente, vengono salvati
nome,cognomee la data di creazione nel percorsousers/{uid}/users-fitney/profile. - Gestione errori: In caso di problemi nella registrazione, viene mostrato un messaggio di errore all’utente.
- Reindirizzamento post-registrazione: Dopo essersi registrato, l'utente viene reindirizzato alla pagina di login (
/login). - Collegamento alla pagina di login: Per gli utenti già registrati.
- React / Next.js
- TypeScript
- Firebase Authentication
- Firebase Firestore
- Tailwind CSS
- Componenti UI personalizzati:
Input,Button,Image.
name,surname,email,password: input che vengono gestiti tramite l'hook fornito da react:useState.error: stato per visualizzare eventuali errori a schermo.
- Previene il comportamento predefinito del form.
- Crea l’utente con
createUserWithEmailAndPassword. - Recupera l’UID dell’utente autenticato.
- Crea un documento Firestore nel percorso
users/{uid}/users-fitney/profilecon nome, cognome e timestamp. - Reindirizza alla pagina di login.
- In caso di errore, lo salva nello stato per mostrarlo all'utente.
La UI è centrata nello schermo e include:
- Logo del progetto.
- Titolo "Registrati" con link per chi ha già un account.
- Form con:
- Input divisi tra
nomeecognome. - Email e password.
- Messaggio di errore.
- Pulsante per inviare i dati.
- Input divisi tra
La pagina /dashboard/schede è il cuore della dashboard utente all'interno del progetto Fitney, dedicata alla gestione delle schede di allenamento dei clienti. Una volta autenticato, il Personal Trainer può visualizzare le schede esistenti e accedere a una modale per aggiungerne di nuove,modificarle oppure eliminarle.
- Controllo autenticazione Firebase: Se l’utente non è autenticato, viene reindirizzato alla pagina di login (
/login). - Recupero dinamico del nome utente: Viene richiamata una funzione asincrona per mostrare un saluto personalizzato.
- Modale per aggiunta schede: È presente un pulsante per aprire una modale (
AddSchedule) dove inserire nuove schede. - Visualizzazione elenco schede: Il componente
SchedeSelectionsi occupa di visualizzare le schede esistenti. - Sidebar persistente: La
Sidebarconsente di navigare tra le sezioni dell’applicazione.
nome: Nome dell’utente, recuperato da Firestore.loading: Stato di caricamento durante il recupero del nome.showModal: Booleano che controlla l'apertura della modale per aggiungere una nuova scheda.
useEffect(() => {
const unsub = onAuthStateChanged(auth, (user) => {
if (!user) {
router.push("/login");
}
});
return () => unsub();
}, [router]);useEffect(() => {
getUserName()
.then((userName) => setNome(userName))
.catch((error) => {
console.error("Errore nel recupero del nome:", error);
setNome("");
})
.finally(() => setLoading(false));
}, []);Componente per la creazione di una nuova scheda di allenamento associata ad un cliente.
Questo componente visualizza un modale che permette al personal trainer di:
- Selezionare un cliente associato.
- Selezionare uno o più esercizi personalizzati.
- Scegliere un giorno della settimana.
- Inserire una nota.
- Creare e salvare una nuova scheda di allenamento nel database Firebase (Firestore).
| Stato | Tipo | Descrizione |
|---|---|---|
user |
User | null |
Utente attualmente autenticato |
clients |
any[] |
Lista dei clienti associati al trainer |
exercises |
any[] |
Lista degli esercizi creati dal trainer |
selectedClient |
string |
ID del cliente selezionato |
selectedExercises |
string[] |
Lista degli ID degli esercizi selezionati |
note |
string |
Nota associata alla scheda |
day |
string |
Giorno della settimana selezionato |
- Recupera l'utente autenticato (
onAuthStateChanged) - Recupera gli esercizi filtrati per
user.uid - Recupera i clienti filtrati per
trainerId
Aggiunge o rimuove un esercizio dalla lista degli esercizi selezionati.
Salva la nuova scheda in Firestore (schedules) con i seguenti campi:
clientIdexercisesnotedaytrainerId
Se l'utente non è autenticato, viene mostrato un alert.
Componente che consente ai personal trainer di visualizzare, filtrare, modificare e eliminare le schede di allenamento. In particolare, utilizza Firebase Firestore per il recupero e per la modifica dei dati.
- Recupero delle schede associate all’utente loggato (come client o trainer).
- Visualizzazione degli esercizi e dettagli associati.
- Filtraggio per giorno della settimana e per nome cliente.
- Modifica e salvataggio delle schede.
- Eliminazione delle schede con conferma.
- Visualizzazione in modal dei dettagli scheda.
const fetchSchedules = = async () => {
const user = getAuth().currentUser;
if (!user) return;
const qClient = query(
collection(db, "schedules"),
where("clientId", "==", user.uid)
);
const qTrainer = query(
collection(db, "schedules"),
where("trainerId", "==", user.uid)
);
const [clientSnap, trainerSnap] = await Promise.all([
getDocs(qClient),
getDocs(qTrainer),
]);
const clientSchedules = clientSnap.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
const trainerSchedules = trainerSnap.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
const allSchedules = [...clientSchedules, ...trainerSchedules];
const uniqueSchedules = Array.from(
new Map(allSchedules.map((s) => [s.id, s])).values()
);
setSchedules(uniqueSchedules);
};Recupera tutte le schede dal database Firebase relative al current.user, quindi all'utente corrente.
In particolare, popola lo state schedules con i documenti trovati nella collezione schedules.
const fetchExercises = async () => {
const querySnapshot = await getDocs(collection(db, "exercises"));
const exercisesArray = querySnapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setAllExercises(exercisesArray);
};Recupera tutti gli esercizi disponibili nella collezione exercises, popola lo state allExercises.
const fetchClients = async () => {
const user = getAuth().currentUser;
if (!user) return;
const q = query(
collection(db, "clients"),
where("trainerId", "==", user.uid)
);
const clientsSnap = await getDocs(q);
const clientsArray = clientsSnap.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}));
setAllClients(clientsArray);
};Recupera la lista dei clienti assegnati al personal trainer attualmente loggato; popola lo state allClients.
const handleDelete = async (id: string) => {
if (!confirm("Vuoi davvero eliminare questa scheda?")) return;
try {
await deleteDoc(doc(db, "schedules", id));
setSchedules((prev) => prev.filter((s) => s.id !== id));
} catch (error) {
console.error(error);
}
};Elimina una specifica scheda da Firebase.
- Parametri:
id: l'ID della scheda da eliminare.
Dopo aver rimosso la scheda, aggiorna lo state schedules, in modo tale da riflettere correttamente le modifiche.
const handleEditClick = (schedule: any) => {
setEditingSchedule(schedule);
setFormData({
note: schedule.note || "",
day: schedule.day || "",
exercises: schedule.exercises || [],
});
setModalOpen(true);
};Apre il modale per la modifica della scheda selezionata.
Popola formData con i dati della scheda selezionata.
const handleSave = async () => {
if (!editingSchedule) return;
try {
const docRef = doc(db, "schedules", editingSchedule.id);
await updateDoc(docRef, formData);
setSchedules((prev) =>
prev.map((s) =>
s.id === editingSchedule.id ? { ...s, ...formData } : s
)
);
setModalOpen(false);
setEditingSchedule(null);
} catch (error) {
console.error(error);
}
};Salva le modifiche fatte alla scheda e aggiorna il documento in Firestore.
Per poter visualizzare i clienti in base al giorno della settimana oppure in base al nome, è stato implementato questo filtro:
const filteredSchedules = schedules.filter((s) => {
const client = allClients.find((c) => c.id === s.clientId);
if (!client) return false; // se non ha ancora caricato il client, escludi
const fullName = `${client.nome} ${client.cognome}`.toLowerCase();
const matchesDay = selectedDay === "Tutti" || s.day === selectedDay;
const matchesName = fullName.includes(searchName.toLowerCase());
return matchesDay && matchesName;
});
/dashboard/clienti
Questa pagina permette di selezionare,aggiungere ed eliminare i clienti da parte del personal trainer loggato.
---
Questa pagina permette di aggiungere un nuovo cliente a una collezione Firebase Firestore tramite un modale.
I dati vengono salvati nella collezione clients, con il campo trainerId:
await addDoc(collection(db, "clients"), {
nome,
cognome,
età: Number(età),
limitazioni,
trainerId: user?.uid,
});Questa pagina,
/dashboard/esercizi/Esercizi.tsx, è dedicata alla visualizzazione e modifica degli esercizi.
In particolare l'aggiunta di esercizi è implementata in /esercizi/addExercise.tsx.
Grazie a questo codice:
try {
await addDoc(collection(db, "exercises"), {
nome,
difficoltà,
note,
ripetizioni,
userId: user.uid,
});
onClose();
} catch (e: any) {
setError("Errore durante il salvataggio: " + e.message);
}
};Possiamo aggiungere alla collezione exercises un nuovo documento che rappresenta il nostro nuovo esercizio che comprenderà il nome dell'esercizio, la difficlotà, note aggiuntive, il numero di ripetizione e l'ID dell'utente loggato, quindi del personal trainer.
Questa pagina permette agli utenti autenticati di visualizzare,modificare ed eliminare degli esercizi salvati nel database Firestore.
Recupero degli esercizi
const q = query(
collection(db, "exercises"),
where("userId", "==", user.uid)
);
const querySnapshot = await getDocs(q);Tramite questa query al database, db, alla collezione exercises andiamo a filtrare tutti gli esercizi, ottenendo solo quelli che hanno uno userId uguale user.uid dell'utente corrente.
Eliminazione e Modifica degli esercizi
try {
await deleteDoc(doc(db, "exercises", id));
setExercises((prev) => prev.filter((ex) => ex.id !== id));
} catch (error) {
console.error("Errore durante l'eliminazione:", error);
}
};
const handleEditClick = (exercise: Exercise) => {
setEditingExercise(exercise);
setFormData({
nome: exercise.nome,
ripetizioni: exercise.ripetizioni,
difficoltà: exercise.difficoltà,
note: exercise.note,
});
setModalOpen(true);
};Questa pagina è dedicata al Sign Out dell'utente utilizzando una funzione fornita da Firebase, che elimina i cookie relativi alla sessione dell'utente, garantendo una maggiore sicurezza.
Il codice relativo è:
const router = useRouter();
const handleSignOut = async () => {
try{
await signOut(auth);
router.push("/login")
}catch(err: any){
console.log(err.message);
}
}