From 6177976a6f1518ca1a515ab5d6808501e8d44e59 Mon Sep 17 00:00:00 2001 From: Dennis Eckerskorn Date: Wed, 14 May 2025 00:09:04 +0200 Subject: [PATCH] Some adjustments and added finance module to API, started createinvoice in frontend --- .../config/SecurityConfig.java | 5 + .../finance_management/IVATypeController.java | 65 +++++++++ .../finance_management/InvoiceController.java | 105 +++++++++++++++ .../InvoiceLineController.java | 68 ++++++++++ .../finance_management/PaymentController.java | 65 +++++++++ .../ProductServiceController.java | 65 +++++++++ .../AdminController.java | 1 - .../finance_management_dtos/IVATypeDTO.java | 63 +++++++++ .../finance_management_dtos/InvoiceDTO.java | 119 ++++++++++++++++ .../InvoiceLineDTO.java | 112 +++++++++++++++ .../finance_management_dtos/PaymentDTO.java | 105 +++++++++++++++ .../ProductServiceDTO.java | 115 ++++++++++++++++ memberflow-data/.idea/vcs.xml | 1 + .../src/components/forms/InvoiceForm.jsx | 127 ++++++++++++++++++ .../src/components/forms/InvoiceLineItem.jsx | 49 +++++++ .../src/components/forms/LoginForm.jsx | 93 +++++++------ .../src/components/layout/ContentArea.jsx | 27 +++- .../src/components/layout/MainLayout.jsx | 11 +- .../src/components/layout/Sidebar.jsx | 104 +++++--------- .../src/components/layout/SidebarAdmin.jsx | 52 +++++++ .../src/components/layout/SidebarStudent.jsx | 26 ++++ .../src/components/layout/SidebarTeacher.jsx | 25 ++++ .../src/components/layout/Topbar.jsx | 10 +- .../src/pages/admin/AdminDashboard.jsx | 2 +- memberflow-frontend/src/utils/jwtHelper.js | 5 + 25 files changed, 1292 insertions(+), 128 deletions(-) create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java create mode 100644 memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java create mode 100644 memberflow-frontend/src/components/forms/InvoiceForm.jsx create mode 100644 memberflow-frontend/src/components/forms/InvoiceLineItem.jsx create mode 100644 memberflow-frontend/src/components/layout/SidebarAdmin.jsx create mode 100644 memberflow-frontend/src/components/layout/SidebarStudent.jsx create mode 100644 memberflow-frontend/src/components/layout/SidebarTeacher.jsx diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java b/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java index 299f92a..a7ce3ff 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/config/SecurityConfig.java @@ -60,6 +60,11 @@ public class SecurityConfig { .requestMatchers("/api/v1/assistances/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") .requestMatchers("/api/v1/training-sessions/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") .requestMatchers("/api/v1/training-groups/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") + .requestMatchers("/api/v1/invoices/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") + .requestMatchers("/api/v1/invoice-lines/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") + .requestMatchers("/api/v1/payments/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") + .requestMatchers("/api/v1/products-services/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") + .requestMatchers("/api/v1/iva-types/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS") .anyRequest().authenticated() ) .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java new file mode 100644 index 0000000..116b397 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/IVATypeController.java @@ -0,0 +1,65 @@ +package com.denniseckerskorn.controllers.finance_management; + +import com.denniseckerskorn.dtos.finance_management_dtos.IVATypeDTO; +import com.denniseckerskorn.entities.finance.IVAType; +import com.denniseckerskorn.exceptions.DuplicateEntityException; +import com.denniseckerskorn.exceptions.EntityNotFoundException; +import com.denniseckerskorn.exceptions.InvalidDataException; +import com.denniseckerskorn.services.finance_services.IVATypeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/iva-types") +@Tag(name = "IVA Types", description = "Operations related to IVA type management") +public class IVATypeController { + + private final IVATypeService ivaTypeService; + + public IVATypeController(IVATypeService ivaTypeService) { + this.ivaTypeService = ivaTypeService; + } + + @PostMapping("/create") + @Operation(summary = "Create a new IVA type") + public ResponseEntity create(@Valid @RequestBody IVATypeDTO dto) throws DuplicateEntityException { + IVAType saved = ivaTypeService.save(dto.toEntity()); + return new ResponseEntity<>(new IVATypeDTO(saved), HttpStatus.CREATED); + } + + @PutMapping("/update") + @Operation(summary = "Update an existing IVA type") + public ResponseEntity update(@Valid @RequestBody IVATypeDTO dto) throws EntityNotFoundException, InvalidDataException { + IVAType updated = ivaTypeService.update(dto.toEntity()); + return new ResponseEntity<>(new IVATypeDTO(updated), HttpStatus.OK); + } + + @GetMapping("/getById/{id}") + @Operation(summary = "Get IVA type by ID") + public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + IVAType entity = ivaTypeService.findById(id); + return new ResponseEntity<>(new IVATypeDTO(entity), HttpStatus.OK); + } + + @GetMapping("/getAll") + @Operation(summary = "Get all IVA types") + public ResponseEntity> getAll() { + List dtos = ivaTypeService.findAll() + .stream().map(IVATypeDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @DeleteMapping("/deleteById/{id}") + @Operation(summary = "Delete IVA type by ID (if not in use)") + public ResponseEntity delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + ivaTypeService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java new file mode 100644 index 0000000..ce42d84 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceController.java @@ -0,0 +1,105 @@ +package com.denniseckerskorn.controllers.finance_management; + + +import com.denniseckerskorn.dtos.finance_management_dtos.InvoiceDTO; +import com.denniseckerskorn.entities.finance.Invoice; +import com.denniseckerskorn.entities.finance.InvoiceLine; +import com.denniseckerskorn.exceptions.DuplicateEntityException; +import com.denniseckerskorn.exceptions.EntityNotFoundException; +import com.denniseckerskorn.exceptions.InvalidDataException; +import com.denniseckerskorn.services.finance_services.InvoiceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/invoices") +@Tag(name = "Invoices", description = "Operations related to invoice management") +public class InvoiceController { + + private final InvoiceService invoiceService; + + public InvoiceController(InvoiceService invoiceService) { + this.invoiceService = invoiceService; + } + + @PostMapping("/create") + @Operation(summary = "Create a new invoice") + public ResponseEntity createInvoice(@Valid @RequestBody InvoiceDTO dto) throws DuplicateEntityException { + Invoice saved = invoiceService.save(dto.toEntity()); + return new ResponseEntity<>(new InvoiceDTO(saved), HttpStatus.CREATED); + } + + @PutMapping("/update") + @Operation(summary = "Update an existing invoice") + public ResponseEntity updateInvoice(@Valid @RequestBody InvoiceDTO dto) throws EntityNotFoundException, InvalidDataException { + Invoice updated = invoiceService.update(dto.toEntity()); + return new ResponseEntity<>(new InvoiceDTO(updated), HttpStatus.OK); + } + + @GetMapping("/getById/{id}") + @Operation(summary = "Get invoice by ID") + public ResponseEntity getInvoiceById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + Invoice invoice = invoiceService.findById(id); + return new ResponseEntity<>(new InvoiceDTO(invoice), HttpStatus.OK); + } + + @GetMapping("/getAll") + @Operation(summary = "Get all invoices") + public ResponseEntity> getAllInvoices() { + List dtos = invoiceService.findAll() + .stream().map(InvoiceDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @DeleteMapping("/deleteById/{id}") + @Operation(summary = "Delete invoice by ID") + public ResponseEntity deleteInvoice(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + invoiceService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @GetMapping("/getAllInvoicesByUserId/{userId}") + @Operation(summary = "Get all invoices by user ID") + public ResponseEntity> getInvoicesByUserId(@PathVariable Integer userId) throws InvalidDataException { + List dtos = invoiceService.findAllInvoicesByUserId(userId) + .stream().map(InvoiceDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @PostMapping("/addLinesByInvoiceId/{invoiceId}") + @Operation(summary = "Add a line to an invoice by invoice ID") + public ResponseEntity addLineToInvoice(@PathVariable Integer invoiceId, @RequestBody @Valid InvoiceLine line) { + invoiceService.addLineToInvoiceById(invoiceId, line); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping("/removeLineFromInvoiceById/{invoiceId}") + @Operation(summary = "Remove a line from an invoice by IDs") + public ResponseEntity removeLineFromInvoice(@PathVariable Integer invoiceId, @PathVariable Integer lineId) { + invoiceService.removeLineFromInvoiceById(invoiceId, lineId); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } + + @PutMapping("/recalculateTotalOfInvoiceById/{invoiceId}") + @Operation(summary = "Recalculate the total of an invoice") + public ResponseEntity recalculateTotal(@PathVariable Integer invoiceId) throws EntityNotFoundException, InvalidDataException { + Invoice invoice = invoiceService.findById(invoiceId); + invoiceService.recalculateTotal(invoice); + return new ResponseEntity<>(HttpStatus.OK); + } + + @DeleteMapping("/clearAllLinesFromInvoiceById/{invoiceId}") + @Operation(summary = "Clear all invoice lines from an invoice") + public ResponseEntity clearInvoiceLines(@PathVariable Integer invoiceId) throws EntityNotFoundException, InvalidDataException { + Invoice invoice = invoiceService.findById(invoiceId); + invoiceService.clearInvoiceLines(invoice); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java new file mode 100644 index 0000000..19965a5 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/InvoiceLineController.java @@ -0,0 +1,68 @@ +package com.denniseckerskorn.controllers.finance_management; + +import com.denniseckerskorn.dtos.finance_management_dtos.InvoiceLineDTO; +import com.denniseckerskorn.entities.finance.InvoiceLine; +import com.denniseckerskorn.exceptions.DuplicateEntityException; +import com.denniseckerskorn.exceptions.EntityNotFoundException; +import com.denniseckerskorn.exceptions.InvalidDataException; +import com.denniseckerskorn.services.finance_services.InvoiceLineService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * InvoiceLineController handles operations related to invoice lines (items in invoices). + */ +@RestController +@RequestMapping("/api/v1/invoice-lines") +@Tag(name = "Invoice Lines", description = "Operations related to invoice lines") +public class InvoiceLineController { + + private final InvoiceLineService invoiceLineService; + + public InvoiceLineController(InvoiceLineService invoiceLineService) { + this.invoiceLineService = invoiceLineService; + } + + @PostMapping("/create") + @Operation(summary = "Create a new invoice line") + public ResponseEntity create(@Valid @RequestBody InvoiceLineDTO dto) throws DuplicateEntityException { + InvoiceLine saved = invoiceLineService.save(dto.toEntity()); + return new ResponseEntity<>(new InvoiceLineDTO(saved), HttpStatus.CREATED); + } + + @PutMapping("/update") + @Operation(summary = "Update an existing invoice line") + public ResponseEntity update(@Valid @RequestBody InvoiceLineDTO dto) throws InvalidDataException, EntityNotFoundException { + InvoiceLine updated = invoiceLineService.update(dto.toEntity()); + return new ResponseEntity<>(new InvoiceLineDTO(updated), HttpStatus.OK); + } + + @GetMapping("/getById/{id}") + @Operation(summary = "Get invoice line by ID") + public ResponseEntity getById(@PathVariable Integer id) throws InvalidDataException, EntityNotFoundException { + InvoiceLine line = invoiceLineService.findById(id); + return new ResponseEntity<>(new InvoiceLineDTO(line), HttpStatus.OK); + } + + @GetMapping("/getAll") + @Operation(summary = "Get all invoice lines") + public ResponseEntity> getAll() { + List dtos = invoiceLineService.findAll() + .stream().map(InvoiceLineDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @DeleteMapping("/deleteById/{id}") + @Operation(summary = "Delete invoice line by ID") + public ResponseEntity delete(@PathVariable Integer id) throws InvalidDataException, EntityNotFoundException { + invoiceLineService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java new file mode 100644 index 0000000..6a83586 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java @@ -0,0 +1,65 @@ +package com.denniseckerskorn.controllers.finance_management; + +import com.denniseckerskorn.dtos.finance_management_dtos.PaymentDTO; +import com.denniseckerskorn.entities.finance.Payment; +import com.denniseckerskorn.exceptions.DuplicateEntityException; +import com.denniseckerskorn.exceptions.EntityNotFoundException; +import com.denniseckerskorn.exceptions.InvalidDataException; +import com.denniseckerskorn.services.finance_services.PaymentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/payments") +@Tag(name = "Payments", description = "Operations related to payment management") +public class PaymentController { + + private final PaymentService paymentService; + + public PaymentController(PaymentService paymentService) { + this.paymentService = paymentService; + } + + @PostMapping("/create") + @Operation(summary = "Create a new payment and mark invoice as PAID") + public ResponseEntity create(@Valid @RequestBody PaymentDTO dto) throws DuplicateEntityException { + Payment saved = paymentService.save(dto.toEntity()); + return new ResponseEntity<>(new PaymentDTO(saved), HttpStatus.CREATED); + } + + @PutMapping("/update") + @Operation(summary = "Update an existing payment and adjust invoice status") + public ResponseEntity update(@Valid @RequestBody PaymentDTO dto) throws EntityNotFoundException, InvalidDataException { + Payment updated = paymentService.update(dto.toEntity()); + return new ResponseEntity<>(new PaymentDTO(updated), HttpStatus.OK); + } + + @GetMapping("/getById/{id}") + @Operation(summary = "Get payment by ID") + public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + Payment payment = paymentService.findById(id); + return new ResponseEntity<>(new PaymentDTO(payment), HttpStatus.OK); + } + + @GetMapping("/getAll") + @Operation(summary = "Get all payments") + public ResponseEntity> getAll() { + List dtos = paymentService.findAll() + .stream().map(PaymentDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @DeleteMapping("/deleteById/{id}") + @Operation(summary = "Remove a payment and set invoice status to NOT_PAID") + public ResponseEntity removePayment(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + paymentService.removePayment(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java new file mode 100644 index 0000000..da8873f --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/ProductServiceController.java @@ -0,0 +1,65 @@ +package com.denniseckerskorn.controllers.finance_management; + +import com.denniseckerskorn.dtos.finance_management_dtos.ProductServiceDTO; +import com.denniseckerskorn.entities.finance.ProductService; +import com.denniseckerskorn.exceptions.DuplicateEntityException; +import com.denniseckerskorn.exceptions.EntityNotFoundException; +import com.denniseckerskorn.exceptions.InvalidDataException; +import com.denniseckerskorn.services.finance_services.ProductServiceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/v1/products-services") +@Tag(name = "Product Services", description = "Operations related to product/service management") +public class ProductServiceController { + + private final ProductServiceService productServiceService; + + public ProductServiceController(ProductServiceService productServiceService) { + this.productServiceService = productServiceService; + } + + @PostMapping("/create") + @Operation(summary = "Create a new product/service") + public ResponseEntity create(@Valid @RequestBody ProductServiceDTO dto) throws DuplicateEntityException { + ProductService saved = productServiceService.save(dto.toEntity()); + return new ResponseEntity<>(new ProductServiceDTO(saved), HttpStatus.CREATED); + } + + @PutMapping("/update") + @Operation(summary = "Update an existing product/service") + public ResponseEntity update(@Valid @RequestBody ProductServiceDTO dto) throws EntityNotFoundException, InvalidDataException { + ProductService updated = productServiceService.update(dto.toEntity()); + return new ResponseEntity<>(new ProductServiceDTO(updated), HttpStatus.OK); + } + + @GetMapping("/getById/{id}") + @Operation(summary = "Get product/service by ID") + public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + ProductService entity = productServiceService.findById(id); + return new ResponseEntity<>(new ProductServiceDTO(entity), HttpStatus.OK); + } + + @GetMapping("/getAll") + @Operation(summary = "Get all products/services") + public ResponseEntity> getAll() { + List dtos = productServiceService.findAll() + .stream().map(ProductServiceDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + + @DeleteMapping("/deleteById/{id}") + @Operation(summary = "Delete product/service by ID") + public ResponseEntity delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { + productServiceService.deleteById(id); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java index 85bd37e..509ef80 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/user_managment_controllers/AdminController.java @@ -18,7 +18,6 @@ import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; -//TODO: test @RestController @RequestMapping("/api/v1/admins") @Tag(name = "Administrator Management", description = "Operations related to administrator management") diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java new file mode 100644 index 0000000..643ecdb --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/IVATypeDTO.java @@ -0,0 +1,63 @@ +package com.denniseckerskorn.dtos.finance_management_dtos; + +import com.denniseckerskorn.entities.finance.IVAType; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; + +public class IVATypeDTO { + + private Integer id; + + @NotNull + private BigDecimal percentage; + + private String description; + + public IVATypeDTO() {} + + public IVATypeDTO(IVAType entity) { + this.id = entity.getId(); + this.percentage = entity.getPercentage(); + this.description = entity.getDescription(); + } + + public static IVATypeDTO fromEntity(IVAType entity) { + return new IVATypeDTO(entity); + } + + public IVAType toEntity() { + IVAType iva = new IVAType(); + iva.setId(this.id); + iva.setPercentage(this.percentage); + iva.setDescription(this.description); + return iva; + } + + // Getters y setters... + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public BigDecimal getPercentage() { + return percentage; + } + + public void setPercentage(BigDecimal percentage) { + this.percentage = percentage; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java new file mode 100644 index 0000000..0edf1bb --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceDTO.java @@ -0,0 +1,119 @@ +package com.denniseckerskorn.dtos.finance_management_dtos; + +import com.denniseckerskorn.entities.finance.Invoice; +import com.denniseckerskorn.enums.StatusValues; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Set; +import java.util.stream.Collectors; + +public class InvoiceDTO { + + private Integer id; + + @NotNull + private Integer userId; + + @NotNull + private LocalDateTime date; + + @NotNull + private BigDecimal total; + + @NotNull + private StatusValues status; + + private Integer paymentId; + + private Set invoiceLineIds; + + public InvoiceDTO() { + } + + public InvoiceDTO(Invoice invoice) { + this.id = invoice.getId(); + this.userId = invoice.getUser() != null ? invoice.getUser().getId() : null; + this.date = invoice.getDate(); + this.total = invoice.getTotal(); + this.status = invoice.getStatus(); + this.paymentId = invoice.getPayment() != null ? invoice.getPayment().getId() : null; + this.invoiceLineIds = invoice.getInvoiceLines() + .stream() + .map(line -> line.getId()) + .collect(Collectors.toSet()); + } + + public static InvoiceDTO fromEntity(Invoice invoice) { + return new InvoiceDTO(invoice); + } + + public Invoice toEntity() { + Invoice invoice = new Invoice(); + invoice.setId(this.id); + invoice.setDate(this.date); + invoice.setTotal(this.total); + invoice.setStatus(this.status); + return invoice; + } + + // Getters y setters (puedes generarlos automáticamente) + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getUserId() { + return userId; + } + + public void setUserId(Integer userId) { + this.userId = userId; + } + + public LocalDateTime getDate() { + return date; + } + + public void setDate(LocalDateTime date) { + this.date = date; + } + + public BigDecimal getTotal() { + return total; + } + + public void setTotal(BigDecimal total) { + this.total = total; + } + + public StatusValues getStatus() { + return status; + } + + public void setStatus(StatusValues status) { + this.status = status; + } + + public Integer getPaymentId() { + return paymentId; + } + + public void setPaymentId(Integer paymentId) { + this.paymentId = paymentId; + } + + public Set getInvoiceLineIds() { + return invoiceLineIds; + } + + public void setInvoiceLineIds(Set invoiceLineIds) { + this.invoiceLineIds = invoiceLineIds; + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java new file mode 100644 index 0000000..551be37 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/InvoiceLineDTO.java @@ -0,0 +1,112 @@ +package com.denniseckerskorn.dtos.finance_management_dtos; + +import com.denniseckerskorn.entities.finance.InvoiceLine; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; + +public class InvoiceLineDTO { + + private Integer id; + + @NotNull + private Integer invoiceId; + + @NotNull + private Integer productServiceId; + + private String description; + + @NotNull + private Integer quantity; + + @NotNull + private BigDecimal unitPrice; + + private BigDecimal subtotal; + + public InvoiceLineDTO() {} + + public InvoiceLineDTO(InvoiceLine entity) { + this.id = entity.getId(); + this.invoiceId = entity.getInvoice() != null ? entity.getInvoice().getId() : null; + this.productServiceId = entity.getProductService() != null ? entity.getProductService().getId() : null; + this.description = entity.getDescription(); + this.quantity = entity.getQuantity(); + this.unitPrice = entity.getUnitPrice(); + this.subtotal = entity.getSubtotal(); + } + + public static InvoiceLineDTO fromEntity(InvoiceLine entity) { + return new InvoiceLineDTO(entity); + } + + public InvoiceLine toEntity() { + InvoiceLine entity = new InvoiceLine(); + entity.setId(this.id); + entity.setDescription(this.description); + entity.setQuantity(this.quantity); + entity.setUnitPrice(this.unitPrice); + entity.setSubtotal(this.subtotal); + return entity; + } + + // Getters y setters... + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getInvoiceId() { + return invoiceId; + } + + public void setInvoiceId(Integer invoiceId) { + this.invoiceId = invoiceId; + } + + public Integer getProductServiceId() { + return productServiceId; + } + + public void setProductServiceId(Integer productServiceId) { + this.productServiceId = productServiceId; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Integer getQuantity() { + return quantity; + } + + public void setQuantity(Integer quantity) { + this.quantity = quantity; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } + + public BigDecimal getSubtotal() { + return subtotal; + } + + public void setSubtotal(BigDecimal subtotal) { + this.subtotal = subtotal; + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java new file mode 100644 index 0000000..f53606a --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java @@ -0,0 +1,105 @@ +package com.denniseckerskorn.dtos.finance_management_dtos; + +import com.denniseckerskorn.entities.finance.Payment; +import com.denniseckerskorn.enums.PaymentMethodValues; +import com.denniseckerskorn.enums.StatusValues; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public class PaymentDTO { + + private Integer id; + + @NotNull + private Integer invoiceId; + + @NotNull + private LocalDateTime paymentDate; + + @NotNull + private BigDecimal amount; + + @NotNull + private PaymentMethodValues paymentMethod; + + @NotNull + private StatusValues status; + + public PaymentDTO() {} + + public PaymentDTO(Payment entity) { + this.id = entity.getId(); + this.invoiceId = entity.getInvoice() != null ? entity.getInvoice().getId() : null; + this.paymentDate = entity.getPaymentDate(); + this.amount = entity.getAmount(); + this.paymentMethod = entity.getPaymentMethod(); + this.status = entity.getStatus(); + } + + public static PaymentDTO fromEntity(Payment entity) { + return new PaymentDTO(entity); + } + + public Payment toEntity() { + Payment payment = new Payment(); + payment.setId(this.id); + payment.setPaymentDate(this.paymentDate); + payment.setAmount(this.amount); + payment.setPaymentMethod(this.paymentMethod); + payment.setStatus(this.status); + return payment; + } + + // Getters y setters... + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getInvoiceId() { + return invoiceId; + } + + public void setInvoiceId(Integer invoiceId) { + this.invoiceId = invoiceId; + } + + public LocalDateTime getPaymentDate() { + return paymentDate; + } + + public void setPaymentDate(LocalDateTime paymentDate) { + this.paymentDate = paymentDate; + } + + public BigDecimal getAmount() { + return amount; + } + + public void setAmount(BigDecimal amount) { + this.amount = amount; + } + + public PaymentMethodValues getPaymentMethod() { + return paymentMethod; + } + + public void setPaymentMethod(PaymentMethodValues paymentMethod) { + this.paymentMethod = paymentMethod; + } + + public StatusValues getStatus() { + return status; + } + + public void setStatus(StatusValues status) { + this.status = status; + } +} diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java new file mode 100644 index 0000000..ad10182 --- /dev/null +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/ProductServiceDTO.java @@ -0,0 +1,115 @@ +package com.denniseckerskorn.dtos.finance_management_dtos; + +import com.denniseckerskorn.entities.finance.ProductService; +import com.denniseckerskorn.enums.StatusValues; +import jakarta.validation.constraints.NotNull; + +import java.math.BigDecimal; + +public class ProductServiceDTO { + + private Integer id; + + @NotNull + private Integer ivaTypeId; + + @NotNull + private String name; + + private String description; + + @NotNull + private BigDecimal price; + + @NotNull + private String type; + + @NotNull + private StatusValues status; + + public ProductServiceDTO() {} + + public ProductServiceDTO(ProductService entity) { + this.id = entity.getId(); + this.ivaTypeId = entity.getIvaType() != null ? entity.getIvaType().getId() : null; + this.name = entity.getName(); + this.description = entity.getDescription(); + this.price = entity.getPrice(); + this.type = entity.getType(); + this.status = entity.getStatus(); + } + + public static ProductServiceDTO fromEntity(ProductService entity) { + return new ProductServiceDTO(entity); + } + + public ProductService toEntity() { + ProductService ps = new ProductService(); + ps.setId(this.id); + ps.setName(this.name); + ps.setDescription(this.description); + ps.setPrice(this.price); + ps.setType(this.type); + ps.setStatus(this.status); + return ps; + } + + // Getters y setters... + + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getIvaTypeId() { + return ivaTypeId; + } + + public void setIvaTypeId(Integer ivaTypeId) { + this.ivaTypeId = ivaTypeId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public BigDecimal getPrice() { + return price; + } + + public void setPrice(BigDecimal price) { + this.price = price; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public StatusValues getStatus() { + return status; + } + + public void setStatus(StatusValues status) { + this.status = status; + } +} diff --git a/memberflow-data/.idea/vcs.xml b/memberflow-data/.idea/vcs.xml index 35eb1dd..62bd7a0 100644 --- a/memberflow-data/.idea/vcs.xml +++ b/memberflow-data/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/memberflow-frontend/src/components/forms/InvoiceForm.jsx b/memberflow-frontend/src/components/forms/InvoiceForm.jsx new file mode 100644 index 0000000..67f59e7 --- /dev/null +++ b/memberflow-frontend/src/components/forms/InvoiceForm.jsx @@ -0,0 +1,127 @@ +import React, { useEffect, useState } from "react"; +import InvoiceLineItem from "./InvoiceLineItem"; +import api from "../../api/axiosConfig"; + +const InvoiceForm = () => { + const [students, setStudents] = useState([]); + const [userId, setUserId] = useState(""); + const [date, setDate] = useState(new Date().toISOString().slice(0, 16)); + const [lines, setLines] = useState([]); + const [products, setProducts] = useState([]); + + useEffect(() => { + api.get("/students/getAll").then((res) => setStudents(res.data)); + + api.get("/products-services/getAll").then((res) => setProducts(res.data)); + }, []); + + const addLine = () => { + setLines([...lines, { productServiceId: "", quantity: 1 }]); + }; + + const updateLine = (index, updatedLine) => { + const newLines = [...lines]; + newLines[index] = updatedLine; + setLines(newLines); + }; + + const removeLine = (index) => { + setLines(lines.filter((_, i) => i !== index)); + }; + + const createInvoice = async () => { + if (!userId || lines.length === 0) { + alert("Selecciona un estudiante y al menos un producto."); + return; + } + + const invoiceRes = await api.post("/invoices/create", { + user: { id: parseInt(userId) }, + date, + total: 0, + status: "NOT_PAID", + invoiceLineIds: [], + }); + + const invoice = invoiceRes.data; + + for (const line of lines) { + const product = products.find( + (p) => p.id === parseInt(line.productServiceId) + ); + const subtotal = product.price * line.quantity; + + await api.post(`/invoices/addLinesByInvoiceId/${invoice.id}`, { + productServiceId: parseInt(line.productServiceId), + quantity: parseInt(line.quantity), + unitPrice: product.price, + subtotal, + description: product.description, + }); + } + + await api.put(`/invoices/recalculateTotalOfInvoiceById/${invoice.id}`); + + alert("Factura creada correctamente."); + setUserId(""); + setLines([]); + }; + + return ( +
+

Crear Factura

+ + + + + + setDate(e.target.value)} + /> + +

Productos / Servicios

+ + {lines.map((line, i) => ( + + ))} + +
+ + +
+
+ ); +}; + +export default InvoiceForm; diff --git a/memberflow-frontend/src/components/forms/InvoiceLineItem.jsx b/memberflow-frontend/src/components/forms/InvoiceLineItem.jsx new file mode 100644 index 0000000..c207f1c --- /dev/null +++ b/memberflow-frontend/src/components/forms/InvoiceLineItem.jsx @@ -0,0 +1,49 @@ +// src/components/forms/InvoiceLineItem.jsx + +import React from 'react'; + +const InvoiceLineItem = ({ index, line, products, onUpdate, onRemove }) => { + const handleChange = (e) => { + onUpdate(index, { ...line, [e.target.name]: e.target.value }); + }; + + const selectedProduct = products.find(p => p.id === parseInt(line.productServiceId)); + + return ( +
+ + + + + {selectedProduct && ( +
+ {selectedProduct.name}: {selectedProduct.description} +
+ )} + + +
+ ); +}; + +export default InvoiceLineItem; diff --git a/memberflow-frontend/src/components/forms/LoginForm.jsx b/memberflow-frontend/src/components/forms/LoginForm.jsx index 6d8b277..9650e77 100644 --- a/memberflow-frontend/src/components/forms/LoginForm.jsx +++ b/memberflow-frontend/src/components/forms/LoginForm.jsx @@ -1,51 +1,58 @@ import React, { useState } from "react"; import api from "../../api/axiosConfig"; -import "../styles/Login.css" +import "../styles/Login.css"; const LoginForm = ({ onLoginSuccess }) => { - const [email, setEmail] = useState(''); - const [password, setPassword] = useState(''); - const [error, setError] = useState(''); + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [error, setError] = useState(''); - const handleLogin = async (e) => { - e.preventDefault(); - setError(''); - try { - const res = await api.post('/auth/login', { email, password }); - localStorage.setItem('token', res.data.token); - console.log("Token guardado:", res.data.token); - onLoginSuccess(); - } catch (err) { - console.error("Error al iniciar sesión:", err); - setError('Email o contraseña incorrectos'); - } - }; + const handleLogin = async (e) => { + e.preventDefault(); + setError(''); + try { + const res = await api.post('/auth/login', { email, password }); + const token = res.data.token; + localStorage.setItem('token', token); + console.log("Token guardado:", token); - return ( -
-
-

MemberFlow

-
- setEmail(e.target.value)} - required - /> - setPassword(e.target.value)} - required - /> - - {error &&

{error}

} -
-
-
- ); + const profileRes = await api.get('/users/me'); + const roleName = profileRes.data.roleName; + console.log("Rol del usuario:", roleName); + localStorage.setItem('roleName', roleName); + + onLoginSuccess(); + } catch (err) { + console.error("Error al iniciar sesión:", err); + setError('Email o contraseña incorrectos'); + } + }; + + return ( +
+
+

MemberFlow

+
+ setEmail(e.target.value)} + required + /> + setPassword(e.target.value)} + required + /> + + {error &&

{error}

} +
+
+
+ ); }; -export default LoginForm; \ No newline at end of file +export default LoginForm; diff --git a/memberflow-frontend/src/components/layout/ContentArea.jsx b/memberflow-frontend/src/components/layout/ContentArea.jsx index 8939403..96a63d8 100644 --- a/memberflow-frontend/src/components/layout/ContentArea.jsx +++ b/memberflow-frontend/src/components/layout/ContentArea.jsx @@ -4,9 +4,11 @@ import '../styles/ContentArea.css'; import UserForm from '../forms/UserForm'; import StudentForm from '../forms/StudentForm'; import TeacherForm from '../forms/TeacherForm'; +import { getRoleFromToken } from '../../utils/jwtHelper'; const ContentArea = ({ section, entity, action }) => { - // Si falta info, mostramos encabezado básico + const role = getRoleFromToken(); + if (!section || !entity || !action) { return (
@@ -16,7 +18,26 @@ const ContentArea = ({ section, entity, action }) => { ); } - // Sección de User Management + // Definir qué roles tienen acceso a qué entidades + const permissions = { + FULL_ACCESS: ['Users', 'Students', 'Teachers', 'Admin', 'Notifications', 'Assistance', 'TrainingGroups', 'TrainingSessions', 'Memberships', 'StudentHistories'], + MANAGE_STUDENTS: ['Students', 'Assistance', 'TrainingGroups', 'TrainingSessions'], + VIEW_OWN_DATA: ['Assistance', 'Timetable', 'History'], + }; + + // Validar si tiene acceso a la entidad actual + const hasPermission = permissions[role]?.includes(entity); + + if (!hasPermission) { + return ( +
+

Acceso denegado

+

No tienes permiso para acceder a esta sección.

+
+ ); + } + + // Sección de gestión de usuarios if (section === 'User Management') { if (action === 'create') { switch (entity) { @@ -42,7 +63,6 @@ const ContentArea = ({ section, entity, action }) => { ); } - // Puedes continuar con update, delete, findById, etc. return (

{action} {entity}

@@ -51,7 +71,6 @@ const ContentArea = ({ section, entity, action }) => { ); } - // Si no se reconoce la sección return (

{section}

diff --git a/memberflow-frontend/src/components/layout/MainLayout.jsx b/memberflow-frontend/src/components/layout/MainLayout.jsx index c2df829..04aaf23 100644 --- a/memberflow-frontend/src/components/layout/MainLayout.jsx +++ b/memberflow-frontend/src/components/layout/MainLayout.jsx @@ -22,6 +22,7 @@ import TrainingSessionList from '../lists/TrainingSessionList'; import ViewTimetable from '../forms/ViewTimetable'; import MembershipForm from '../forms/MembershipCreateForm'; import MembershipList from '../lists/MembershipList'; +import InvoiceForm from '../forms/InvoiceForm'; import '../styles/MainLayout.css'; @@ -52,6 +53,7 @@ const MainLayout = () => { {/* ContentArea general para secciones sin componentes específicos */} } /> } /> + } /> {/*Class Management - rutas específicas*/} } /> @@ -65,13 +67,8 @@ const MainLayout = () => { } /> - - - - - - - } /> + + } /> {/* Profile Page*/} } /> diff --git a/memberflow-frontend/src/components/layout/Sidebar.jsx b/memberflow-frontend/src/components/layout/Sidebar.jsx index 661a6e5..64a2d21 100644 --- a/memberflow-frontend/src/components/layout/Sidebar.jsx +++ b/memberflow-frontend/src/components/layout/Sidebar.jsx @@ -1,77 +1,45 @@ -import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { getRoleFromToken } from '../../utils/jwtHelper'; -import '../styles/Sidebar.css'; +import React, { useState, useEffect } from 'react'; +import SidebarAdmin from './SidebarAdmin'; +import SidebarTeacher from './SidebarTeacher'; +import SidebarStudent from './SidebarStudent'; const Sidebar = () => { - const navigate = useNavigate(); - const role = getRoleFromToken(); - - return ( -
-

Menú

-
- {/* Botón común para todos: Mi Perfil */} - + const [role, setRole] = useState(null); - {role === 'FULL_ACCESS' && ( - <> - + useEffect(() => { + const interval = setInterval(() => { + const storedRole = localStorage.getItem('roleName'); + if (storedRole) { + setRole(storedRole); + clearInterval(interval); + } + }, 100); // intenta cada 100ms - -
-

👥 Administración de Usuarios

- - - - - - -
- -
-

📚 Administración de Clases

- - - - - - - - - -
+ return () => clearInterval(interval); + }, []); -
-

💵 Administración de Pagos y Facturas

- - - - - - -
- - - )} - - {role === 'MANAGE_STUDENTS' && ( - <> - - - - - )} - - {role === 'VIEW_OWN_DATA' && ( - <> - - - - )} + if (!role) { + return ( +
+

Cargando menú...

-
- ); + ); + } + + switch (role) { + case 'ROLE_ADMIN': + return ; + case 'ROLE_TEACHER': + return ; + case 'ROLE_STUDENT': + return ; + default: + return ( +
+

Sin acceso

+
+ ); + } }; export default Sidebar; diff --git a/memberflow-frontend/src/components/layout/SidebarAdmin.jsx b/memberflow-frontend/src/components/layout/SidebarAdmin.jsx new file mode 100644 index 0000000..d368009 --- /dev/null +++ b/memberflow-frontend/src/components/layout/SidebarAdmin.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import '../styles/Sidebar.css'; + +const SidebarAdmin = () => { + const navigate = useNavigate(); + + return ( +
+

Menú

+
+ + + +
+

👥 Administración de Usuarios

+ + + + + + +
+ +
+

📚 Administración de Clases

+ + + + + + + + + +
+ +
+

💵 Finanzas

+ + + + + + +
+
+
+ ); +}; + +export default SidebarAdmin; diff --git a/memberflow-frontend/src/components/layout/SidebarStudent.jsx b/memberflow-frontend/src/components/layout/SidebarStudent.jsx new file mode 100644 index 0000000..16449dd --- /dev/null +++ b/memberflow-frontend/src/components/layout/SidebarStudent.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import '../styles/Sidebar.css'; + +const SidebarStudent = () => { + const navigate = useNavigate(); + + return ( +
+

Menú

+
+ + + +
+

🎓 Opciones de Estudiante

+ + + +
+
+
+ ); +}; + +export default SidebarStudent; diff --git a/memberflow-frontend/src/components/layout/SidebarTeacher.jsx b/memberflow-frontend/src/components/layout/SidebarTeacher.jsx new file mode 100644 index 0000000..f24125f --- /dev/null +++ b/memberflow-frontend/src/components/layout/SidebarTeacher.jsx @@ -0,0 +1,25 @@ +import React from 'react'; +import { useNavigate } from 'react-router-dom'; +import '../styles/Sidebar.css'; + +const SidebarTeacher = () => { + const navigate = useNavigate(); + + return ( +
+

Menú

+
+ + + +
+

👨‍🏫 Gestión del Profesor

+ + +
+
+
+ ); +}; + +export default SidebarTeacher; diff --git a/memberflow-frontend/src/components/layout/Topbar.jsx b/memberflow-frontend/src/components/layout/Topbar.jsx index 0f2af6a..ecc29b4 100644 --- a/memberflow-frontend/src/components/layout/Topbar.jsx +++ b/memberflow-frontend/src/components/layout/Topbar.jsx @@ -7,10 +7,12 @@ const Topbar = () => { const navigate = useNavigate(); const role = getRoleFromToken(); - const handleLogout = () => { - localStorage.removeItem('token'); - navigate('/'); - }; + const handleLogout = () => { + localStorage.removeItem('token'); + localStorage.removeItem('roleName'); + navigate('/'); +}; + const roleLabel = { FULL_ACCESS: 'Admin', diff --git a/memberflow-frontend/src/pages/admin/AdminDashboard.jsx b/memberflow-frontend/src/pages/admin/AdminDashboard.jsx index eeae749..f7568f8 100644 --- a/memberflow-frontend/src/pages/admin/AdminDashboard.jsx +++ b/memberflow-frontend/src/pages/admin/AdminDashboard.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { useNavigate } from 'react-router-dom'; -import '../../components/styles/DashboardCards.css'; // lo creamos ahora +import '../../components/styles/DashboardCards.css'; const AdminDashboard = () => { const navigate = useNavigate(); diff --git a/memberflow-frontend/src/utils/jwtHelper.js b/memberflow-frontend/src/utils/jwtHelper.js index 273d990..7fbe52f 100644 --- a/memberflow-frontend/src/utils/jwtHelper.js +++ b/memberflow-frontend/src/utils/jwtHelper.js @@ -13,3 +13,8 @@ export const getRoleFromToken = () => { return null; } }; + +export const getRoleFromStorage = () => { + return localStorage.getItem('roleName'); +}; +