diff --git a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/application/pilot/PatchPilotUseCase.java b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/application/pilot/PatchPilotUseCase.java index 5e8b88c..8c23020 100644 --- a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/application/pilot/PatchPilotUseCase.java +++ b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/application/pilot/PatchPilotUseCase.java @@ -37,6 +37,10 @@ public PilotResponse patchPilot(UUID id, PilotPatchRequest request) { if (surname != null) { pilot.setSurname(surname); } + String licence = request.getLicence(); + if (licence != null) { + pilot.setLicence(licence); + } String icaoCode = request.getOperationalBaseIcaoCode(); if (icaoCode != null) { pilot.setOperationalBase( diff --git a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/GlobalExceptionHandler.java b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/GlobalExceptionHandler.java index fe78514..cfa9b65 100644 --- a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/GlobalExceptionHandler.java +++ b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/GlobalExceptionHandler.java @@ -71,8 +71,22 @@ public ResponseEntity handleRuleViolation(RuleViolat @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity handleDataIntegrityViolation(DataIntegrityViolationException ex, HttpServletRequest request) { + String causeMessage = ex.getMostSpecificCause().getMessage(); + if (causeMessage != null) { + String lower = causeMessage.toLowerCase(); + if (lower.contains("value too long") || lower.contains("too long for type")) { + return buildResponse(HttpStatus.BAD_REQUEST, + "Podana wartość jest zbyt długa", + request); + } + if (lower.contains("flight") || lower.contains("pilot_id") || lower.contains("aircraft_id")) { + return buildResponse(HttpStatus.CONFLICT, + "Nie można usunąć samolotu, ponieważ ma przypisane loty", + request); + } + } return buildResponse(HttpStatus.CONFLICT, - "Aircraft cannot be deleted because it has assigned flights", + "Operacja narusza ograniczenia integralności danych", request); } diff --git a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/security/SecurityConfig.java b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/security/SecurityConfig.java index eedc941..fa9299e 100644 --- a/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/security/SecurityConfig.java +++ b/skyroster-backend/src/main/java/pl/skyroster/skyroster_backend/infrastructure/config/security/SecurityConfig.java @@ -52,7 +52,6 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) { .requestMatchers(HttpMethod.DELETE, "/api/pilots/**").hasRole("operations_administrator") .requestMatchers(HttpMethod.PATCH, "/api/pilots/**").hasRole("operations_administrator") .requestMatchers(HttpMethod.POST, "/api/pilots/**").hasRole("operations_administrator") - .requestMatchers(HttpMethod.POST,"/api/rule/**").hasRole("compliance_officer") .requestMatchers("/api/**").authenticated() .anyRequest().denyAll() ) diff --git a/skyroster-frontend/src/components/pilots/PilotDialog.vue b/skyroster-frontend/src/components/pilots/PilotDialog.vue index 4ddc4ab..e16aed5 100644 --- a/skyroster-frontend/src/components/pilots/PilotDialog.vue +++ b/skyroster-frontend/src/components/pilots/PilotDialog.vue @@ -18,6 +18,7 @@ const emit = defineEmits(['update:visible', 'save']) const pilotsStore = usePilotsStore() const formData = ref(getEmptyForm()) +const submitting = ref(false) const dialogVisible = computed({ get: () => props.visible, @@ -26,6 +27,16 @@ const dialogVisible = computed({ const dialogTitle = computed(() => props.isEditMode ? 'Edytuj pilota' : 'Dodaj pilota') +const isFormValid = computed(() => { + const f = formData.value + return ( + f.imie?.trim().length > 0 && + f.nazwisko?.trim().length > 0 && + f.bazaMacierzysta != null && + f.kwalifikacje?.length > 0 + ) +}) + function getEmptyForm() { return { imie: '', @@ -45,8 +56,11 @@ function resetForm() { } } -function handleSave() { +async function handleSave() { + if (!isFormValid.value) return + submitting.value = true emit('save', { ...formData.value }) + submitting.value = false dialogVisible.value = false } @@ -121,7 +135,7 @@ function handleCancel() { diff --git a/skyroster-frontend/src/components/rules/RuleDialog.vue b/skyroster-frontend/src/components/rules/RuleDialog.vue index a0b4d15..6b3501a 100644 --- a/skyroster-frontend/src/components/rules/RuleDialog.vue +++ b/skyroster-frontend/src/components/rules/RuleDialog.vue @@ -19,6 +19,7 @@ const emit = defineEmits(['update:visible', 'save']) const rulesStore = useRulesStore() const formData = ref(getEmptyForm()) +const submitting = ref(false) const dialogVisible = computed({ get: () => props.visible, @@ -27,6 +28,16 @@ const dialogVisible = computed({ const dialogTitle = computed(() => props.isEditMode ? 'Edytuj zasadę' : 'Dodaj zasadę') +const isFormValid = computed(() => { + const f = formData.value + return ( + f.nazwa?.trim().length > 0 && + f.typ != null && + f.okres != null && + f.wartosc != null && f.wartosc > 0 + ) +}) + const valueLabel = computed(() => { switch (formData.value.typ) { case 'max_czas_pracy': @@ -58,8 +69,11 @@ function resetForm() { } } -function handleSave() { +async function handleSave() { + if (!isFormValid.value) return + submitting.value = true emit('save', { ...formData.value }) + submitting.value = false dialogVisible.value = false } @@ -79,7 +93,7 @@ function handleCancel() {
- +
@@ -115,13 +129,13 @@ function handleCancel() {
-