mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-11-12 07:08:25 +00:00
Introduce edit functionality for PersonEdit and refactor OnTheFly components for improved modularity.
- Added support for editing a person entity in `PersonEdit.vue` with proper data initialization and API integration. - Refactored `OnTheFly.vue` to dynamically render components based on the action (`show`, `edit`, etc.). - Introduced `personToWritePerson` conversion logic for mapping `Person` to `PersonWrite` structure. - Enhanced template conditions and loading states for `PersonEdit`. - Updated API with `editPerson` function and adjusted related types for consistency.
This commit is contained in:
@@ -30,7 +30,7 @@
|
||||
</h3>
|
||||
</template>
|
||||
|
||||
<template #body v-if="type === 'person' && action !== 'addContact'">
|
||||
<template #body v-if="type === 'person' && action === 'show'">
|
||||
<on-the-fly-person
|
||||
:id="id"
|
||||
:type="type"
|
||||
@@ -45,6 +45,15 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #body v-else-if="type === 'person' && action === 'edit'">
|
||||
<PersonEdit
|
||||
:id="id"
|
||||
:action="'edit'"
|
||||
:query="''"
|
||||
ref="castEditPerson"
|
||||
></PersonEdit>
|
||||
</template>
|
||||
|
||||
<template #body v-else-if="type === 'thirdparty'">
|
||||
<on-the-fly-thirdparty
|
||||
:id="id"
|
||||
@@ -80,17 +89,21 @@
|
||||
|
||||
<template #footer>
|
||||
<a
|
||||
v-if="action === 'show'"
|
||||
:href="buildLocation(id, type)"
|
||||
:title="titleMessage"
|
||||
class="btn btn-show"
|
||||
>{{ buttonMessage }}
|
||||
</a>
|
||||
<a v-else class="btn btn-save" @click="saveAction">
|
||||
{{ trans(ACTION_SAVE) }}
|
||||
</a>
|
||||
</template>
|
||||
</modal>
|
||||
</teleport>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, defineEmits, defineProps } from "vue";
|
||||
import {ref, computed, defineEmits, defineProps, useTemplateRef} from "vue";
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import OnTheFlyCreate from "./Create.vue";
|
||||
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
||||
@@ -100,6 +113,7 @@ import {
|
||||
ACTION_SHOW,
|
||||
ACTION_EDIT,
|
||||
ACTION_CREATE,
|
||||
ACTION_SAVE,
|
||||
ONTHEFLY_CREATE_TITLE_DEFAULT,
|
||||
ONTHEFLY_CREATE_TITLE_PERSON,
|
||||
ONTHEFLY_CREATE_TITLE_THIRDPARTY,
|
||||
@@ -116,6 +130,7 @@ import {
|
||||
THIRDPARTY_ADDCONTACT,
|
||||
THIRDPARTY_ADDCONTACT_TITLE,
|
||||
} from "translator";
|
||||
import PersonEdit from "ChillPersonAssets/vuejs/_components/OnTheFly/PersonEdit.vue";
|
||||
|
||||
// Types
|
||||
type EntityType = "person" | "thirdparty";
|
||||
@@ -153,6 +168,9 @@ const emit = defineEmits<{
|
||||
(e: "saveFormOnTheFly", payload: { type: string | undefined; data: any }): void;
|
||||
}>();
|
||||
|
||||
type castEditPersonType = InstanceType<typeof PersonEdit>;
|
||||
const castEditPerson = useTemplateRef<castEditPersonType>('castEditPerson')
|
||||
|
||||
const modal = ref<{ showModal: boolean; modalDialogClass: string }>({
|
||||
showModal: false,
|
||||
modalDialogClass: "modal-dialog-scrollable modal-xl",
|
||||
@@ -287,6 +305,15 @@ function buildLocation(id: string | number | undefined, type: EntityType | undef
|
||||
return undefined;
|
||||
}
|
||||
|
||||
async function saveAction() {
|
||||
if (props.type === "person") {
|
||||
const person = await castEditPerson.value?.postPerson();
|
||||
if (null !== person) {
|
||||
emit("saveFormOnTheFly", {type: props.type, data: person})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openModal,
|
||||
closeModal,
|
||||
|
||||
@@ -20,15 +20,30 @@ import { StoredObject } from "ChillDocStoreAssets/types";
|
||||
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
|
||||
import { Calendar } from "../../../ChillCalendarBundle/Resources/public/types";
|
||||
|
||||
/**
|
||||
* An alternative name, as configured locally
|
||||
*/
|
||||
export interface AltName {
|
||||
labels: TranslatableString;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface AltNameWrite {
|
||||
export interface PersonAltNameWrite {
|
||||
key: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An altname for a person
|
||||
*/
|
||||
export interface PersonAltName {
|
||||
label: string;
|
||||
/**
|
||||
* will match a key in @link{AltName}
|
||||
*/
|
||||
key: string;
|
||||
}
|
||||
|
||||
export interface Person {
|
||||
id: number;
|
||||
type: "person";
|
||||
@@ -36,7 +51,7 @@ export interface Person {
|
||||
textAge: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
altNames: AltName[];
|
||||
altNames: PersonAltName[];
|
||||
suffixText: string;
|
||||
current_household_address: Address | null;
|
||||
birthdate: DateTime | null;
|
||||
@@ -54,6 +69,20 @@ export interface Person {
|
||||
* The person id as configured by the user
|
||||
*/
|
||||
personId: string;
|
||||
identifiers: PersonIdentifier[];
|
||||
}
|
||||
|
||||
export interface PersonIdentifier {
|
||||
id: number;
|
||||
type: "person_identifier";
|
||||
value: object;
|
||||
definition: PersonIdentifierDefinition;
|
||||
}
|
||||
|
||||
export interface PersonIdentifierDefinition {
|
||||
id: number;
|
||||
type: "person_identifier_definition";
|
||||
engine: string;
|
||||
}
|
||||
|
||||
export interface ResidentialAddress {
|
||||
@@ -77,8 +106,8 @@ export interface PersonWrite {
|
||||
type: "person";
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
altNames: AltNameWrite[];
|
||||
// address: number | null;
|
||||
altNames: PersonAltNameWrite[];
|
||||
addressId: number | null;
|
||||
birthdate: DateTimeWrite | null;
|
||||
deathdate: DateTimeWrite | null;
|
||||
phonenumber: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import { Center, Civility, Gender } from "ChillMainAssets/types";
|
||||
import {Center, Civility, Gender, SetCenter} from "ChillMainAssets/types";
|
||||
import {
|
||||
AltName,
|
||||
Person,
|
||||
Person, PersonIdentifier,
|
||||
PersonIdentifierWorker,
|
||||
PersonWrite,
|
||||
} from "ChillPersonAssets/types";
|
||||
@@ -21,6 +21,27 @@ export const getPerson = async (id: number): Promise<Person> => {
|
||||
});
|
||||
};
|
||||
|
||||
export const personToWritePerson = (person: Person): PersonWrite => {
|
||||
return {
|
||||
type: "person",
|
||||
firstName: person.firstName,
|
||||
lastName: person.lastName,
|
||||
altNames: person.altNames.map((altName) => ({key: altName.key, value: altName.label})),
|
||||
addressId: null,
|
||||
birthdate: null === person.birthdate ? null : {datetime: person.birthdate.datetime8601},
|
||||
deathdate: null === person.deathdate ? null : {datetime: person.deathdate.datetime8601},
|
||||
phonenumber: person.phonenumber,
|
||||
mobilenumber: person.mobilenumber,
|
||||
center: null === person.centers ? null : person.centers
|
||||
.map((center): SetCenter => ({id: center.id, type: "center"}))
|
||||
.find(() => true) || null,
|
||||
email: person.email,
|
||||
civility: null === person.civility ? null : {id: person.civility.id, type: "chill_main_civility"},
|
||||
gender: null === person.gender ? null : {id: person.gender.id, type: "chill_main_gender"},
|
||||
identifiers: person.identifiers.map((identifier: PersonIdentifier) => ({type: "person_identifier", definition_id: identifier.definition.id, value: identifier.value})),
|
||||
}
|
||||
}
|
||||
|
||||
export const getPersonAltNames = async (): Promise<AltName[]> =>
|
||||
fetch("/api/1.0/person/config/alt_names.json").then((response) => {
|
||||
if (response.ok) {
|
||||
@@ -85,3 +106,11 @@ export const createPerson = async (person: PersonWrite): Promise<Person> => {
|
||||
person,
|
||||
);
|
||||
};
|
||||
|
||||
export const editPerson = async (person: PersonWrite, personId: number): Promise<Person> => {
|
||||
return makeFetch<PersonWrite, Person, WritePersonViolationMap>(
|
||||
"PATCH",
|
||||
`/api/1.0/person/person/${personId}.json`,
|
||||
person,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,22 +48,4 @@ const props = withDefaults(defineProps<Props>(), {query: ""});
|
||||
|
||||
const person = ref<Person | null>(null);
|
||||
|
||||
function loadData(): void {
|
||||
if (props.id === undefined || props.id === null) {
|
||||
return;
|
||||
}
|
||||
const idNum = props.id;
|
||||
if (!Number.isFinite(idNum)) {
|
||||
return;
|
||||
}
|
||||
getPerson(idNum as number).then((p) => {
|
||||
person.value = p;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.action !== "create") {
|
||||
loadData();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="action === 'create' || (action === 'edit' && dataLoaded)">
|
||||
<div class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
@@ -70,48 +70,53 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div v-for="(a, i) in config.altNames" :key="a.key" class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:id="a.key"
|
||||
:name="'label_' + a.key"
|
||||
value=""
|
||||
@input="onAltNameInput($event, a.key)"
|
||||
/>
|
||||
<label :for="'label_' + a.key">{{ localizeString(a.labels) }}</label>
|
||||
<template v-if="action === 'create'">
|
||||
<div v-for="(a, i) in config.altNames" :key="a.key" class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:id="a.key"
|
||||
:name="'label_' + a.key"
|
||||
value=""
|
||||
@input="onAltNameInput($event, a.key)"
|
||||
/>
|
||||
<label :for="'label_' + a.key">{{ localizeString(a.labels) }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="worker in config.identifiers"
|
||||
:key="worker.definition_id"
|
||||
class="mb-3"
|
||||
>
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{'is-invalid': violations.hasViolationWithParameter('identifiers', 'definition_id', worker.definition_id.toString())}"
|
||||
type="text"
|
||||
:name="'worker_' + worker.definition_id"
|
||||
:placeholder="localizeString(worker.label)"
|
||||
@input="onIdentifierInput($event, worker.definition_id)"
|
||||
/>
|
||||
<label :for="'worker_' + worker.definition_id">{{
|
||||
localizeString(worker.label)
|
||||
}}</label>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitlesWithParameter('identifiers', 'definition_id', worker.definition_id.toString())"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</template>
|
||||
|
||||
<template v-if="action === 'create'">
|
||||
<div
|
||||
v-for="worker in config.identifiers"
|
||||
:key="worker.definition_id"
|
||||
class="mb-3"
|
||||
>
|
||||
<div class="input-group has-validation">
|
||||
<div class="form-floating">
|
||||
<input
|
||||
class="form-control form-control-lg"
|
||||
:class="{'is-invalid': violations.hasViolationWithParameter('identifiers', 'definition_id', worker.definition_id.toString())}"
|
||||
type="text"
|
||||
:name="'worker_' + worker.definition_id"
|
||||
:placeholder="localizeString(worker.label)"
|
||||
@input="onIdentifierInput($event, worker.definition_id)"
|
||||
/>
|
||||
<label :for="'worker_' + worker.definition_id">{{
|
||||
localizeString(worker.label)
|
||||
}}</label>
|
||||
</div>
|
||||
<div
|
||||
v-for="err in violations.violationTitlesWithParameter('identifiers', 'definition_id', worker.definition_id.toString())"
|
||||
class="invalid-feedback was-validated-force"
|
||||
>
|
||||
{{ err }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="input-group has-validation">
|
||||
@@ -322,11 +327,9 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning" v-if="errors.length">
|
||||
<ul>
|
||||
<li v-for="(e, i) in errors" :key="i">{{ e }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -335,13 +338,14 @@ import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
||||
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
|
||||
import {
|
||||
createPerson,
|
||||
createPerson, editPerson,
|
||||
getCentersForPersonCreation,
|
||||
getCivilities,
|
||||
getGenders,
|
||||
getPerson,
|
||||
getPersonAltNames,
|
||||
getPersonIdentifiers,
|
||||
personToWritePerson,
|
||||
WritePersonViolationMap,
|
||||
} from "../../_api/OnTheFly";
|
||||
import {
|
||||
@@ -366,15 +370,12 @@ import {
|
||||
Center,
|
||||
Civility,
|
||||
Gender,
|
||||
DateTimeWrite, ValidationExceptionInterface,
|
||||
} from "ChillMainAssets/types";
|
||||
import {
|
||||
AltName,
|
||||
Person,
|
||||
PersonWrite,
|
||||
PersonIdentifierWorker,
|
||||
type Suggestion,
|
||||
type EntitiesOrMe,
|
||||
} from "ChillPersonAssets/types";
|
||||
import {
|
||||
isValidationException,
|
||||
@@ -385,14 +386,12 @@ import {useViolationList} from "ChillMainAssets/vuejs/_composables/violationList
|
||||
|
||||
interface PersonEditComponentConfig {
|
||||
id?: number | null;
|
||||
type?: string;
|
||||
action: "edit" | "create";
|
||||
query: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<PersonEditComponentConfig>(), {
|
||||
id: null,
|
||||
type: "TODO",
|
||||
});
|
||||
|
||||
const emit =
|
||||
@@ -407,7 +406,7 @@ const person = reactive<PersonWrite>({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
altNames: [],
|
||||
// address: null,
|
||||
addressId: null,
|
||||
birthdate: null,
|
||||
deathdate: null,
|
||||
phonenumber: "",
|
||||
@@ -435,7 +434,6 @@ const config = reactive<{
|
||||
|
||||
const showCenters = ref(false);
|
||||
const showAddressFormValue = ref(false);
|
||||
const errors = ref<string[]>([]);
|
||||
|
||||
const addAddress = reactive({
|
||||
options: {
|
||||
@@ -560,9 +558,27 @@ const queryItems = computed(() => {
|
||||
.filter((word) => !lastNameWords.includes(word.toLowerCase()));
|
||||
});
|
||||
|
||||
const dataLoaded = ref<boolean>(false);
|
||||
|
||||
async function loadData() {
|
||||
if (props.id !== undefined && props.id !== null) {
|
||||
const person = await getPerson(props.id);
|
||||
const p = await getPerson(props.id);
|
||||
const w = personToWritePerson(p);
|
||||
person.firstName = w.firstName;
|
||||
person.lastName = w.lastName;
|
||||
person.altNames.push(...w.altNames)
|
||||
person.civility = w.civility;
|
||||
person.addressId = w.addressId;
|
||||
person.birthdate = w.birthdate;
|
||||
person.deathdate = w.deathdate;
|
||||
person.phonenumber = w.phonenumber;
|
||||
person.mobilenumber = w.mobilenumber;
|
||||
person.email = w.email;
|
||||
person.gender = w.gender;
|
||||
person.center = w.center;
|
||||
person.civility = w.civility;
|
||||
person.identifiers.push(...w.identifiers);
|
||||
dataLoaded.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,14 +628,22 @@ function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
|
||||
const violations = useViolationList<WritePersonViolationMap>();
|
||||
|
||||
function submitNewAddress(payload: { addressId: number }) {
|
||||
// person.addressId = payload.addressId;
|
||||
person.addressId = payload.addressId;
|
||||
}
|
||||
|
||||
async function postPerson(): Promise<void> {
|
||||
async function postPerson(): Promise<Person> {
|
||||
try {
|
||||
const createdPerson = await createPerson(person);
|
||||
if (props.action === 'create') {
|
||||
const createdPerson = await createPerson(person);
|
||||
emit("onPersonCreated", { person: createdPerson });
|
||||
|
||||
emit("onPersonCreated", { person: createdPerson });
|
||||
return createdPerson;
|
||||
} else if (props.id !== null) {
|
||||
const updatedPerson = await editPerson(person, props.id);
|
||||
emit("onPersonCreated", { person: updatedPerson });
|
||||
|
||||
return updatedPerson;
|
||||
}
|
||||
} catch (e: unknown) {
|
||||
if (isValidationException<WritePersonViolationMap>(e)) {
|
||||
violations.setValidationException(e);
|
||||
@@ -627,6 +651,7 @@ async function postPerson(): Promise<void> {
|
||||
toast.error(trans(PERSON_EDIT_ERROR_WHILE_SAVING));
|
||||
}
|
||||
}
|
||||
throw "impossible case might happen...";
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
Reference in New Issue
Block a user