Some adjustments and added finance module to API, started createinvoice in frontend
This commit is contained in:
parent
7c1adbe2b7
commit
6177976a6f
|
|
@ -60,6 +60,11 @@ public class SecurityConfig {
|
||||||
.requestMatchers("/api/v1/assistances/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS")
|
.requestMatchers("/api/v1/assistances/**").hasAnyAuthority("FULL_ACCESS", "MANAGE_STUDENTS")
|
||||||
.requestMatchers("/api/v1/training-sessions/**").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/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()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
|
|
|
||||||
|
|
@ -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<IVATypeDTO> 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<IVATypeDTO> 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<IVATypeDTO> 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<List<IVATypeDTO>> getAll() {
|
||||||
|
List<IVATypeDTO> 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<Void> delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException {
|
||||||
|
ivaTypeService.deleteById(id);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<InvoiceDTO> 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<InvoiceDTO> 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<InvoiceDTO> 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<List<InvoiceDTO>> getAllInvoices() {
|
||||||
|
List<InvoiceDTO> 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<Void> 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<List<InvoiceDTO>> getInvoicesByUserId(@PathVariable Integer userId) throws InvalidDataException {
|
||||||
|
List<InvoiceDTO> 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<Void> 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<Void> 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<Void> 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<Void> clearInvoiceLines(@PathVariable Integer invoiceId) throws EntityNotFoundException, InvalidDataException {
|
||||||
|
Invoice invoice = invoiceService.findById(invoiceId);
|
||||||
|
invoiceService.clearInvoiceLines(invoice);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<InvoiceLineDTO> 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<InvoiceLineDTO> 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<InvoiceLineDTO> 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<List<InvoiceLineDTO>> getAll() {
|
||||||
|
List<InvoiceLineDTO> 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<Void> delete(@PathVariable Integer id) throws InvalidDataException, EntityNotFoundException {
|
||||||
|
invoiceLineService.deleteById(id);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<PaymentDTO> 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<PaymentDTO> 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<PaymentDTO> 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<List<PaymentDTO>> getAll() {
|
||||||
|
List<PaymentDTO> 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<Void> removePayment(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException {
|
||||||
|
paymentService.removePayment(id);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<ProductServiceDTO> 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<ProductServiceDTO> 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<ProductServiceDTO> 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<List<ProductServiceDTO>> getAll() {
|
||||||
|
List<ProductServiceDTO> 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<Void> delete(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException {
|
||||||
|
productServiceService.deleteById(id);
|
||||||
|
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
//TODO: test
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/admins")
|
@RequestMapping("/api/v1/admins")
|
||||||
@Tag(name = "Administrator Management", description = "Operations related to administrator management")
|
@Tag(name = "Administrator Management", description = "Operations related to administrator management")
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Integer> 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<Integer> getInvoiceLineIds() {
|
||||||
|
return invoiceLineIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInvoiceLineIds(Set<Integer> invoiceLineIds) {
|
||||||
|
this.invoiceLineIds = invoiceLineIds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,5 +2,6 @@
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="" vcs="Git" />
|
<mapping directory="" vcs="Git" />
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
|
|
@ -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 (
|
||||||
|
<div className="content-area">
|
||||||
|
<h2>Crear Factura</h2>
|
||||||
|
|
||||||
|
<label>Seleccionar estudiante:</label>
|
||||||
|
<select
|
||||||
|
className="form-select"
|
||||||
|
value={userId}
|
||||||
|
onChange={(e) => setUserId(e.target.value)}
|
||||||
|
>
|
||||||
|
<option value="">-- Selecciona un estudiante --</option>
|
||||||
|
{students.map((s) =>
|
||||||
|
s.user ? (
|
||||||
|
<option key={s.id} value={s.user.id}>
|
||||||
|
{s.user.name} {s.user.lastName} ({s.user.email})
|
||||||
|
</option>
|
||||||
|
) : null
|
||||||
|
)}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<label>Fecha de emisión:</label>
|
||||||
|
<input
|
||||||
|
className="form-input"
|
||||||
|
type="datetime-local"
|
||||||
|
value={date}
|
||||||
|
onChange={(e) => setDate(e.target.value)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<h3>Productos / Servicios</h3>
|
||||||
|
|
||||||
|
{lines.map((line, i) => (
|
||||||
|
<InvoiceLineItem
|
||||||
|
key={i}
|
||||||
|
index={i}
|
||||||
|
line={line}
|
||||||
|
products={products}
|
||||||
|
onUpdate={updateLine}
|
||||||
|
onRemove={removeLine}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div style={{ marginTop: "1rem" }}>
|
||||||
|
<button className="btn btn-secondary" onClick={addLine}>
|
||||||
|
+ Añadir Producto
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={createInvoice}
|
||||||
|
style={{ marginLeft: "1rem" }}
|
||||||
|
>
|
||||||
|
✅ Crear Factura
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvoiceForm;
|
||||||
|
|
@ -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 (
|
||||||
|
<div className="content-area" style={{ marginBottom: "1rem", display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
||||||
|
<select
|
||||||
|
name="productServiceId"
|
||||||
|
value={line.productServiceId}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="form-select"
|
||||||
|
>
|
||||||
|
<option value="">Selecciona producto</option>
|
||||||
|
{products.map(p => (
|
||||||
|
<option key={p.id} value={p.id}>
|
||||||
|
{p.name} - {p.price}€ ({p.type})
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
name="quantity"
|
||||||
|
min="1"
|
||||||
|
value={line.quantity}
|
||||||
|
onChange={handleChange}
|
||||||
|
className="form-input"
|
||||||
|
placeholder="Cantidad"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{selectedProduct && (
|
||||||
|
<div style={{ fontSize: '0.9rem', color: '#666' }}>
|
||||||
|
<strong>{selectedProduct.name}</strong>: {selectedProduct.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button className="btn btn-danger" onClick={() => onRemove(index)}>Eliminar producto</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default InvoiceLineItem;
|
||||||
|
|
@ -1,51 +1,58 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import api from "../../api/axiosConfig";
|
import api from "../../api/axiosConfig";
|
||||||
import "../styles/Login.css"
|
import "../styles/Login.css";
|
||||||
|
|
||||||
const LoginForm = ({ onLoginSuccess }) => {
|
const LoginForm = ({ onLoginSuccess }) => {
|
||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
|
||||||
const handleLogin = async (e) => {
|
const handleLogin = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setError('');
|
setError('');
|
||||||
try {
|
try {
|
||||||
const res = await api.post('/auth/login', { email, password });
|
const res = await api.post('/auth/login', { email, password });
|
||||||
localStorage.setItem('token', res.data.token);
|
const token = res.data.token;
|
||||||
console.log("Token guardado:", res.data.token);
|
localStorage.setItem('token', token);
|
||||||
onLoginSuccess();
|
console.log("Token guardado:", token);
|
||||||
} catch (err) {
|
|
||||||
console.error("Error al iniciar sesión:", err);
|
|
||||||
setError('Email o contraseña incorrectos');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
const profileRes = await api.get('/users/me');
|
||||||
<div className="login-wrapper">
|
const roleName = profileRes.data.roleName;
|
||||||
<div className="login-card">
|
console.log("Rol del usuario:", roleName);
|
||||||
<h1>MemberFlow</h1>
|
localStorage.setItem('roleName', roleName);
|
||||||
<form onSubmit={handleLogin}>
|
|
||||||
<input
|
onLoginSuccess();
|
||||||
type="email"
|
} catch (err) {
|
||||||
placeholder="Email"
|
console.error("Error al iniciar sesión:", err);
|
||||||
value={email}
|
setError('Email o contraseña incorrectos');
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
}
|
||||||
required
|
};
|
||||||
/>
|
|
||||||
<input
|
return (
|
||||||
type="password"
|
<div className="login-wrapper">
|
||||||
placeholder="Password"
|
<div className="login-card">
|
||||||
value={password}
|
<h1>MemberFlow</h1>
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
<form onSubmit={handleLogin}>
|
||||||
required
|
<input
|
||||||
/>
|
type="email"
|
||||||
<button type="submit">Entrar</button>
|
placeholder="Email"
|
||||||
{error && <p className="error">{error}</p>}
|
value={email}
|
||||||
</form>
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
</div>
|
required
|
||||||
</div>
|
/>
|
||||||
);
|
<input
|
||||||
|
type="password"
|
||||||
|
placeholder="Password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<button type="submit">Entrar</button>
|
||||||
|
{error && <p className="error">{error}</p>}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default LoginForm;
|
export default LoginForm;
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@ import '../styles/ContentArea.css';
|
||||||
import UserForm from '../forms/UserForm';
|
import UserForm from '../forms/UserForm';
|
||||||
import StudentForm from '../forms/StudentForm';
|
import StudentForm from '../forms/StudentForm';
|
||||||
import TeacherForm from '../forms/TeacherForm';
|
import TeacherForm from '../forms/TeacherForm';
|
||||||
|
import { getRoleFromToken } from '../../utils/jwtHelper';
|
||||||
|
|
||||||
const ContentArea = ({ section, entity, action }) => {
|
const ContentArea = ({ section, entity, action }) => {
|
||||||
// Si falta info, mostramos encabezado básico
|
const role = getRoleFromToken();
|
||||||
|
|
||||||
if (!section || !entity || !action) {
|
if (!section || !entity || !action) {
|
||||||
return (
|
return (
|
||||||
<div className="content-area">
|
<div className="content-area">
|
||||||
|
|
@ -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 (
|
||||||
|
<div className="content-area">
|
||||||
|
<h2>Acceso denegado</h2>
|
||||||
|
<p>No tienes permiso para acceder a esta sección.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sección de gestión de usuarios
|
||||||
if (section === 'User Management') {
|
if (section === 'User Management') {
|
||||||
if (action === 'create') {
|
if (action === 'create') {
|
||||||
switch (entity) {
|
switch (entity) {
|
||||||
|
|
@ -42,7 +63,6 @@ const ContentArea = ({ section, entity, action }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Puedes continuar con update, delete, findById, etc.
|
|
||||||
return (
|
return (
|
||||||
<div className="content-area">
|
<div className="content-area">
|
||||||
<h2>{action} {entity}</h2>
|
<h2>{action} {entity}</h2>
|
||||||
|
|
@ -51,7 +71,6 @@ const ContentArea = ({ section, entity, action }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Si no se reconoce la sección
|
|
||||||
return (
|
return (
|
||||||
<div className="content-area">
|
<div className="content-area">
|
||||||
<h2>{section}</h2>
|
<h2>{section}</h2>
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import TrainingSessionList from '../lists/TrainingSessionList';
|
||||||
import ViewTimetable from '../forms/ViewTimetable';
|
import ViewTimetable from '../forms/ViewTimetable';
|
||||||
import MembershipForm from '../forms/MembershipCreateForm';
|
import MembershipForm from '../forms/MembershipCreateForm';
|
||||||
import MembershipList from '../lists/MembershipList';
|
import MembershipList from '../lists/MembershipList';
|
||||||
|
import InvoiceForm from '../forms/InvoiceForm';
|
||||||
import '../styles/MainLayout.css';
|
import '../styles/MainLayout.css';
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -52,6 +53,7 @@ const MainLayout = () => {
|
||||||
{/* ContentArea general para secciones sin componentes específicos */}
|
{/* ContentArea general para secciones sin componentes específicos */}
|
||||||
<Route path="/admin/user-management/*" element={<ContentArea />} />
|
<Route path="/admin/user-management/*" element={<ContentArea />} />
|
||||||
<Route path="/admin/class-management/*" element={<ContentArea />} />
|
<Route path="/admin/class-management/*" element={<ContentArea />} />
|
||||||
|
<Route path="/admin/finance/*" element={<ContentArea />} />
|
||||||
|
|
||||||
{/*Class Management - rutas específicas*/}
|
{/*Class Management - rutas específicas*/}
|
||||||
<Route path="/admin/class-management/training-groups/create" element={<TrainingGroupForm />} />
|
<Route path="/admin/class-management/training-groups/create" element={<TrainingGroupForm />} />
|
||||||
|
|
@ -65,13 +67,8 @@ const MainLayout = () => {
|
||||||
<Route path="/admin/class-management/memberships/list" element={<MembershipList />} />
|
<Route path="/admin/class-management/memberships/list" element={<MembershipList />} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<Route path="/admin/finance/invoices/create" element={<InvoiceForm />} />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<Route path="/admin/finance/*" element={<ContentArea />} />
|
|
||||||
|
|
||||||
{/* Profile Page*/}
|
{/* Profile Page*/}
|
||||||
<Route path="/profile" element={<ProfilePage />} />
|
<Route path="/profile" element={<ProfilePage />} />
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,45 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import SidebarAdmin from './SidebarAdmin';
|
||||||
import { getRoleFromToken } from '../../utils/jwtHelper';
|
import SidebarTeacher from './SidebarTeacher';
|
||||||
import '../styles/Sidebar.css';
|
import SidebarStudent from './SidebarStudent';
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
const navigate = useNavigate();
|
const [role, setRole] = useState(null);
|
||||||
const role = getRoleFromToken();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="sidebar">
|
|
||||||
<h4>Menú</h4>
|
|
||||||
<div className='sidebar-scroll'>
|
|
||||||
{/* Botón común para todos: Mi Perfil */}
|
|
||||||
<button onClick={() => navigate('/profile')}>👤 Mi Perfil</button>
|
|
||||||
|
|
||||||
{role === 'FULL_ACCESS' && (
|
useEffect(() => {
|
||||||
<>
|
const interval = setInterval(() => {
|
||||||
<button onClick={() => navigate('/admin/dashboard')}>🏠 Dashboard Admin</button>
|
const storedRole = localStorage.getItem('roleName');
|
||||||
|
if (storedRole) {
|
||||||
|
setRole(storedRole);
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 100); // intenta cada 100ms
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
<div className="submenu">
|
}, []);
|
||||||
<h4>👥 Administración de Usuarios</h4>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/users/create')}>➕ Crear Usuario</button>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/users/list')}>📋 Ver Usuarios</button>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/notifications/create')}>🔔 Crear Notificación</button>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/notifications/list')}>📨 Ver Notificaciones</button>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/student-history/create')}>🕒 Crear Historial</button>
|
|
||||||
<button onClick={() => navigate('/admin/user-management/student-history/list')}>📜 Ver Historial</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="submenu">
|
|
||||||
<h4>📚 Administración de Clases</h4>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/training-groups/create')}>➕ Crear Grupo</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/training-groups/list')}>👥 Ver Grupos</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/training-groups/manage-students')}>🧑🏫 Administrar Grupos</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/training-groups/view-timetable')}>🗓️ Ver Horario</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/training-session/list')}>📆 Ver Sesiones</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/assistance/create')}>📝 Registrar Asistencia</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/assistance/list')}>📋 Ver Asistencias</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/memberships/create')}>➕ Crear Membresía</button>
|
|
||||||
<button onClick={() => navigate('/admin/class-management/memberships/list')}>🏷️ Ver Membresías</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='submenu'>
|
if (!role) {
|
||||||
<h4>💵 Administración de Pagos y Facturas</h4>
|
return (
|
||||||
<button onClick={() => navigate('')}>🧾 Crear Factura</button>
|
<div className="sidebar">
|
||||||
<button onClick={() => navigate('')}>📄 Ver Facturas</button>
|
<p>Cargando menú...</p>
|
||||||
<button onClick={() => navigate('')}>💳 Nuevo Pago</button>
|
|
||||||
<button onClick={() => navigate('')}>🛒 Añadir Productos</button>
|
|
||||||
<button onClick={() => navigate('')}>📦 Ver Productos</button>
|
|
||||||
<button onClick={() => navigate('')}>💱 Añadir Tipo de IVA</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{role === 'MANAGE_STUDENTS' && (
|
|
||||||
<>
|
|
||||||
<button onClick={() => navigate('/teacher/dashboard')}>🏠 Dashboard Profesor</button>
|
|
||||||
<button onClick={() => navigate('/teacher/students')}>👨🎓 Mis Estudiantes</button>
|
|
||||||
<button onClick={() => navigate('/teacher/classes')}>📚 Mis Clases</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{role === 'VIEW_OWN_DATA' && (
|
|
||||||
<>
|
|
||||||
<button onClick={() => navigate('/student/dashboard')}>🏠 Mi Panel</button>
|
|
||||||
<button onClick={() => navigate('/student/history')}>🕒 Mi Historial</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
}
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case 'ROLE_ADMIN':
|
||||||
|
return <SidebarAdmin />;
|
||||||
|
case 'ROLE_TEACHER':
|
||||||
|
return <SidebarTeacher />;
|
||||||
|
case 'ROLE_STUDENT':
|
||||||
|
return <SidebarStudent />;
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="sidebar">
|
||||||
|
<p>Sin acceso</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Sidebar;
|
export default Sidebar;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import '../styles/Sidebar.css';
|
||||||
|
|
||||||
|
const SidebarAdmin = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sidebar">
|
||||||
|
<h4>Menú</h4>
|
||||||
|
<div className="sidebar-scroll">
|
||||||
|
<button onClick={() => navigate('/profile')}>👤 Mi Perfil</button>
|
||||||
|
<button onClick={() => navigate('/admin/dashboard')}>🏠 Dashboard Admin</button>
|
||||||
|
|
||||||
|
<div className="submenu">
|
||||||
|
<h4>👥 Administración de Usuarios</h4>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/users/create')}>➕ Crear Usuario</button>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/users/list')}>📋 Ve Usuarios</button>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/notifications/create')}>🔔 Crear Notificación</button>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/notifications/list')}>📨 Ver Notificaciones</button>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/student-history/create')}>🕒 Crear Historial</button>
|
||||||
|
<button onClick={() => navigate('/admin/user-management/student-history/list')}>📜 Ver Historial</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="submenu">
|
||||||
|
<h4>📚 Administración de Clases</h4>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/training-groups/create')}>➕ Crear Grupo</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/training-groups/list')}>👥 Ver Grupos</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/training-groups/manage-students')}>🧑🏫 Administrar Grupos</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/training-groups/view-timetable')}>🗓️ Ver Horario</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/training-session/list')}>📆 Ver Sesiones</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/assistance/create')}>📝 Registrar Asistencia</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/assistance/list')}>📋 Ver Asistencias</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/memberships/create')}>➕ Crear Membresía</button>
|
||||||
|
<button onClick={() => navigate('/admin/class-management/memberships/list')}>🏷️ Ver Membresías</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="submenu">
|
||||||
|
<h4>💵 Finanzas</h4>
|
||||||
|
<button onClick={() => navigate('/admin/finance/invoices/create')}>🧾 Crear Factura</button>
|
||||||
|
<button onClick={() => navigate('/admin/finance/invoices/list')}>📄 Ver Facturas</button>
|
||||||
|
<button onClick={() => navigate('/admin/finance/payments/create')}>💳 Nuevo Pago</button>
|
||||||
|
<button onClick={() => navigate('/admin/finance/products/create')}>🛒 Añadir Productos</button>
|
||||||
|
<button onClick={() => navigate('/admin/finance/products/list')}>📦 Ver Productos</button>
|
||||||
|
<button onClick={() => navigate('/admin/finance/ivatypes/create')}>💱 Añadir Tipo de IVA</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarAdmin;
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import '../styles/Sidebar.css';
|
||||||
|
|
||||||
|
const SidebarStudent = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sidebar">
|
||||||
|
<h4>Menú</h4>
|
||||||
|
<div className="sidebar-scroll">
|
||||||
|
<button onClick={() => navigate('/profile')}>👤 Mi Perfil</button>
|
||||||
|
<button onClick={() => navigate('/student/dashboard')}>🏠 Mi Panel</button>
|
||||||
|
|
||||||
|
<div className="submenu">
|
||||||
|
<h4>🎓 Opciones de Estudiante</h4>
|
||||||
|
<button onClick={() => navigate('/student/history')}>🕒 Mi Historial</button>
|
||||||
|
<button onClick={() => navigate('/student/timetable')}>📅 Ver Horario</button>
|
||||||
|
<button onClick={() => navigate('/student/assistance')}>📋 Ver Asistencia</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarStudent;
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import '../styles/Sidebar.css';
|
||||||
|
|
||||||
|
const SidebarTeacher = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sidebar">
|
||||||
|
<h4>Menú</h4>
|
||||||
|
<div className="sidebar-scroll">
|
||||||
|
<button onClick={() => navigate('/profile')}>👤 Mi Perfil</button>
|
||||||
|
<button onClick={() => navigate('/teacher/dashboard')}>🏠 Dashboard Profesor</button>
|
||||||
|
|
||||||
|
<div className="submenu">
|
||||||
|
<h4>👨🏫 Gestión del Profesor</h4>
|
||||||
|
<button onClick={() => navigate('/teacher/students')}>👨🎓 Mis Estudiantes</button>
|
||||||
|
<button onClick={() => navigate('/teacher/classes')}>📚 Mis Clases</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SidebarTeacher;
|
||||||
|
|
@ -7,10 +7,12 @@ const Topbar = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const role = getRoleFromToken();
|
const role = getRoleFromToken();
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
navigate('/');
|
localStorage.removeItem('roleName');
|
||||||
};
|
navigate('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
const roleLabel = {
|
const roleLabel = {
|
||||||
FULL_ACCESS: 'Admin',
|
FULL_ACCESS: 'Admin',
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import '../../components/styles/DashboardCards.css'; // lo creamos ahora
|
import '../../components/styles/DashboardCards.css';
|
||||||
|
|
||||||
const AdminDashboard = () => {
|
const AdminDashboard = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,8 @@ export const getRoleFromToken = () => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getRoleFromStorage = () => {
|
||||||
|
return localStorage.getItem('roleName');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue