mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-27 06:08:30 +00:00
Enhance validation in PersonEdit: Introduce hasValidationError and validationError helpers for form inputs. Improve error feedback for fields such as firstName, lastName, gender, and others. Refactor postPerson to handle validation exceptions and map errors to specific fields. Update related methods, styles, and API error type definitions.
This commit is contained in:
@@ -117,9 +117,9 @@ export class ValidationException<
|
|||||||
* Check that the exception is a ValidationExceptionInterface
|
* Check that the exception is a ValidationExceptionInterface
|
||||||
* @param x
|
* @param x
|
||||||
*/
|
*/
|
||||||
export function isValidationException(
|
export function isValidationException<M extends Record<string, Record<string, unknown>>>(
|
||||||
x: unknown,
|
x: unknown,
|
||||||
): x is ValidationExceptionInterface<Record<string, Record<string, unknown>>> {
|
): x is ValidationExceptionInterface<M> {
|
||||||
return (
|
return (
|
||||||
x instanceof ValidationException ||
|
x instanceof ValidationException ||
|
||||||
(typeof x === "object" &&
|
(typeof x === "object" &&
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ import {
|
|||||||
ONTHEFLY_CREATE_THIRDPARTY,
|
ONTHEFLY_CREATE_THIRDPARTY,
|
||||||
trans,
|
trans,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
import {CreatableEntityType, Person} from "ChillPersonAssets/types";
|
import { CreatableEntityType, Person } from "ChillPersonAssets/types";
|
||||||
import { CreateComponentConfig } from "ChillMainAssets/types";
|
import { CreateComponentConfig } from "ChillMainAssets/types";
|
||||||
import PersonEdit from "ChillPersonAssets/vuejs/_components/OnTheFly/PersonEdit.vue";
|
import PersonEdit from "ChillPersonAssets/vuejs/_components/OnTheFly/PersonEdit.vue";
|
||||||
|
|
||||||
@@ -66,9 +66,8 @@ const props = withDefaults(defineProps<CreateComponentConfig>(), {
|
|||||||
query: "",
|
query: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit =
|
||||||
(e: "onPersonCreated", payload: { person: Person }): void;
|
defineEmits<(e: "onPersonCreated", payload: { person: Person }) => void>();
|
||||||
}>();
|
|
||||||
|
|
||||||
const type = ref<CreatableEntityType | null>(null);
|
const type = ref<CreatableEntityType | null>(null);
|
||||||
|
|
||||||
@@ -112,9 +111,7 @@ function save(): void {
|
|||||||
castPerson.value.postPerson();
|
castPerson.value.postPerson();
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({save});
|
defineExpose({ save });
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||||
import Create from "ChillMainAssets/vuejs/OnTheFly/components/Create.vue";
|
import Create from "ChillMainAssets/vuejs/OnTheFly/components/Create.vue";
|
||||||
import { CreateComponentConfig } from "ChillMainAssets/types";
|
import { CreateComponentConfig } from "ChillMainAssets/types";
|
||||||
import {trans, SAVE} from "translator";
|
import { trans, SAVE } from "translator";
|
||||||
import {useTemplateRef} from "vue";
|
import { useTemplateRef } from "vue";
|
||||||
import {Person} from "ChillPersonAssets/types";
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
(e: "onPersonCreated", payload: { person: Person }): void;
|
(e: "onPersonCreated", payload: { person: Person }): void;
|
||||||
@@ -14,17 +14,16 @@ const emit = defineEmits<{
|
|||||||
const props = defineProps<CreateComponentConfig>();
|
const props = defineProps<CreateComponentConfig>();
|
||||||
const modalDialogClass = { "modal-xl": true, "modal-scrollable": true };
|
const modalDialogClass = { "modal-xl": true, "modal-scrollable": true };
|
||||||
|
|
||||||
type CreateComponentType = InstanceType<typeof Create>
|
type CreateComponentType = InstanceType<typeof Create>;
|
||||||
|
|
||||||
const create = useTemplateRef<CreateComponentType>("create");
|
const create = useTemplateRef<CreateComponentType>("create");
|
||||||
|
|
||||||
function save(): void {
|
function save(): void {
|
||||||
console.log('save from CreateModal');
|
console.log("save from CreateModal");
|
||||||
create.value?.save();
|
create.value?.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({save})
|
defineExpose({ save });
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -49,7 +48,9 @@ defineExpose({save})
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<button class="btn btn-save" type="button" @click.prevent="save">{{ trans(SAVE) }}</button>
|
<button class="btn btn-save" type="button" @click.prevent="save">
|
||||||
|
{{ trans(SAVE) }}
|
||||||
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</teleport>
|
</teleport>
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import {
|
|||||||
Scope,
|
Scope,
|
||||||
Job,
|
Job,
|
||||||
PrivateCommentEmbeddable,
|
PrivateCommentEmbeddable,
|
||||||
TranslatableString, DateTimeCreate, SetGender, SetCenter, SetCivility,
|
TranslatableString,
|
||||||
|
DateTimeCreate,
|
||||||
|
SetGender,
|
||||||
|
SetCenter,
|
||||||
|
SetCivility,
|
||||||
} from "ChillMainAssets/types";
|
} from "ChillMainAssets/types";
|
||||||
import { StoredObject } from "ChillDocStoreAssets/types";
|
import { StoredObject } from "ChillDocStoreAssets/types";
|
||||||
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
|
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||||
import { Center, Civility, Gender } from "ChillMainAssets/types";
|
import { Center, Civility, Gender } from "ChillMainAssets/types";
|
||||||
import {AltName, Person, PersonIdentifierWorker, PersonWrite} from "ChillPersonAssets/types";
|
import {
|
||||||
|
AltName,
|
||||||
|
Person,
|
||||||
|
PersonIdentifierWorker,
|
||||||
|
PersonWrite,
|
||||||
|
} from "ChillPersonAssets/types";
|
||||||
import person from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
import person from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -30,12 +35,48 @@ export const getCivilities = async (): Promise<Civility[]> =>
|
|||||||
export const getGenders = async (): Promise<Gender[]> =>
|
export const getGenders = async (): Promise<Gender[]> =>
|
||||||
fetchResults("/api/1.0/main/gender.json");
|
fetchResults("/api/1.0/main/gender.json");
|
||||||
|
|
||||||
export const getCentersForPersonCreation = async (): Promise<{showCenters: boolean; centers: Center[];}> =>
|
export const getCentersForPersonCreation = async (): Promise<{
|
||||||
makeFetch("GET", "/api/1.0/person/creation/authorized-centers", null);
|
showCenters: boolean;
|
||||||
|
centers: Center[];
|
||||||
|
}> => makeFetch("GET", "/api/1.0/person/creation/authorized-centers", null);
|
||||||
|
|
||||||
export const getPersonIdentifiers = async (): Promise<PersonIdentifierWorker[]> =>
|
export const getPersonIdentifiers = async (): Promise<
|
||||||
fetchResults("/api/1.0/person/identifiers/workers");
|
PersonIdentifierWorker[]
|
||||||
|
> => fetchResults("/api/1.0/person/identifiers/workers");
|
||||||
|
|
||||||
export const createPerson = async (person: PersonWrite): Promise<Person> => {
|
export interface WritePersonViolationMap
|
||||||
return makeFetch("POST", "/api/1.0/person/person.json", person);
|
extends Record<string, Record<string, unknown>> {
|
||||||
|
firstName: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
|
lastName: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
|
gender: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
|
mobilenumber: {
|
||||||
|
"{{ types }}": string; // ex: "mobile number"
|
||||||
|
"{{ value }}": string; // ex: "+33 1 02 03 04 05"
|
||||||
|
};
|
||||||
|
phonenumber: {
|
||||||
|
"{{ types }}": string; // ex: "mobile number"
|
||||||
|
"{{ value }}": string; // ex: "+33 1 02 03 04 05"
|
||||||
|
};
|
||||||
|
email: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
|
center: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
|
civility: {
|
||||||
|
"{{ value }}": string | null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
export const createPerson = async (person: PersonWrite): Promise<Person> => {
|
||||||
|
return makeFetch<PersonWrite, Person, WritePersonViolationMap>(
|
||||||
|
"POST",
|
||||||
|
"/api/1.0/person/person.json",
|
||||||
|
person,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ import type {
|
|||||||
Suggestion,
|
Suggestion,
|
||||||
SearchOptions,
|
SearchOptions,
|
||||||
CreatableEntityType,
|
CreatableEntityType,
|
||||||
EntityType, Person,
|
EntityType,
|
||||||
|
Person,
|
||||||
} from "ChillPersonAssets/types";
|
} from "ChillPersonAssets/types";
|
||||||
import { marked } from "marked";
|
import { marked } from "marked";
|
||||||
import options = marked.options;
|
import options = marked.options;
|
||||||
@@ -113,8 +114,12 @@ function closeModalCreate() {
|
|||||||
function onPersonCreated(payload: { person: Person }) {
|
function onPersonCreated(payload: { person: Person }) {
|
||||||
console.log("onPersonCreated", payload);
|
console.log("onPersonCreated", payload);
|
||||||
showModalCreate.value = false;
|
showModalCreate.value = false;
|
||||||
const suggestion = {result: payload.person, relevance: 999999, key: "person"};
|
const suggestion = {
|
||||||
emit("addNewPersons", {selected: [suggestion]});
|
result: payload.person,
|
||||||
|
relevance: 999999,
|
||||||
|
key: "person",
|
||||||
|
};
|
||||||
|
emit("addNewPersons", { selected: [suggestion] });
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
|
:class="{ 'is-invalid': hasValidationError('lastName') }"
|
||||||
id="lastname"
|
id="lastname"
|
||||||
v-model="lastName"
|
v-model="lastName"
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
|
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
|
||||||
/>
|
/>
|
||||||
<label for="lastname">{{ trans(PERSON_MESSAGES_PERSON_LASTNAME) }}</label>
|
<label for="lastname">{{
|
||||||
|
trans(PERSON_MESSAGES_PERSON_LASTNAME)
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('lastName')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="queryItems">
|
<div v-if="queryItems">
|
||||||
@@ -22,9 +35,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
|
:class="{ 'is-invalid': hasValidationError('firstName') }"
|
||||||
id="firstname"
|
id="firstname"
|
||||||
v-model="firstName"
|
v-model="firstName"
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
|
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
|
||||||
@@ -33,6 +49,14 @@
|
|||||||
trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
|
trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
|
||||||
}}</label>
|
}}</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('firstName')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="queryItems">
|
<div v-if="queryItems">
|
||||||
<ul class="list-suggest add-items inline">
|
<ul class="list-suggest add-items inline">
|
||||||
@@ -46,38 +70,51 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div v-for="(a, i) in config.altNames" :key="a.key" class="mb-3">
|
||||||
v-for="(a, i) in config.altNames"
|
<div class="input-group has-validation">
|
||||||
:key="a.key"
|
<div class="form-floating">
|
||||||
class="form-floating mb-3"
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
:id="a.key"
|
:id="a.key"
|
||||||
:name="'label_'+a.key"
|
:name="'label_' + a.key"
|
||||||
value=""
|
value=""
|
||||||
@input="onAltNameInput($event, a.key)"
|
@input="onAltNameInput($event, a.key)"
|
||||||
/>
|
/>
|
||||||
<label :for="'label_'+a.key">{{ localizeString(a.labels) }}</label>
|
<label :for="'label_' + a.key">{{ localizeString(a.labels) }}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-for="worker in config.identifiers"
|
v-for="worker in config.identifiers"
|
||||||
:key="worker.definition_id"
|
:key="worker.definition_id"
|
||||||
class="form-floating mb-3"
|
class="mb-3"
|
||||||
>
|
>
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
type="text"
|
type="text"
|
||||||
:name="'worker_'+worker.definition_id"
|
:name="'worker_' + worker.definition_id"
|
||||||
:placeholder="localizeString(worker.label)"
|
:placeholder="localizeString(worker.label)"
|
||||||
@input="onIdentifierInput($event, worker.definition_id)"
|
@input="onIdentifierInput($event, worker.definition_id)"
|
||||||
/>
|
/>
|
||||||
<label :for="'worker_'+worker.definition_id">{{ localizeString(worker.label) }}</label>
|
<label :for="'worker_' + worker.definition_id">{{
|
||||||
|
localizeString(worker.label)
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
<select class="form-select form-select-lg" id="gender" v-model="gender">
|
<div class="input-group has-validation">
|
||||||
|
<div class="form-floating">
|
||||||
|
<select
|
||||||
|
class="form-select form-select-lg"
|
||||||
|
:class="{ 'is-invalid': hasValidationError('gender') }"
|
||||||
|
id="gender"
|
||||||
|
v-model="gender"
|
||||||
|
>
|
||||||
<option selected disabled>
|
<option selected disabled>
|
||||||
{{ trans(PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER) }}
|
{{ trans(PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER) }}
|
||||||
</option>
|
</option>
|
||||||
@@ -85,14 +122,27 @@
|
|||||||
{{ g.label }}
|
{{ g.label }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_GENDER_TITLE) }}</label>
|
<label for="gender" class="form-label">{{
|
||||||
|
trans(PERSON_MESSAGES_PERSON_GENDER_TITLE)
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('gender')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div class="mb-3" v-if="showCenters && config.centers.length > 1">
|
||||||
class="form-floating mb-3"
|
<div class="input-group">
|
||||||
v-if="showCenters && config.centers.length > 1"
|
<div class="form-floating">
|
||||||
|
<select
|
||||||
|
class="form-select form-select-lg"
|
||||||
|
id="center"
|
||||||
|
v-model="center"
|
||||||
>
|
>
|
||||||
<select class="form-select form-select-lg" id="center" v-model="center">
|
|
||||||
<option selected disabled>
|
<option selected disabled>
|
||||||
{{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
|
{{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
|
||||||
</option>
|
</option>
|
||||||
@@ -102,8 +152,18 @@
|
|||||||
</select>
|
</select>
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
|
<label>{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('center')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
|
<div class="form-floating">
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-lg"
|
class="form-select form-select-lg"
|
||||||
id="civility"
|
id="civility"
|
||||||
@@ -118,11 +178,21 @@
|
|||||||
</select>
|
</select>
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
|
<label>{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('civility')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
<span class="input-group-text" id="phonenumber">
|
<span class="input-group-text" id="phonenumber">
|
||||||
<i class="fa fa-fw fa-phone"></i>
|
<i class="fa fa-fw fa-phone"></i>
|
||||||
</span>
|
</span>
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
v-model="phonenumber"
|
v-model="phonenumber"
|
||||||
@@ -131,11 +201,21 @@
|
|||||||
aria-describedby="phonenumber"
|
aria-describedby="phonenumber"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('phonenumber')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
<span class="input-group-text" id="mobilenumber">
|
<span class="input-group-text" id="mobilenumber">
|
||||||
<i class="fa fa-fw fa-mobile"></i>
|
<i class="fa fa-fw fa-mobile"></i>
|
||||||
</span>
|
</span>
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
v-model="mobilenumber"
|
v-model="mobilenumber"
|
||||||
@@ -144,11 +224,21 @@
|
|||||||
aria-describedby="mobilenumber"
|
aria-describedby="mobilenumber"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('mobilenumber')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
|
<div class="input-group has-validation">
|
||||||
<span class="input-group-text" id="email">
|
<span class="input-group-text" id="email">
|
||||||
<i class="fa fa-fw fa-at"></i>
|
<i class="fa fa-fw fa-at"></i>
|
||||||
</span>
|
</span>
|
||||||
|
<div class="form-floating">
|
||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
@@ -157,6 +247,14 @@
|
|||||||
aria-describedby="email"
|
aria-describedby="email"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('email')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="action === 'create'" class="input-group mb-3 form-check">
|
<div v-if="action === 'create'" class="input-group mb-3 form-check">
|
||||||
<input
|
<input
|
||||||
@@ -190,6 +288,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, onMounted } from "vue";
|
import { ref, reactive, computed, onMounted } from "vue";
|
||||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
||||||
@@ -200,7 +299,9 @@ import {
|
|||||||
getCivilities,
|
getCivilities,
|
||||||
getGenders,
|
getGenders,
|
||||||
getPerson,
|
getPerson,
|
||||||
getPersonAltNames, getPersonIdentifiers,
|
getPersonAltNames,
|
||||||
|
getPersonIdentifiers,
|
||||||
|
WritePersonViolationMap,
|
||||||
} from "../../_api/OnTheFly";
|
} from "../../_api/OnTheFly";
|
||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
@@ -230,9 +331,12 @@ import {
|
|||||||
PersonWrite,
|
PersonWrite,
|
||||||
PersonIdentifierWorker,
|
PersonIdentifierWorker,
|
||||||
type Suggestion,
|
type Suggestion,
|
||||||
type EntitiesOrMe
|
type EntitiesOrMe,
|
||||||
} from "ChillPersonAssets/types";
|
} from "ChillPersonAssets/types";
|
||||||
|
import {
|
||||||
|
isValidationException,
|
||||||
|
ValidationExceptionInterface,
|
||||||
|
} from "ChillMainAssets/lib/api/apiMethods";
|
||||||
|
|
||||||
interface PersonEditComponentConfig {
|
interface PersonEditComponentConfig {
|
||||||
id?: number | null;
|
id?: number | null;
|
||||||
@@ -246,11 +350,10 @@ const props = withDefaults(defineProps<PersonEditComponentConfig>(), {
|
|||||||
type: "TODO",
|
type: "TODO",
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit =
|
||||||
(e: "onPersonCreated", payload: { person: Person }): void;
|
defineEmits<(e: "onPersonCreated", payload: { person: Person }) => void>();
|
||||||
}>();
|
|
||||||
|
|
||||||
defineExpose({postPerson});
|
defineExpose({ postPerson });
|
||||||
|
|
||||||
const person = reactive<PersonWrite>({
|
const person = reactive<PersonWrite>({
|
||||||
type: "person",
|
type: "person",
|
||||||
@@ -318,7 +421,9 @@ const lastName = computed({
|
|||||||
const gender = computed({
|
const gender = computed({
|
||||||
get: () => (person.gender ? person.gender.id : null),
|
get: () => (person.gender ? person.gender.id : null),
|
||||||
set: (value: string | null) => {
|
set: (value: string | null) => {
|
||||||
person.gender = value ? { id: Number.parseInt(value), type: "chill_main_gender" } : null;
|
person.gender = value
|
||||||
|
? { id: Number.parseInt(value), type: "chill_main_gender" }
|
||||||
|
: null;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const civility = computed({
|
const civility = computed({
|
||||||
@@ -373,7 +478,7 @@ const center = computed({
|
|||||||
if (null !== value) {
|
if (null !== value) {
|
||||||
person.center = {
|
person.center = {
|
||||||
id: value.id,
|
id: value.id,
|
||||||
type: (value).type,
|
type: value.type,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
person.center = null;
|
person.center = null;
|
||||||
@@ -385,13 +490,16 @@ const center = computed({
|
|||||||
* Find the query items to display for suggestion
|
* Find the query items to display for suggestion
|
||||||
*/
|
*/
|
||||||
const queryItems = computed(() => {
|
const queryItems = computed(() => {
|
||||||
const words: null|string[] = props.query ? props.query.split(" ") : null;
|
const words: null | string[] = props.query ? props.query.split(" ") : null;
|
||||||
|
|
||||||
if (null === words) {
|
if (null === words) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstNameWords = (person.firstName || "").trim().toLowerCase().split(" ");
|
const firstNameWords = (person.firstName || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.split(" ");
|
||||||
const lastNameWords = (person.lastName || "").trim().toLowerCase().split(" ");
|
const lastNameWords = (person.lastName || "").trim().toLowerCase().split(" ");
|
||||||
|
|
||||||
return words
|
return words
|
||||||
@@ -399,25 +507,6 @@ const queryItems = computed(() => {
|
|||||||
.filter((word) => !lastNameWords.includes(word.toLowerCase()));
|
.filter((word) => !lastNameWords.includes(word.toLowerCase()));
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
function checkErrors() {
|
|
||||||
errors.value = [];
|
|
||||||
if (person.lastName === "") {
|
|
||||||
errors.value.push("Le nom ne doit pas être vide.");
|
|
||||||
}
|
|
||||||
if (person.firstName === "") {
|
|
||||||
errors.value.push("Le prénom ne doit pas être vide.");
|
|
||||||
}
|
|
||||||
if (!person.gender) {
|
|
||||||
errors.value.push("Le genre doit être renseigné");
|
|
||||||
}
|
|
||||||
if (showCenters.value && person.center === null) {
|
|
||||||
errors.value.push("Le centre doit être renseigné");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function loadData() {
|
async function loadData() {
|
||||||
if (props.id !== undefined && props.id !== null) {
|
if (props.id !== undefined && props.id !== null) {
|
||||||
const person = await getPerson(props.id);
|
const person = await getPerson(props.id);
|
||||||
@@ -429,7 +518,7 @@ function onAltNameInput(event: Event, key: string): void {
|
|||||||
const value = target.value;
|
const value = target.value;
|
||||||
const updateAltNamesKey = person.altNames.findIndex((a) => a.key === key);
|
const updateAltNamesKey = person.altNames.findIndex((a) => a.key === key);
|
||||||
if (-1 === updateAltNamesKey) {
|
if (-1 === updateAltNamesKey) {
|
||||||
person.altNames.push({key, value})
|
person.altNames.push({ key, value });
|
||||||
} else {
|
} else {
|
||||||
person.altNames[updateAltNamesKey].value = value;
|
person.altNames[updateAltNamesKey].value = value;
|
||||||
}
|
}
|
||||||
@@ -438,11 +527,17 @@ function onAltNameInput(event: Event, key: string): void {
|
|||||||
function onIdentifierInput(event: Event, definition_id: number): void {
|
function onIdentifierInput(event: Event, definition_id: number): void {
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
const value = target.value;
|
const value = target.value;
|
||||||
const updateIdentifierKey = person.identifiers.findIndex((w) => w.definition_id === definition_id);
|
const updateIdentifierKey = person.identifiers.findIndex(
|
||||||
|
(w) => w.definition_id === definition_id,
|
||||||
|
);
|
||||||
if (-1 === updateIdentifierKey) {
|
if (-1 === updateIdentifierKey) {
|
||||||
person.identifiers.push({type: "person_identifier", definition_id, value: { content: value }})
|
person.identifiers.push({
|
||||||
|
type: "person_identifier",
|
||||||
|
definition_id,
|
||||||
|
value: { content: value },
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
person.identifiers[updateIdentifierKey].value = {content: value}
|
person.identifiers[updateIdentifierKey].value = { content: value };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,15 +556,38 @@ function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WritePersonViolationKey = Extract<keyof WritePersonViolationMap, string>;
|
||||||
|
const validationErrors = ref<Partial<Record<WritePersonViolationKey, string[]>>>({});
|
||||||
|
|
||||||
|
|
||||||
|
function validationError(
|
||||||
|
property: WritePersonViolationKey,
|
||||||
|
): string[] {
|
||||||
|
return validationErrors.value[property] ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasValidationError(
|
||||||
|
property: WritePersonViolationKey,
|
||||||
|
): boolean {
|
||||||
|
return validationError(property).length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
function submitNewAddress(payload: { addressId: number }) {
|
function submitNewAddress(payload: { addressId: number }) {
|
||||||
// person.addressId = payload.addressId;
|
// person.addressId = payload.addressId;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postPerson(): Promise<void> {
|
async function postPerson(): Promise<void> {
|
||||||
console.log('postPerson');
|
console.log("postPerson");
|
||||||
|
try {
|
||||||
const createdPerson = await createPerson(person);
|
const createdPerson = await createPerson(person);
|
||||||
|
|
||||||
emit('onPersonCreated', {person: createdPerson});
|
emit("onPersonCreated", { person: createdPerson });
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (isValidationException<WritePersonViolationMap>(e)) {
|
||||||
|
console.log(e.byProperty);
|
||||||
|
validationErrors.value = e.byProperty;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -495,10 +613,16 @@ onMounted(() => {
|
|||||||
// if there is only one center, preselect it
|
// if there is only one center, preselect it
|
||||||
person.center = {
|
person.center = {
|
||||||
id: config.centers[0].id,
|
id: config.centers[0].id,
|
||||||
type: (config.centers[0]).type ?? "center",
|
type: config.centers[0].type ?? "center",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.was-validated-force {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user