diff --git a/build.gradle b/build.gradle index f2a60e9452b42d7ea069c74c7711bea3a6463b8d..077b7ac5a3b05a83a993c6d3ba69a1b357d2fa21 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-mail' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + implementation 'org.springframework.boot:spring-boot-starter:3.2.1' + implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'org.springframework.ai:spring-ai-tika-document-reader' diff --git a/src/main/java/com/projet/projetIndu/controllers/AdminController.java b/src/main/java/com/projet/projetIndu/controllers/AdminController.java index 7b5cfa3eb850de60869b78798c9c5c7075de9557..b3fc248bc2904759c9ff3850897ddcb49894a50b 100644 --- a/src/main/java/com/projet/projetIndu/controllers/AdminController.java +++ b/src/main/java/com/projet/projetIndu/controllers/AdminController.java @@ -3,10 +3,12 @@ package com.projet.projetIndu.controllers; import com.projet.projetIndu.entities.Doctor; import com.projet.projetIndu.services.DoctorService; import com.projet.projetIndu.services.PatientService; +import com.projet.projetIndu.services.InvoiceService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -19,6 +21,9 @@ public class AdminController { @Autowired private PatientService patientService; + @Autowired + private InvoiceService invoiceService; + @GetMapping("/dashboard") public String adminDashboard() { return "admin-dashboard"; @@ -47,4 +52,17 @@ public class AdminController { model.addAttribute("doctors", doctorService.getAllDoctors()); return "doctors"; } + @GetMapping("/invoices") + public String invoices(Model model) { + model.addAttribute("invoices", invoiceService.getAllInvoices()); + return "invoices"; // Page HTML pour afficher les factures + } + + @PostMapping("/invoices/{id}/pay") + public String markInvoiceAsPaid(@PathVariable Long id) { + invoiceService.markAsPaid(id); + return "redirect:/admin/invoices"; + } + + } diff --git a/src/main/java/com/projet/projetIndu/controllers/DoctorController.java b/src/main/java/com/projet/projetIndu/controllers/DoctorController.java index d365e24006e52c051b1775d5add2570de3429d27..7518b4c1122664299bef67388d02a02ffd84f61f 100644 --- a/src/main/java/com/projet/projetIndu/controllers/DoctorController.java +++ b/src/main/java/com/projet/projetIndu/controllers/DoctorController.java @@ -1,15 +1,17 @@ package com.projet.projetIndu.controllers; import com.projet.projetIndu.entities.Doctor; +import com.projet.projetIndu.entities.Invoice; import com.projet.projetIndu.entities.Patient; import com.projet.projetIndu.services.DoctorService; +import com.projet.projetIndu.services.InvoiceService; import com.projet.projetIndu.services.PatientService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; +import java.security.Principal; import java.util.List; @RequestMapping("/doctors") @@ -18,6 +20,9 @@ public class DoctorController { private final DoctorService doctorService; private final PatientService patientService; + @Autowired + private InvoiceService invoiceService; + public DoctorController(DoctorService doctorService, PatientService patientService) { this.doctorService = doctorService; @@ -49,4 +54,21 @@ public class DoctorController { model.addAttribute("patients", patients); return "patients"; } + /*@GetMapping("/invoices") + public String viewDoctorInvoices(Model model, Principal principal) { + // Récupérer le nom d'utilisateur du médecin connecté (supposons qu'il soit stocké dans `principal`) + String doctorUsername = principal.getName(); + + // Récupérer les factures des patients du médecin connecté + List<Invoice> doctorInvoices = invoiceService.getInvoicesByDoctorUsername(doctorUsername); + + model.addAttribute("invoices", doctorInvoices); + return "doctor-invoices"; // Page HTML pour afficher les factures + } + + @PostMapping("/invoices/{id}/pay") + public String markInvoiceAsPaid(@PathVariable Long id) { + invoiceService.markAsPaid(id); + return "redirect:/doctor/invoices"; + }*/ } diff --git a/src/main/java/com/projet/projetIndu/entities/Appointment.java b/src/main/java/com/projet/projetIndu/entities/Appointment.java index 5af4c61c08ceb90a19d5f148a61ac29223fcb57c..db9c1d35b8a5c902ceddaba7e058947ad425c4a2 100644 --- a/src/main/java/com/projet/projetIndu/entities/Appointment.java +++ b/src/main/java/com/projet/projetIndu/entities/Appointment.java @@ -57,4 +57,7 @@ public class Appointment { } + public void setStatus(AppointmentStatus appointmentStatus) { + this.status= appointmentStatus; + } } diff --git a/src/main/java/com/projet/projetIndu/entities/Doctor.java b/src/main/java/com/projet/projetIndu/entities/Doctor.java index cc524e6a6a2b320b75e1d7d8c1c75d3ca20a98a1..c37255081b631f94e59de70d2b3feff124f25fe2 100644 --- a/src/main/java/com/projet/projetIndu/entities/Doctor.java +++ b/src/main/java/com/projet/projetIndu/entities/Doctor.java @@ -27,6 +27,4 @@ public class Doctor extends User { this.role = Role.DOCTOR; this.speciality = speciality; } - - } diff --git a/src/main/java/com/projet/projetIndu/entities/Invoice.java b/src/main/java/com/projet/projetIndu/entities/Invoice.java index af8b413d70da5d96259159d0ac97364fb9c76e2d..5e394bf75e34d6e26ba2c0bcd6aa37e4d17b44b5 100644 --- a/src/main/java/com/projet/projetIndu/entities/Invoice.java +++ b/src/main/java/com/projet/projetIndu/entities/Invoice.java @@ -1,9 +1,7 @@ package com.projet.projetIndu.entities; import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import java.time.LocalDateTime; @@ -12,22 +10,49 @@ import java.time.LocalDateTime; @Getter @Setter @NoArgsConstructor +@AllArgsConstructor public class Invoice { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(nullable = false) - private double amount; + @OneToOne(mappedBy = "invoice") + private Appointment appointment; - @Column(nullable = false, updatable = false) - private LocalDateTime issue_date = LocalDateTime.now(); + @Column(nullable = false) + private Double amount; @Enumerated(EnumType.STRING) - @Column(nullable = false) - private InvoiceStatus status; + private PaymentStatus status; @Column(nullable = false, updatable = false) - private LocalDateTime createdAt = LocalDateTime.now(); + private LocalDateTime issueDate = LocalDateTime.now(); + + + @ManyToOne + @JoinColumn(name = "doctor_id") + private Doctor doctor; // + + public Invoice(Appointment appointment, Double amount) { + this.appointment = appointment; + this.amount = amount; + this.status = PaymentStatus.PENDING; + this.issueDate = LocalDateTime.now(); + } + + public void setStatus(PaymentStatus status) { + this.status = status; + } + + public PaymentStatus getStatus() { + return status; + } + public Doctor getDoctor() { + return doctor; + } + + public void setDoctor(Doctor doctor) { + this.doctor = doctor; + } } diff --git a/src/main/java/com/projet/projetIndu/entities/MedicalDocument.java b/src/main/java/com/projet/projetIndu/entities/MedicalDocument.java index fab8f1650b5d1e210af010aab967644ba6c9ed1d..b8e88e6c27481edbb3399a84e5fa7d06fae61141 100644 --- a/src/main/java/com/projet/projetIndu/entities/MedicalDocument.java +++ b/src/main/java/com/projet/projetIndu/entities/MedicalDocument.java @@ -24,4 +24,11 @@ public class MedicalDocument { @JoinColumn(name = "medical_file_id", nullable = true) private MedicalFile medicalFile; + public String getFileName() { + return fileName; + } + + public byte[] getFileData() { + return fileData; + } } diff --git a/src/main/java/com/projet/projetIndu/entities/Patient.java b/src/main/java/com/projet/projetIndu/entities/Patient.java index 047a23746e42ecaea0ffeeaabf901472d0fe22f5..51be87cdfbb6eb05081d78bd525f92691761fd2a 100644 --- a/src/main/java/com/projet/projetIndu/entities/Patient.java +++ b/src/main/java/com/projet/projetIndu/entities/Patient.java @@ -30,5 +30,4 @@ public class Patient extends User { this.dateOfBirth = dateOfBirth; } - } diff --git a/src/main/java/com/projet/projetIndu/entities/PaymentStatus.java b/src/main/java/com/projet/projetIndu/entities/PaymentStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..d719826629516c91b9196db77ef36cf2db0ec3f3 --- /dev/null +++ b/src/main/java/com/projet/projetIndu/entities/PaymentStatus.java @@ -0,0 +1,7 @@ +package com.projet.projetIndu.entities; + +public enum PaymentStatus { + PENDING, // En attente + PAID, // Payé + CANCELLED // Annulé +} diff --git a/src/main/java/com/projet/projetIndu/entities/User.java b/src/main/java/com/projet/projetIndu/entities/User.java index 709f0de420efba52e07ace80394d1d2735e68240..9cee9114fd9036cef29e4ee54a2b450b80224605 100644 --- a/src/main/java/com/projet/projetIndu/entities/User.java +++ b/src/main/java/com/projet/projetIndu/entities/User.java @@ -61,6 +61,10 @@ public abstract class User { this.email = email; this.password = password; } + public void setId(Long id) { + } - + public long getId() { + return id; + } } diff --git a/src/main/java/com/projet/projetIndu/repositories/InvoiceRepository.java b/src/main/java/com/projet/projetIndu/repositories/InvoiceRepository.java index 38e7759b0e474f04bfccf69e12ed972daa60177c..a5a03a9750b422c1b3ffc8858df72a0503f294bb 100644 --- a/src/main/java/com/projet/projetIndu/repositories/InvoiceRepository.java +++ b/src/main/java/com/projet/projetIndu/repositories/InvoiceRepository.java @@ -2,7 +2,15 @@ package com.projet.projetIndu.repositories; import com.projet.projetIndu.entities.Invoice; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; -public interface InvoiceRepository extends JpaRepository<Invoice, Long> { +import java.time.LocalDate; +import java.util.List; +@Repository +public interface InvoiceRepository extends JpaRepository<Invoice, Long> { + List<Invoice> findInvoicesByIssueDateBetween(LocalDate startDate, LocalDate endDate); } + diff --git a/src/main/java/com/projet/projetIndu/services/AppointmentService.java b/src/main/java/com/projet/projetIndu/services/AppointmentService.java index 5f41a669005ad663eaa4bd5aa355023321094696..6a464d282d296434285763d161d5305a7a4c21fb 100644 --- a/src/main/java/com/projet/projetIndu/services/AppointmentService.java +++ b/src/main/java/com/projet/projetIndu/services/AppointmentService.java @@ -13,10 +13,12 @@ import java.util.Optional; @Service public class AppointmentService { private final AppointmentRepository appointmentRepository; + private final InvoiceService invoiceService; // Ajoutez cette ligne pour l'injection du service InvoiceService @Autowired - public AppointmentService(AppointmentRepository appointmentRepository) { + public AppointmentService(AppointmentRepository appointmentRepository, InvoiceService invoiceService) { this.appointmentRepository = appointmentRepository; + this.invoiceService = invoiceService; // Initialisez invoiceService dans le constructeur } public List<Appointment> getAllAppointments() { @@ -43,7 +45,7 @@ public class AppointmentService { Optional<Appointment> optionalAppointment = appointmentRepository.findById(id); if (optionalAppointment.isPresent()) { Appointment appointment = optionalAppointment.get(); - appointment.setStatus(AppointmentStatus.valueOf("CANCELLED")); + appointment.setStatus(AppointmentStatus.CANCELLED); appointmentRepository.save(appointment); } } @@ -62,8 +64,19 @@ public class AppointmentService { Appointment appointment = optionalAppointment.get(); appointment.setStatus(AppointmentStatus.CONFIRMED); appointmentRepository.save(appointment); + + // Création automatique d'une facture + double amount = calculateAmount(appointment); // Méthode à définir pour calculer le montant + invoiceService.createInvoice(appointment, amount); // Création de la facture } else { throw new RuntimeException("Rendez-vous introuvable."); } } + + private double calculateAmount(Appointment appointment) { + // Calculer le montant en fonction du type de médecin, de la durée, etc. + // Exemple avec un montant fixe + return 50.0; + } + } diff --git a/src/main/java/com/projet/projetIndu/services/DoctorService.java b/src/main/java/com/projet/projetIndu/services/DoctorService.java index 3b69c9ffbc848ddf524258f6acf3eb3a7f013c68..7f3486a799a7ceae2c5e58297e65c2f2d5a89284 100644 --- a/src/main/java/com/projet/projetIndu/services/DoctorService.java +++ b/src/main/java/com/projet/projetIndu/services/DoctorService.java @@ -45,13 +45,27 @@ public class DoctorService { doctorRepository.deleteById(id); } - public Long getAuthenticatedDoctorId() { + /*public Long getAuthenticatedDoctorId() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String email = authentication.getName(); return doctorRepository.findByEmail(email) .map(Doctor::getId) .orElseThrow(() -> new RuntimeException("Médecin non trouvé")); + }*/ + public Long getAuthenticatedDoctorId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + + Optional<Doctor> doctorOpt = doctorRepository.findByEmail(email); + + if (doctorOpt.isPresent()) { + return (Long) doctorOpt.get().getId(); // Cast en Long + } else { + throw new RuntimeException("Médecin non trouvé"); + } } + + } diff --git a/src/main/java/com/projet/projetIndu/services/InvoiceService.java b/src/main/java/com/projet/projetIndu/services/InvoiceService.java index 834201988ee3ff5e96062bbccf14126680d97b0c..ade97d9ecb5c95a5370ee17014eb1868351163f4 100644 --- a/src/main/java/com/projet/projetIndu/services/InvoiceService.java +++ b/src/main/java/com/projet/projetIndu/services/InvoiceService.java @@ -1,15 +1,19 @@ package com.projet.projetIndu.services; +import com.projet.projetIndu.entities.Appointment; import com.projet.projetIndu.entities.Invoice; +import com.projet.projetIndu.entities.PaymentStatus; import com.projet.projetIndu.repositories.InvoiceRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.time.LocalDate; import java.util.List; import java.util.Optional; @Service public class InvoiceService { + private final InvoiceRepository invoiceRepository; @Autowired @@ -25,11 +29,33 @@ public class InvoiceService { return invoiceRepository.findById(id); } - public void deleteInvoiceById(Long id) { - invoiceRepository.deleteById(id); + public Invoice createInvoice(Appointment appointment, Double amount) { + Invoice invoice = new Invoice(appointment, amount); + return invoiceRepository.save(invoice); } - public Invoice saveInvoice(Invoice invoice) { - return invoiceRepository.save(invoice); + public void markAsPaid(Long invoiceId) { + Optional<Invoice> optionalInvoice = invoiceRepository.findById(invoiceId); + if (optionalInvoice.isPresent()) { + Invoice invoice = optionalInvoice.get(); + invoice.setStatus(PaymentStatus.PAID); // Changer le statut de paiement + invoiceRepository.save(invoice); // Sauvegarder la facture mise à jour + } else { + throw new RuntimeException("Facture non trouvée"); + } + } + public List<Invoice> getInvoicesByPeriod(LocalDate startDate, LocalDate endDate) { + return invoiceRepository.findInvoicesByIssueDateBetween(startDate, endDate); // Implémenter la méthode dans le repository + } + + public double calculateMonthlyRevenue(LocalDate month) { + LocalDate startDate = month.withDayOfMonth(1); + LocalDate endDate = startDate.plusMonths(1).minusDays(1); + + List<Invoice> invoices = getInvoicesByPeriod(startDate, endDate); + return invoices.stream().mapToDouble(Invoice::getAmount).sum(); // Somme des montants des factures } + + + } diff --git a/src/main/java/com/projet/projetIndu/services/PatientService.java b/src/main/java/com/projet/projetIndu/services/PatientService.java index 0edbe55f592777951839ec41c3cfbabb83231fd0..2d99d54a044d5be24b0654da02ee99a9d97eccf9 100644 --- a/src/main/java/com/projet/projetIndu/services/PatientService.java +++ b/src/main/java/com/projet/projetIndu/services/PatientService.java @@ -6,6 +6,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Optional; @@ -21,6 +22,7 @@ public class PatientService { } public List<Patient> getAllPatients() { + return patientRepository.findAll(); } @@ -41,12 +43,34 @@ public class PatientService { } - public Long getAuthenticatedPatientId() { + /*public Long getAuthenticatedPatientId() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); String email = authentication.getName(); return patientRepository.findByEmail(email) .map(Patient::getId) .orElseThrow(() -> new RuntimeException("Patient non trouvé")); + }*/ + public Long getAuthenticatedPatientId() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String email = authentication.getName(); + + Optional<Patient> patientOpt = patientRepository.findByEmail(email); + + if (patientOpt.isPresent()) { + return (Long) patientOpt.get().getId(); // Cast explicite + } else { + throw new RuntimeException("Patient non trouvé"); + } + } + + + @Transactional + public Patient mettreAJourPatient(Long id, Patient patient) { + if (patientRepository.existsById(id)) { + patient.setId(id); + return patientRepository.save(patient); + } + return null; } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 95add08cd8509282a08ebcaa89f95ea670b00fed..8d19862feb3cb12006f57467c2cc15cbbc9f180d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,15 +1,18 @@ spring.application.name=projetIndu -spring.datasource.url=jdbc:mysql://localhost:3306/projet +spring.datasource.url=jdbc:mysql://localhost:3307/projet spring.datasource.username=root -spring.datasource.password=jessie +spring.datasource.password=mypassword spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect -spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.hibernate.ddl-auto=create spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true spring.thymeleaf.prefix=classpath:/templates/ spring.thymeleaf.suffix=.html spring.thymeleaf.cache=false spring.servlet.multipart.max-file-size=100MB spring.servlet.multipart.max-request-size=100MB -spring.mvc.hiddenmethod.filter.enabled=true +sprin.g.mvc.hiddenmethod.filter.enabled=true + spring.banner.location=banner.txt + diff --git a/src/main/resources/templates/doctor-invoices.html b/src/main/resources/templates/doctor-invoices.html new file mode 100644 index 0000000000000000000000000000000000000000..19132909d64130c7530102981f6a827d77ce828a --- /dev/null +++ b/src/main/resources/templates/doctor-invoices.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="fr" xmlns:th="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Factures</title> + <script src="https://cdn.tailwindcss.com"></script> +</head> +<body class="bg-gray-100 font-sans leading-normal tracking-normal"> + +<header class="bg-white shadow-md"> + <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> + <div class="flex justify-between items-center py-6"> + <h1 class="text-2xl font-semibold text-gray-900">Mes Factures</h1> + <a href="/doctor/dashboard" class="text-blue-500 hover:text-blue-700">Retour au tableau de bord</a> + </div> + </div> +</header> + +<main class="mt-12"> + <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> + <h2 class="text-3xl font-bold text-gray-800 text-center">Liste des factures</h2> + <table class="mt-8 w-full border-collapse bg-white shadow-md rounded-lg"> + <thead> + <tr class="bg-gray-200"> + <th class="p-4 border">ID</th> + <th class="p-4 border">Patient</th> + <th class="p-4 border">Montant</th> + <th class="p-4 border">Statut</th> + <th class="p-4 border">Actions</th> + </tr> + </thead> + <tbody> + <tr th:each="invoice : ${invoices}"> + <td class="p-4 border" th:text="${invoice.id}"></td> + <td class="p-4 border" th:text="${invoice.appointment.patient.firstName} + ' ' + ${invoice.appointment.patient.lastName}"></td> + <td class="p-4 border" th:text="${invoice.amount} + ' €'"></td> + <td class="p-4 border" th:text="${invoice.status}"></td> + <td class="p-4 border"> + <form th:action="@{/doctor/invoices/{id}/pay(id=${invoice.id})}" method="post"> + <button class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Marquer comme payé</button> + </form> + </td> + </tr> + </tbody> + </table> + </div> +</main> + +</body> +</html> diff --git a/src/main/resources/templates/invoices.html b/src/main/resources/templates/invoices.html new file mode 100644 index 0000000000000000000000000000000000000000..b4f53cb9a0628a082e2da27b0f7621e70310b57b --- /dev/null +++ b/src/main/resources/templates/invoices.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="fr" xmlns:th="http://www.w3.org/1999/xhtml"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Factures</title> + <script src="https://cdn.tailwindcss.com"></script> +</head> +<body class="bg-gray-100 font-sans leading-normal tracking-normal"> + +<header class="bg-white shadow-md"> + <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> + <div class="flex justify-between items-center py-6"> + <h1 class="text-2xl font-semibold text-gray-900">Factures</h1> + <a href="/admin/dashboard" class="text-blue-500 hover:text-blue-700">Retour au tableau de bord</a> + </div> + </div> +</header> + +<main class="mt-12"> + <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> + <h2 class="text-3xl font-bold text-gray-800 text-center">Liste des factures</h2> + <table class="mt-8 w-full border-collapse bg-white shadow-md rounded-lg"> + <thead> + <tr class="bg-gray-200"> + <th class="p-4 border">ID</th> + <th class="p-4 border">Patient</th> + <th class="p-4 border">Montant</th> + <th class="p-4 border">Statut</th> + <th class="p-4 border">Actions</th> + </tr> + </thead> + <tbody> + <tr th:each="invoice : ${invoices}"> + <td class="p-4 border" th:text="${invoice.id}"></td> + <td th:text="${(invoice.appointment != null and invoice.appointment.patient != null) ? invoice.appointment.patient.firstName : 'N/A'}"></td> + <td class="p-4 border" th:text="${invoice.amount} + ' €'"></td> + <td class="p-4 border" th:text="${invoice.status}"></td> + <td class="p-4 border"> + <form th:action="@{/admin/invoices/{id}/pay(id=${invoice.id})}" method="post"> + <button class="bg-green-500 text-white px-4 py-2 rounded hover:bg-green-700" type="submit">Marquer comme payé</button> + </form> + </td> + </tr> + </tbody> + </table> + </div> +</main> + +</body> +</html>