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