mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-27 00:55:01 +00:00
Add support for person identifiers workflow: update PersonEdit
component, API methods, and modals for identifier handling during person creation. Adjust related types for improved consistency.
This commit is contained in:
@@ -64,6 +64,8 @@ const props = withDefaults(defineProps<CreateComponentConfig>(), {
|
|||||||
action: "create",
|
action: "create",
|
||||||
query: "",
|
query: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const type = ref<CreatableEntityType | null>(null);
|
const type = ref<CreatableEntityType | null>(null);
|
||||||
|
|
||||||
const radioType = computed<CreatableEntityType | null>({
|
const radioType = computed<CreatableEntityType | null>({
|
||||||
@@ -74,12 +76,14 @@ const radioType = computed<CreatableEntityType | null>({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type PersonEditComponent = InstanceType<typeof PersonEdit>;
|
||||||
|
|
||||||
type AnyComponentInstance =
|
type AnyComponentInstance =
|
||||||
| InstanceType<typeof OnTheFlyPerson>
|
| InstanceType<typeof OnTheFlyPerson>
|
||||||
| InstanceType<typeof OnTheFlyThirdparty>
|
| InstanceType<typeof OnTheFlyThirdparty>
|
||||||
| null;
|
| null;
|
||||||
|
|
||||||
const castPerson = ref<AnyComponentInstance>(null);
|
const castPerson = ref<PersonEditComponent>(null);
|
||||||
const castThirdparty = ref<AnyComponentInstance>(null);
|
const castThirdparty = ref<AnyComponentInstance>(null);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -100,31 +104,13 @@ const containsPerson = computed<boolean>(() => {
|
|||||||
return props.allowedTypes.includes("person");
|
return props.allowedTypes.includes("person");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Types for data structures coming from child components are not declared in TS yet.
|
function save(): void {
|
||||||
// We conservatively type them as any to preserve runtime behavior while enabling TS in this component.
|
castPerson.value.postPerson();
|
||||||
function castDataByType(): any {
|
|
||||||
switch (radioType.value) {
|
|
||||||
case "person":
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
return (castPerson.value as any)?.$data?.person;
|
|
||||||
case "thirdparty": {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
let data: any = (castThirdparty.value as any)?.$data?.thirdparty;
|
|
||||||
if (data && data.address !== undefined && data.address !== null) {
|
|
||||||
data.address = { id: data.address.address_id };
|
|
||||||
} else if (data) {
|
|
||||||
data.address = null;
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw Error("Invalid type of entity");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({save});
|
||||||
castDataByType,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
@@ -2,11 +2,25 @@
|
|||||||
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 {useTemplateRef} from "vue";
|
||||||
|
|
||||||
const emit = defineEmits<(e: "close") => void>();
|
const emit = defineEmits<(e: "close") => void>();
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
|
const create = useTemplateRef<CreateComponentType>("create");
|
||||||
|
|
||||||
|
function save(): void {
|
||||||
|
console.log('save from CreateModal');
|
||||||
|
create.value?.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({save})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -22,12 +36,16 @@ const modalDialogClass = { "modal-xl": true, "modal-scrollable": true };
|
|||||||
<template #body-head>
|
<template #body-head>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<Create
|
<Create
|
||||||
|
ref="create"
|
||||||
:allowedTypes="props.allowedTypes"
|
:allowedTypes="props.allowedTypes"
|
||||||
:action="props.action"
|
:action="props.action"
|
||||||
:query="props.query"
|
:query="props.query"
|
||||||
></Create>
|
></Create>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn btn-save" type="button" @click.prevent="save">{{ trans(SAVE) }}</button>
|
||||||
|
</template>
|
||||||
</modal>
|
</modal>
|
||||||
</teleport>
|
</teleport>
|
||||||
</template>
|
</template>
|
||||||
|
@@ -18,9 +18,14 @@ import { Calendar } from "../../../ChillCalendarBundle/Resources/public/types";
|
|||||||
import Person from "./vuejs/_components/OnTheFly/Person.vue";
|
import Person from "./vuejs/_components/OnTheFly/Person.vue";
|
||||||
|
|
||||||
export interface AltName {
|
export interface AltName {
|
||||||
label: TranslatableString;
|
labels: TranslatableString;
|
||||||
key: string;
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AltNameWrite {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
export interface Person {
|
export interface Person {
|
||||||
id: number;
|
id: number;
|
||||||
type: "person";
|
type: "person";
|
||||||
@@ -44,14 +49,20 @@ export interface Person {
|
|||||||
current_residential_addresses: Address[];
|
current_residential_addresses: Address[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PersonIdentifierWrite {
|
||||||
|
type: "person_identifier";
|
||||||
|
definition_id: number;
|
||||||
|
value: object;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Person representation to create or update a Person
|
* Person representation to create or update a Person
|
||||||
*/
|
*/
|
||||||
export interface PersonEdit {
|
export interface PersonWrite {
|
||||||
type: "person";
|
type: "person";
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
altNames: AltName[];
|
altNames: AltNameWrite[];
|
||||||
// address: number | null;
|
// address: number | null;
|
||||||
birthdate: DateTimeCreate | null;
|
birthdate: DateTimeCreate | null;
|
||||||
deathdate: DateTimeCreate | null;
|
deathdate: DateTimeCreate | null;
|
||||||
@@ -61,6 +72,7 @@ export interface PersonEdit {
|
|||||||
gender: SetGender | null;
|
gender: SetGender | null;
|
||||||
center: SetCenter | null;
|
center: SetCenter | null;
|
||||||
civility: SetCivility | null;
|
civility: SetCivility | null;
|
||||||
|
identifiers: PersonIdentifierWrite[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AccompanyingPeriod {
|
export interface AccompanyingPeriod {
|
||||||
@@ -408,6 +420,14 @@ export interface SearchOptions {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PersonIdentifierWorker {
|
||||||
|
type: "person_identifier_worker";
|
||||||
|
definition_id: number;
|
||||||
|
engine: string;
|
||||||
|
label: TranslatableString;
|
||||||
|
isActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class MakeFetchException extends Error {
|
export class MakeFetchException extends Error {
|
||||||
sta: number;
|
sta: number;
|
||||||
txt: string;
|
txt: string;
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
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 } from "ChillPersonAssets/types";
|
import {AltName, Person, PersonIdentifierWorker, PersonWrite} from "ChillPersonAssets/types";
|
||||||
|
import person from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* GET a person by id
|
* GET a person by id
|
||||||
@@ -31,3 +32,10 @@ export const getGenders = async (): Promise<Gender[]> =>
|
|||||||
|
|
||||||
export const getCentersForPersonCreation = async (): Promise<{showCenters: boolean; centers: Center[];}> =>
|
export const getCentersForPersonCreation = async (): Promise<{showCenters: boolean; centers: Center[];}> =>
|
||||||
makeFetch("GET", "/api/1.0/person/creation/authorized-centers", null);
|
makeFetch("GET", "/api/1.0/person/creation/authorized-centers", null);
|
||||||
|
|
||||||
|
export const getPersonIdentifiers = async (): Promise<PersonIdentifierWorker[]> =>
|
||||||
|
fetchResults("/api/1.0/person/identifiers/workers");
|
||||||
|
|
||||||
|
export const createPerson = async (person: PersonWrite): Promise<Person> => {
|
||||||
|
return makeFetch("POST", "/api/1.0/person/person.json", person);
|
||||||
|
}
|
||||||
|
@@ -35,7 +35,7 @@ const { person, isCut = false, addAge = true } = toRefs(props);
|
|||||||
|
|
||||||
const altNameLabel = computed(() => {
|
const altNameLabel = computed(() => {
|
||||||
if (!person.value.altNames) return "";
|
if (!person.value.altNames) return "";
|
||||||
return person.value.altNames.map((a: AltName) => a.label).join("");
|
return person.value.altNames.map((a: AltName) => a.labels).join("");
|
||||||
});
|
});
|
||||||
|
|
||||||
const altNameKey = computed(() => {
|
const altNameKey = computed(() => {
|
||||||
|
@@ -56,10 +56,26 @@
|
|||||||
<input
|
<input
|
||||||
class="form-control form-control-lg"
|
class="form-control form-control-lg"
|
||||||
:id="a.key"
|
:id="a.key"
|
||||||
:value="personAltNamesLabels[i]"
|
:name="'label_'+a.key"
|
||||||
@input="onAltNameInput"
|
value=""
|
||||||
|
@input="onAltNameInput($event, a.key)"
|
||||||
/>
|
/>
|
||||||
<label :for="a.key">{{ localizeString(a.labels) }}</label>
|
<label :for="'label_'+a.key">{{ localizeString(a.labels) }}</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-for="worker in config.identifiers"
|
||||||
|
:key="worker.definition_id"
|
||||||
|
class="form-floating mb-3"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="form-control form-control-lg"
|
||||||
|
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>
|
||||||
|
|
||||||
<div class="form-floating mb-3">
|
<div class="form-floating mb-3">
|
||||||
@@ -181,11 +197,12 @@ import { ref, reactive, computed, onMounted } from "vue";
|
|||||||
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
||||||
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
|
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
|
||||||
import {
|
import {
|
||||||
|
createPerson,
|
||||||
getCentersForPersonCreation,
|
getCentersForPersonCreation,
|
||||||
getCivilities,
|
getCivilities,
|
||||||
getGenders,
|
getGenders,
|
||||||
getPerson,
|
getPerson,
|
||||||
getPersonAltNames,
|
getPersonAltNames, getPersonIdentifiers,
|
||||||
} from "../../_api/OnTheFly";
|
} from "../../_api/OnTheFly";
|
||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
@@ -209,7 +226,14 @@ import {
|
|||||||
Gender,
|
Gender,
|
||||||
DateTimeCreate,
|
DateTimeCreate,
|
||||||
} from "ChillMainAssets/types";
|
} from "ChillMainAssets/types";
|
||||||
import {AltName, PersonEdit} from "ChillPersonAssets/types";
|
import {
|
||||||
|
AltName,
|
||||||
|
Person,
|
||||||
|
PersonWrite,
|
||||||
|
PersonIdentifierWorker,
|
||||||
|
type Suggestion,
|
||||||
|
type EntitiesOrMe
|
||||||
|
} from "ChillPersonAssets/types";
|
||||||
|
|
||||||
|
|
||||||
interface PersonEditComponentConfig {
|
interface PersonEditComponentConfig {
|
||||||
@@ -224,7 +248,13 @@ const props = withDefaults(defineProps<PersonEditComponentConfig>(), {
|
|||||||
type: "TODO",
|
type: "TODO",
|
||||||
});
|
});
|
||||||
|
|
||||||
const person = reactive<PersonEdit>({
|
const emit = defineEmits<{
|
||||||
|
(e: "onPersonCreated", payload: { person: Person }): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineExpose({postPerson});
|
||||||
|
|
||||||
|
const person = reactive<PersonWrite>({
|
||||||
type: "person",
|
type: "person",
|
||||||
firstName: "",
|
firstName: "",
|
||||||
lastName: "",
|
lastName: "",
|
||||||
@@ -238,6 +268,7 @@ const person = reactive<PersonEdit>({
|
|||||||
gender: null,
|
gender: null,
|
||||||
center: null,
|
center: null,
|
||||||
civility: null,
|
civility: null,
|
||||||
|
identifiers: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const config = reactive<{
|
const config = reactive<{
|
||||||
@@ -245,11 +276,13 @@ const config = reactive<{
|
|||||||
civilities: Civility[];
|
civilities: Civility[];
|
||||||
centers: Center[];
|
centers: Center[];
|
||||||
genders: Gender[];
|
genders: Gender[];
|
||||||
|
identifiers: PersonIdentifierWorker[];
|
||||||
}>({
|
}>({
|
||||||
altNames: [],
|
altNames: [],
|
||||||
civilities: [],
|
civilities: [],
|
||||||
centers: [],
|
centers: [],
|
||||||
genders: [],
|
genders: [],
|
||||||
|
identifiers: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const showCenters = ref(false);
|
const showCenters = ref(false);
|
||||||
@@ -350,9 +383,9 @@ const center = computed({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const personAltNamesLabels = computed(() =>
|
/**
|
||||||
person.altNames.map((a) => (a ? a.label : "")),
|
* 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;
|
||||||
|
|
||||||
@@ -387,21 +420,30 @@ function checkErrors() {
|
|||||||
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);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
function onAltNameInput(event: Event) {
|
function onAltNameInput(event: Event, key: string): void {
|
||||||
const target = event.target as HTMLInputElement;
|
const target = event.target as HTMLInputElement;
|
||||||
const key = target.id;
|
const value = target.value;
|
||||||
const label = target.value;
|
const updateAltNamesKey = person.altNames.findIndex((a) => a.key === key);
|
||||||
const updateAltNames = person.altNames.filter((a) => a.key !== key);
|
if (-1 === updateAltNamesKey) {
|
||||||
updateAltNames.push({ key, label });
|
person.altNames.push({key, value})
|
||||||
person.altNames = updateAltNames;
|
} else {
|
||||||
|
person.altNames[updateAltNamesKey].value = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
function onIdentifierInput(event: Event, definition_id: number): void {
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
const value = target.value;
|
||||||
|
const updateIdentifierKey = person.identifiers.findIndex((w) => w.definition_id === definition_id);
|
||||||
|
if (-1 === updateIdentifierKey) {
|
||||||
|
person.identifiers.push({type: "person_identifier", definition_id, value: { content: value }})
|
||||||
|
} else {
|
||||||
|
person.identifiers[updateIdentifierKey].value = {content: value}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
|
function addQueryItem(field: "lastName" | "firstName", queryItem: string) {
|
||||||
switch (field) {
|
switch (field) {
|
||||||
@@ -422,6 +464,13 @@ function submitNewAddress(payload: { addressId: number }) {
|
|||||||
// person.addressId = payload.addressId;
|
// person.addressId = payload.addressId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function postPerson(): Promise<void> {
|
||||||
|
console.log('postPerson');
|
||||||
|
const createdPerson = await createPerson(person);
|
||||||
|
|
||||||
|
emit('onPersonCreated', {person: createdPerson});
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getPersonAltNames().then((altNames) => {
|
getPersonAltNames().then((altNames) => {
|
||||||
config.altNames = altNames;
|
config.altNames = altNames;
|
||||||
@@ -432,6 +481,9 @@ onMounted(() => {
|
|||||||
getGenders().then((genders) => {
|
getGenders().then((genders) => {
|
||||||
config.genders = genders;
|
config.genders = genders;
|
||||||
});
|
});
|
||||||
|
getPersonIdentifiers().then((identifiers) => {
|
||||||
|
config.identifiers = identifiers;
|
||||||
|
});
|
||||||
if (props.action !== "create") {
|
if (props.action !== "create") {
|
||||||
loadData();
|
loadData();
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user