Refactor violation handling in PersonEdit.vue by introducing useViolationList composable.

- Centralized violation handling logic with `useViolationList` for improved reusability and maintainability.
- Replaced local violation functions with composable methods in `PersonEdit.vue`.
- Streamlined UI binding for validation errors across multiple inputs.
This commit is contained in:
2025-10-29 12:26:37 +01:00
parent e89b33bc1a
commit 83a2c04537
2 changed files with 80 additions and 60 deletions

View File

@@ -0,0 +1,57 @@
import {ref} from "vue";
import {ValidationExceptionInterface} from "ChillMainAssets/types";
export function useViolationList<T extends Record<string, Record<string, string>>>() {
type ViolationKey = Extract<keyof T, string>;
const violationsList = ref<ValidationExceptionInterface<T>|null>(null);
function violationTitles<P extends ViolationKey>(property: P): string[] {
if (null === violationsList.value) {
return [];
}
const r = violationsList.value.violationsByNormalizedProperty(property).map((v) => v.title);
return r;
}
function violationTitlesWithParameter<
P extends ViolationKey,
Param extends Extract<keyof T[P], string>
>(
property: P,
with_parameter: Param,
with_parameter_value: T[P][Param],
): string[] {
if (violationsList.value === null) {
return [];
}
return violationsList.value.violationsByNormalizedPropertyAndParams(property, with_parameter, with_parameter_value)
.map((v) => v.title);
}
function hasViolation<P extends ViolationKey>(property: P): boolean {
return violationTitles(property).length > 0;
}
function hasViolationWithParameter<
P extends ViolationKey,
Param extends Extract<keyof T[P], string>
>(
property: P,
with_parameter: Param,
with_parameter_value: T[P][Param],
): boolean {
return violationTitlesWithParameter(property, with_parameter, with_parameter_value).length > 0;
}
function setValidationException<V extends ValidationExceptionInterface<T>>(validationException: V): void {
violationsList.value = validationException;
}
function cleanException(): void {
violationsList.value = null;
}
return {violationTitles, violationTitlesWithParameter, setValidationException, cleanException, hasViolationWithParameter, hasViolation};
}

View File

