mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-28 09:34:59 +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">
|
||||||
<input
|
<div class="input-group has-validation">
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
id="lastname"
|
<input
|
||||||
v-model="lastName"
|
class="form-control form-control-lg"
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
|
:class="{ 'is-invalid': hasValidationError('lastName') }"
|
||||||
/>
|
id="lastname"
|
||||||
<label for="lastname">{{ trans(PERSON_MESSAGES_PERSON_LASTNAME) }}</label>
|
v-model="lastName"
|
||||||
|
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
|
||||||
|
/>
|
||||||
|
<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,16 +35,27 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
<input
|
<div class="input-group has-validation">
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
id="firstname"
|
<input
|
||||||
v-model="firstName"
|
class="form-control form-control-lg"
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
|
:class="{ 'is-invalid': hasValidationError('firstName') }"
|
||||||
/>
|
id="firstname"
|
||||||
<label for="firstname">{{
|
v-model="firstName"
|
||||||
trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
|
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
|
||||||
}}</label>
|
/>
|
||||||
|
<label for="firstname">{{
|
||||||
|
trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
|
||||||
|
}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('firstName')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="queryItems">
|
<div v-if="queryItems">
|
||||||
@@ -46,116 +70,190 @@
|
|||||||
</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
|
||||||
>
|
class="form-control form-control-lg"
|
||||||
<input
|
:id="a.key"
|
||||||
class="form-control form-control-lg"
|
:name="'label_' + a.key"
|
||||||
:id="a.key"
|
value=""
|
||||||
:name="'label_'+a.key"
|
@input="onAltNameInput($event, a.key)"
|
||||||
value=""
|
/>
|
||||||
@input="onAltNameInput($event, a.key)"
|
<label :for="'label_' + a.key">{{ localizeString(a.labels) }}</label>
|
||||||
/>
|
</div>
|
||||||
<label :for="'label_'+a.key">{{ localizeString(a.labels) }}</label>
|
</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"
|
||||||
>
|
>
|
||||||
<input
|
<div class="input-group has-validation">
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
type="text"
|
<input
|
||||||
:name="'worker_'+worker.definition_id"
|
class="form-control form-control-lg"
|
||||||
:placeholder="localizeString(worker.label)"
|
type="text"
|
||||||
@input="onIdentifierInput($event, worker.definition_id)"
|
:name="'worker_' + worker.definition_id"
|
||||||
/>
|
:placeholder="localizeString(worker.label)"
|
||||||
<label :for="'worker_'+worker.definition_id">{{ localizeString(worker.label) }}</label>
|
@input="onIdentifierInput($event, worker.definition_id)"
|
||||||
|
/>
|
||||||
|
<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">
|
||||||
<option selected disabled>
|
<div class="form-floating">
|
||||||
{{ trans(PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER) }}
|
<select
|
||||||
</option>
|
class="form-select form-select-lg"
|
||||||
<option v-for="g in config.genders" :value="g.id" :key="g.id">
|
:class="{ 'is-invalid': hasValidationError('gender') }"
|
||||||
{{ g.label }}
|
id="gender"
|
||||||
</option>
|
v-model="gender"
|
||||||
</select>
|
>
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_GENDER_TITLE) }}</label>
|
<option selected disabled>
|
||||||
|
{{ trans(PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER) }}
|
||||||
|
</option>
|
||||||
|
<option v-for="g in config.genders" :value="g.id" :key="g.id">
|
||||||
|
{{ g.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<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
|
||||||
<select class="form-select form-select-lg" id="center" v-model="center">
|
class="form-select form-select-lg"
|
||||||
<option selected disabled>
|
id="center"
|
||||||
{{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
|
v-model="center"
|
||||||
</option>
|
>
|
||||||
<option v-for="c in config.centers" :value="c" :key="c.id">
|
<option selected disabled>
|
||||||
{{ c.name }}
|
{{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
<option v-for="c in config.centers" :value="c" :key="c.id">
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
|
{{ c.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<label>{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('center')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="mb-3">
|
||||||
<select
|
<div class="input-group has-validation">
|
||||||
class="form-select form-select-lg"
|
<div class="form-floating">
|
||||||
id="civility"
|
<select
|
||||||
v-model="civility"
|
class="form-select form-select-lg"
|
||||||
>
|
id="civility"
|
||||||
<option selected disabled>
|
v-model="civility"
|
||||||
{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER) }}
|
>
|
||||||
</option>
|
<option selected disabled>
|
||||||
<option v-for="c in config.civilities" :value="c.id" :key="c.id">
|
{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER) }}
|
||||||
{{ localizeString(c.name) }}
|
</option>
|
||||||
</option>
|
<option v-for="c in config.civilities" :value="c.id" :key="c.id">
|
||||||
</select>
|
{{ localizeString(c.name) }}
|
||||||
<label>{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
|
</option>
|
||||||
|
</select>
|
||||||
|
<label>{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('civility')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
<span class="input-group-text" id="phonenumber">
|
<div class="input-group has-validation">
|
||||||
<i class="fa fa-fw fa-phone"></i>
|
<span class="input-group-text" id="phonenumber">
|
||||||
</span>
|
<i class="fa fa-fw fa-phone"></i>
|
||||||
<input
|
</span>
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
v-model="phonenumber"
|
<input
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
|
class="form-control form-control-lg"
|
||||||
:aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
|
v-model="phonenumber"
|
||||||
aria-describedby="phonenumber"
|
:placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
|
||||||
/>
|
:aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
|
||||||
|
aria-describedby="phonenumber"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('phonenumber')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
<span class="input-group-text" id="mobilenumber">
|
<div class="input-group has-validation">
|
||||||
<i class="fa fa-fw fa-mobile"></i>
|
<span class="input-group-text" id="mobilenumber">
|
||||||
</span>
|
<i class="fa fa-fw fa-mobile"></i>
|
||||||
<input
|
</span>
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
v-model="mobilenumber"
|
<input
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
|
class="form-control form-control-lg"
|
||||||
:aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
|
v-model="mobilenumber"
|
||||||
aria-describedby="mobilenumber"
|
:placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
|
||||||
/>
|
:aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
|
||||||
|
aria-describedby="mobilenumber"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('mobilenumber')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group mb-3">
|
<div class="mb-3">
|
||||||
<span class="input-group-text" id="email">
|
<div class="input-group has-validation">
|
||||||
<i class="fa fa-fw fa-at"></i>
|
<span class="input-group-text" id="email">
|
||||||
</span>
|
<i class="fa fa-fw fa-at"></i>
|
||||||
<input
|
</span>
|
||||||
class="form-control form-control-lg"
|
<div class="form-floating">
|
||||||
v-model="email"
|
<input
|
||||||
:placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)"
|
class="form-control form-control-lg"
|
||||||
:aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)"
|
v-model="email"
|
||||||
aria-describedby="email"
|
:placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)"
|
||||||
/>
|
:aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)"
|
||||||
|
aria-describedby="email"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="err in validationError('email')"
|
||||||
|
class="invalid-feedback was-validated-force"
|
||||||
|
>
|
||||||
|
{{ err }}
|
||||||
|
</div>
|
||||||
|
</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">
|
||||||
@@ -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,39 +490,23 @@ 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 || "")
|
||||||
const lastNameWords = (person.lastName || "").trim().toLowerCase().split(" ");
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.split(" ");
|
||||||
|
const lastNameWords = (person.lastName || "").trim().toLowerCase().split(" ");
|
||||||
|
|
||||||
return words
|
return words
|
||||||
.filter((word) => !firstNameWords.includes(word.toLowerCase()))
|
.filter((word) => !firstNameWords.includes(word.toLowerCase()))
|
||||||
.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");
|
||||||
const createdPerson = await createPerson(person);
|
try {
|
||||||
|
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