@@ -5,7 +5,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('lastName') }" :class="{ 'is-invalid': violations.hasViolation('lastName') }"
id="lastname" id="lastname"
v-model="lastName" v-model="lastName"
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)" :placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
@@ -16,7 +16,7 @@
</div> </div>
</div> </div>
<div <div
v-for="err in violationTitles('lastName')" v-for="err in violations.violationTitles('lastName')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -40,7 +40,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('firstName') }" :class="{ 'is-invalid': violations.hasViolation('firstName') }"
id="firstname" id="firstname"
v-model="firstName" v-model="firstName"
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)" :placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
@@ -51,7 +51,7 @@
</div> </div>
</div> </div>
<div <div
v-for="err in violationTitles('firstName')" v-for="err in violations.violationTitles('firstName')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -94,7 +94,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{'is-invalid': hasViolationWithParameter('identifiers', 'definition_id', worker.definition_id.toString())}" :class="{'is-invalid': violations.hasViolationWithParameter('identifiers', 'definition_id', worker.definition_id.toString())}"
type="text" type="text"
:name="'worker_' + worker.definition_id" :name="'worker_' + worker.definition_id"
:placeholder="localizeString(worker.label)" :placeholder="localizeString(worker.label)"
@@ -105,7 +105,7 @@
}}</label> }}</label>
</div> </div>
<div <div
v-for="err in violationTitlesWithParameter('identifiers', 'definition_id', worker.definition_id.toString())" v-for="err in violations.violationTitlesWithParameter('identifiers', 'definition_id', worker.definition_id.toString())"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -118,7 +118,7 @@
<div class="form-floating"> <div class="form-floating">
<select <select
class="form-select form-select-lg" class="form-select form-select-lg"
:class="{ 'is-invalid': hasViolation('gender') }" :class="{ 'is-invalid': violations.hasViolation('gender') }"
id="gender" id="gender"
v-model="gender" v-model="gender"
> >
@@ -134,7 +134,7 @@
}}</label> }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('gender')" v-for="err in violations.violationTitles('gender')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -147,7 +147,7 @@
<div class="form-floating"> <div class="form-floating">
<select <select
class="form-select form-select-lg" class="form-select form-select-lg"
:class="{ 'is-invalid': hasViolation('center') }" :class="{ 'is-invalid': violations.hasViolation('center') }"
id="center" id="center"
v-model="center" v-model="center"
> >
@@ -161,7 +161,7 @@
<label for="center">{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label> <label for="center">{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('center')" v-for="err in violations.violationTitles('center')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -174,7 +174,7 @@
<div class="form-floating"> <div class="form-floating">
<select <select
class="form-select form-select-lg" class="form-select form-select-lg"
:class="{ 'is-invalid': hasViolation('civility') }" :class="{ 'is-invalid': violations.hasViolation('civility') }"
id="civility" id="civility"
v-model="civility" v-model="civility"
> >
@@ -188,7 +188,7 @@
<label for="civility">{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label> <label for="civility">{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('civility')" v-for="err in violations.violationTitles('civility')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -204,7 +204,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('birthdate') }" :class="{ 'is-invalid': violations.hasViolation('birthdate') }"
name="birthdate" name="birthdate"
type="date" type="date"
v-model="birthDate" v-model="birthDate"
@@ -214,7 +214,7 @@
<label for="birthdate">{{ trans(BIRTHDATE) }}</label> <label for="birthdate">{{ trans(BIRTHDATE) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('birthdate')" v-for="err in violations.violationTitles('birthdate')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -230,7 +230,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('phonenumber') }" :class="{ 'is-invalid': violations.hasViolation('phonenumber') }"
v-model="phonenumber" v-model="phonenumber"
:placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)" :placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
:aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)" :aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
@@ -239,7 +239,7 @@
<label for="phonenumber">{{ trans(PERSON_MESSAGES_PERSON_PHONENUMBER) }}</label> <label for="phonenumber">{{ trans(PERSON_MESSAGES_PERSON_PHONENUMBER) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('phonenumber')" v-for="err in violations.violationTitles('phonenumber')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -255,7 +255,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('mobilenumber') }" :class="{ 'is-invalid': violations.hasViolation('mobilenumber') }"
v-model="mobilenumber" v-model="mobilenumber"
:placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)" :placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
:aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)" :aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
@@ -264,7 +264,7 @@
<label for="mobilenumber">{{ trans(PERSON_MESSAGES_PERSON_MOBILENUMBER) }}</label> <label for="mobilenumber">{{ trans(PERSON_MESSAGES_PERSON_MOBILENUMBER) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('mobilenumber')" v-for="err in violations.violationTitles('mobilenumber')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -280,7 +280,7 @@
<div class="form-floating"> <div class="form-floating">
<input <input
class="form-control form-control-lg" class="form-control form-control-lg"
:class="{ 'is-invalid': hasViolation('email') }" :class="{ 'is-invalid': violations.hasViolation('email') }"
v-model="email" v-model="email"
:placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)" :placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)"
:aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)" :aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)"
@@ -289,7 +289,7 @@
<label for="email">{{ trans(PERSON_MESSAGES_PERSON_EMAIL) }}</label> <label for="email">{{ trans(PERSON_MESSAGES_PERSON_EMAIL) }}</label>
</div> </div>
<div <div
v-for="err in violationTitles('email')" v-for="err in violations.violationTitles('email')"
class="invalid-feedback was-validated-force" class="invalid-feedback was-validated-force"
> >
{{ err }} {{ err }}
@@ -381,6 +381,7 @@ import {
} from "ChillMainAssets/lib/api/apiMethods"; } from "ChillMainAssets/lib/api/apiMethods";
import {useToast} from "vue-toast-notification"; import {useToast} from "vue-toast-notification";
import {getTimezoneOffsetString, ISOToDate} from "ChillMainAssets/chill/js/date"; import {getTimezoneOffsetString, ISOToDate} from "ChillMainAssets/chill/js/date";
import {useViolationList} from "ChillMainAssets/vuejs/_composables/violationList";
interface PersonEditComponentConfig { interface PersonEditComponentConfig {
id?: number | null; id?: number | null;
@@ -608,45 +609,7 @@ function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
} }
} }
type WritePersonViolationKey = Extract<keyof WritePersonViolationMap, string>; const violations = useViolationList<WritePersonViolationMap>();
const violationsList = ref<ValidationExceptionInterface<WritePersonViolationMap>|null>(null);
function violationTitles<P extends WritePersonViolationKey>(property: P): string[] {
if (null === violationsList.value) {
return [];
}
return violationsList.value.violationsByNormalizedProperty(property).map((v) => v.title);
}
function violationTitlesWithParameter<
P extends WritePersonViolationKey,
Param extends Extract<keyof WritePersonViolationMap[P], string>
>(
property: P,
with_parameter: Param,
with_parameter_value: WritePersonViolationMap[P][Param],
): string[] {
if (violationsList.value === null) {
return [];
}
return violationsList.value.violationsByNormalizedPropertyAndParams(property, with_parameter, with_parameter_value)
.map((v) => v.title);
}
function hasViolation<P extends WritePersonViolationKey>(property: P): boolean {
return violationTitles(property).length > 0;
}
function hasViolationWithParameter<
P extends WritePersonViolationKey,
Param extends Extract<keyof WritePersonViolationMap[P], string>
>(
property: P,
with_parameter: Param,
with_parameter_value: WritePersonViolationMap[P][Param],
): boolean {
return violationTitlesWithParameter(property, with_parameter, with_parameter_value).length > 0;
}
function submitNewAddress(payload: { addressId: number }) { function submitNewAddress(payload: { addressId: number }) {
// person.addressId = payload.addressId; // person.addressId = payload.addressId;
@@ -659,7 +622,7 @@ async function postPerson(): Promise<void> {
emit("onPersonCreated", { person: createdPerson }); emit("onPersonCreated", { person: createdPerson });
} catch (e: unknown) { } catch (e: unknown) {
if (isValidationException<WritePersonViolationMap>(e)) { if (isValidationException<WritePersonViolationMap>(e)) {
violationsList.value = e; violations.setValidationException(e);
} else { } else {
toast.error(trans(PERSON_EDIT_ERROR_WHILE_SAVING)); toast.error(trans(PERSON_EDIT_ERROR_WHILE_SAVING));
} }