Ajout de commentaires supplémentaires aux motifs

This commit is contained in:
2025-07-11 14:06:40 +00:00
parent 837089ff5d
commit 63d0a52ea1
392 changed files with 35466 additions and 24054 deletions

View File

@@ -1,53 +1,44 @@
<template>
<ul class="list-suggest add-items" v-if="suggested.length > 0">
<li v-for="(r, i) in suggested" @click="setReferrer(r)" :key="i">
<span>{{ r.text }}</span>
</li>
</ul>
<ul class="list-suggest add-items" v-if="suggested.length > 0">
<li v-for="(r, i) in suggested" @click="setReferrer(r)" :key="i">
<span>{{ r.text }}</span>
</li>
</ul>
</template>
<script>
<script setup>
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods.ts";
import { defineProps, defineEmits } from "vue";
export default {
name: "SetReferrer",
props: {
suggested: {
type: Array,
required: false,
//default: [],
},
periodId: {
type: Number,
required: true,
},
},
data() {
return {
/*suggested: [
{id: 5, text: 'Robert'}, {id: 8, text: 'Monique'},
]*/
};
},
emits: ["referrerSet"],
methods: {
setReferrer: function (ref) {
const url = `/api/1.0/person/accompanying-course/${this.periodId}.json`;
const body = {
type: "accompanying_period",
user: { id: ref.id, type: ref.type },
};
const props = defineProps({
suggested: {
type: Array,
required: false,
// default: [],
},
periodId: {
type: Number,
required: true,
},
});
return makeFetch("PATCH", url, body)
.then(() => {
this.$emit("referrerSet", ref);
})
.catch((error) => {
throw error;
});
},
},
};
const emit = defineEmits(["referrerSet"]);
function setReferrer(ref) {
const url = `/api/1.0/person/accompanying-course/${props.periodId}.json`;
const body = {
type: "accompanying_period",
user: { id: ref.id, type: ref.type },
};
return makeFetch("PATCH", url, body)
.then(() => {
emit("referrerSet", ref);
})
.catch((error) => {
throw error;
});
}
</script>
<style scoped></style>

View File

@@ -1,51 +1,45 @@
<template>
<div class="container">
<div class="item-bloc">
<div class="item-row">
<h2 class="badge-title">
<span class="title_label"></span>
<span class="title_action">
<span class="chill-entity entity-social-action">
<span class="badge bg-light text-dark">
{{ acpw?.socialAction?.title.fr }}
</span>
</span>
<div class="container">
<div class="item-bloc">
<div class="item-row">
<h2 class="badge-title">
<span class="title_label"></span>
<span class="title_action">
<span class="chill-entity entity-social-action">
<span class="badge bg-light text-dark">
{{ acpw?.socialAction?.title.fr }}
</span>
</span>
<ul class="small_in_title columns mt-1">
<li>
<span class="item-key">
{{
trans(
ACCOMPANYING_COURSE_WORK_START_DATE,
)
}}
:
</span>
<b>{{ formatDate(acpw.startDate) }}</b>
</li>
<ul class="small_in_title columns mt-1">
<li>
<span class="item-key">
{{ trans(ACCOMPANYING_COURSE_WORK_START_DATE) }}
:
</span>
<b>{{ formatDate(acpw.startDate) }}</b>
</li>
<li v-if="acpw.endDate">
<span class="item-key">
{{
trans(ACCOMPANYING_COURSE_WORK_END_DATE)
}}
:
</span>
<b>{{ formatDate(acpw.endDate) }}</b>
</li>
</ul>
</span>
</h2>
</div>
</div>
<li v-if="acpw.endDate">
<span class="item-key">
{{ trans(ACCOMPANYING_COURSE_WORK_END_DATE) }}
:
</span>
<b>{{ formatDate(acpw.endDate) }}</b>
</li>
</ul>
</span>
</h2>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {
ACCOMPANYING_COURSE_WORK_END_DATE,
ACCOMPANYING_COURSE_WORK_START_DATE,
trans,
ACCOMPANYING_COURSE_WORK_END_DATE,
ACCOMPANYING_COURSE_WORK_START_DATE,
trans,
} from "translator";
import { ISOToDate } from "ChillMainAssets/chill/js/date";
import { DateTime } from "ChillMainAssets/types";
@@ -54,15 +48,15 @@ import { AccompanyingPeriodWork } from "../../../types";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps<{ acpw: AccompanyingPeriodWork }>();
const formatDate = (dateObject: DateTime) => {
if (dateObject) {
const parsedDate = ISOToDate(dateObject.datetime);
if (parsedDate) {
return new Intl.DateTimeFormat("default", {
dateStyle: "short",
}).format(parsedDate);
} else {
return "";
}
if (dateObject) {
const parsedDate = ISOToDate(dateObject.datetime);
if (parsedDate) {
return new Intl.DateTimeFormat("default", {
dateStyle: "short",
}).format(parsedDate);
} else {
return "";
}
}
};
</script>

View File

@@ -1,24 +1,24 @@
<template>
<div class="results">
<div
v-for="acpw in accompanyingPeriodWorks"
:key="acpw.id"
class="list-item"
>
<label class="acpw-item">
<div>
<input
type="radio"
:value="acpw"
v-model="selectedAcpw"
name="item"
/>
</div>
<accompanying-period-work-item :acpw="acpw" />
</label>
<div class="results">
<div
v-for="acpw in accompanyingPeriodWorks"
:key="acpw.id"
class="list-item"
>
<label class="acpw-item">
<div>
<input
type="radio"
:value="acpw"
v-model="selectedAcpw"
name="item"
/>
</div>
<accompanying-period-work-item :acpw="acpw" />
</label>
</div>
</div>
</template>
<script setup lang="ts">
@@ -28,7 +28,7 @@ import { defineProps, ref, watch } from "vue";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const props = defineProps<{
accompanyingPeriodWorks: AccompanyingPeriodWork[];
accompanyingPeriodWorks: AccompanyingPeriodWork[];
}>();
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
@@ -36,12 +36,12 @@ const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
const emit = defineEmits();
watch(selectedAcpw, (newValue) => {
emit("update:selectedAcpw", newValue);
emit("update:selectedAcpw", newValue);
});
</script>
<style>
.acpw-item {
display: flex;
display: flex;
}
</style>

View File

@@ -1,62 +1,52 @@
<template>
<div>
<div class="row justify-content-end">
<div class="col-md-6 col-sm-10" v-if="selectedAcpw">
<ul class="list-suggest remove-items">
<li>
<span
@click="selectedAcpw = null"
class="chill-denomination"
>{{ selectedAcpw?.socialAction?.title.fr }}</span
>
</li>
</ul>
</div>
</div>
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-create mt-3" @click="openModal">
{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}
</a>
</li>
<div>
<div class="row justify-content-end">
<div class="col-md-6 col-sm-10" v-if="selectedAcpw">
<ul class="list-suggest remove-items">
<li>
<span @click="selectedAcpw = null" class="chill-denomination">{{
selectedAcpw?.socialAction?.title.fr
}}</span>
</li>
</ul>
<teleport to="body">
<modal
v-if="showModal"
@close="closeModal"
modal-dialog-class="modal-dialog-scrollable modal-xl"
>
<template #header>
<h3>
{{
trans(
ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
)
}}
</h3>
</template>
<template #body>
<accompanying-period-work-list
:accompanying-period-works="accompanyingPeriodWorks"
v-model:selectedAcpw="selectedAcpw"
/>
</template>
<template #footer>
<button
type="button"
class="btn btn-save"
@click="confirmSelection"
>
{{ trans(CONFIRM) }}
</button>
</template>
</modal>
</teleport>
</div>
</div>
<ul class="record_actions">
<li>
<a class="btn btn-sm btn-create mt-3" @click="openModal">
{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}
</a>
</li>
</ul>
<teleport to="body">
<modal
v-if="showModal"
@close="closeModal"
modal-dialog-class="modal-dialog-scrollable modal-xl"
>
<template #header>
<h3>
{{ trans(ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK) }}
</h3>
</template>
<template #body>
<accompanying-period-work-list
:accompanying-period-works="accompanyingPeriodWorks"
v-model:selectedAcpw="selectedAcpw"
/>
</template>
<template #footer>
<button type="button" class="btn btn-save" @click="confirmSelection">
{{ trans(CONFIRM) }}
</button>
</template>
</modal>
</teleport>
</div>
</template>
<script setup lang="ts">
@@ -65,14 +55,14 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import AccompanyingPeriodWorkList from "./AccompanyingPeriodWorkList.vue";
import { AccompanyingPeriodWork } from "../../../types";
import {
ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
CONFIRM,
trans,
ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
CONFIRM,
trans,
} from "translator";
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
interface AccompanyingPeriodWorkSelectorModalProps {
accompanyingPeriodId: number;
accompanyingPeriodId: number;
}
const selectedAcpw = ref<AccompanyingPeriodWork | null>(null);
@@ -81,25 +71,25 @@ const accompanyingPeriodWorks = ref<AccompanyingPeriodWork[]>([]);
const props = defineProps<AccompanyingPeriodWorkSelectorModalProps>();
const emit = defineEmits<{
pickWork: [payload: { work: AccompanyingPeriodWork | null }];
pickWork: [payload: { work: AccompanyingPeriodWork | null }];
}>();
onMounted(() => {
if (props.accompanyingPeriodId) {
getAccompanyingPeriodWorks(props.accompanyingPeriodId);
} else {
console.error("No accompanyingperiod id was given");
}
if (props.accompanyingPeriodId) {
getAccompanyingPeriodWorks(props.accompanyingPeriodId);
} else {
console.error("No accompanyingperiod id was given");
}
});
const getAccompanyingPeriodWorks = async (periodId: number) => {
const url = `/api/1.0/person/accompanying-course/${periodId}/works.json`;
const url = `/api/1.0/person/accompanying-course/${periodId}/works.json`;
try {
accompanyingPeriodWorks.value = await fetchResults(url);
} catch (error) {
console.log(error);
}
/* makeFetch<number, AccompanyingPeriodWork[]>("GET", url)
try {
accompanyingPeriodWorks.value = await fetchResults(url);
} catch (error) {
console.log(error);
}
/* makeFetch<number, AccompanyingPeriodWork[]>("GET", url)
.then((response) => {
accompanyingPeriodWorks.value = response;
})
@@ -111,7 +101,7 @@ const getAccompanyingPeriodWorks = async (periodId: number) => {
const openModal = () => (showModal.value = true);
const closeModal = () => (showModal.value = false);
const confirmSelection = () => {
emit("pickWork", { work: selectedAcpw.value });
closeModal();
emit("pickWork", { work: selectedAcpw.value });
closeModal();
};
</script>

View File

@@ -1,517 +1,489 @@
<template>
<a
class="btn"
:class="getClassButton"
:title="$t(buttonTitle || '')"
@click="openModal"
<a
class="btn"
:class="getClassButton"
:title="buttonTitle"
@click="openModal"
>
<span v-if="displayTextButton">{{ buttonTitle }}</span>
</a>
<teleport to="body">
<modal
v-if="showModal"
@close="closeModal"
:modal-dialog-class="modalDialogClass"
:show="showModal"
:hide-footer="false"
>
<span v-if="displayTextButton">{{ $t(buttonTitle || "") }}</span>
</a>
<template #header>
<h3 class="modal-title">
{{ modalTitle }}
</h3>
</template>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
<template #body-head>
<div class="modal-body">
<div class="search">
<label class="col-form-label" style="float: right">
{{
trans(ADD_PERSONS_SUGGESTED_COUNTER, {
count: suggestedCounter,
})
}}
</label>
<input
id="search-persons"
name="query"
v-model="query"
:placeholder="trans(ADD_PERSONS_SEARCH_SOME_PERSONS)"
ref="searchRef"
/>
<i class="fa fa-search fa-lg" />
<i
class="fa fa-times"
v-if="queryLength >= 3"
@click="resetSuggestion"
/>
</div>
</div>
<div class="modal-body" v-if="checkUniq === 'checkbox'">
<div class="count">
<span>
<a v-if="suggestedCounter > 2" @click="selectAll">
{{ trans(ACTION_CHECK_ALL) }}
</a>
<a v-if="selectedCounter > 0" @click="resetSelection">
<i v-if="suggestedCounter > 2"> </i>
{{ trans(ACTION_RESET) }}
</a>
</span>
<span v-if="selectedCounter > 0">
{{
trans(ADD_PERSONS_SELECTED_COUNTER, {
count: selectedCounter,
})
}}
</span>
</div>
</div>
</template>
<template #body>
<div class="results">
<person-suggestion
v-for="item in selectedAndSuggested.slice().reverse()"
:key="itemKey(item)"
:item="item"
:search="search"
:type="checkUniq"
@save-form-on-the-fly="saveFormOnTheFly"
@new-prior-suggestion="newPriorSuggestion"
@update-selected="updateSelected"
/>
<div class="create-button">
<on-the-fly
v-if="
queryLength >= 3 &&
(options.type.includes('person') ||
options.type.includes('thirdparty'))
"
:button-text="trans(ONTHEFLY_CREATE_BUTTON, { q: query })"
:allowed-types="options.type"
:query="query"
action="create"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-create"
@click.prevent="
() => {
$emit('addNewPersons', {
selected: selectedComputed,
});
query = '';
closeModal();
}
"
>
<template #header>
<h3 class="modal-title">
{{ $t(modalTitle) }}
</h3>
</template>
<template #body-head>
<div class="modal-body">
<div class="search">
<label class="col-form-label" style="float: right">
{{
$tc(
"add_persons.suggested_counter",
suggestedCounter,
)
}}
</label>
<input
id="search-persons"
name="query"
v-model="query"
:placeholder="$t('add_persons.search_some_persons')"
ref="search"
/>
<i class="fa fa-search fa-lg" />
<i
class="fa fa-times"
v-if="queryLength >= 3"
@click="resetSuggestion"
/>
</div>
</div>
<div class="modal-body" v-if="checkUniq === 'checkbox'">
<div class="count">
<span>
<a v-if="suggestedCounter > 2" @click="selectAll">
{{ $t("action.check_all") }}
</a>
<a
v-if="selectedCounter > 0"
@click="resetSelection"
>
<i v-if="suggestedCounter > 2"> </i>
{{ $t("action.reset") }}
</a>
</span>
<span v-if="selectedCounter > 0">
{{
$tc(
"add_persons.selected_counter",
selectedCounter,
)
}}
</span>
</div>
</div>
</template>
<template #body>
<div class="results">
<person-suggestion
v-for="item in this.selectedAndSuggested
.slice()
.reverse()"
:key="itemKey(item)"
:item="item"
:search="search"
:type="checkUniq"
@save-form-on-the-fly="saveFormOnTheFly"
@new-prior-suggestion="newPriorSuggestion"
@update-selected="updateSelected"
/>
<div class="create-button">
<on-the-fly
v-if="
queryLength >= 3 &&
(options.type.includes('person') ||
options.type.includes('thirdparty'))
"
:button-text="
$t('onthefly.create.button', { q: query })
"
:allowed-types="options.type"
:query="query"
action="create"
@save-form-on-the-fly="saveFormOnTheFly"
ref="onTheFly"
/>
</div>
</div>
</template>
<template #footer>
<button
class="btn btn-create"
@click.prevent="$emit('addNewPersons', { selected, modal })"
>
{{ $t("action.add") }}
</button>
</template>
</modal>
</teleport>
{{ trans(ACTION_ADD) }}
</button>
</template>
</modal>
</teleport>
</template>
<script>
import Modal from "ChillMainAssets/vuejs/_components/Modal";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import PersonSuggestion from "./AddPersons/PersonSuggestion";
<script setup lang="ts">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { ref, reactive, computed, nextTick, watch, shallowRef } from "vue";
import PersonSuggestion from "./AddPersons/PersonSuggestion.vue";
import { searchEntities } from "ChillPersonAssets/vuejs/_api/AddPersons";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
export default {
name: "AddPersons",
components: {
Modal,
PersonSuggestion,
OnTheFly,
},
props: ["buttonTitle", "modalTitle", "options"],
emits: ["addNewPersons"],
data() {
return {
modal: {
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
},
search: {
query: "",
previousQuery: "",
currentSearchQueryController: null,
suggested: [],
selected: [],
priorSuggestion: {},
},
};
},
computed: {
query: {
set(query) {
return this.setQuery(query);
},
get() {
return this.search.query;
},
},
queryLength() {
return this.search.query.length;
},
suggested() {
return this.search.suggested;
},
suggestedCounter() {
return this.search.suggested.length;
},
selected() {
return this.search.selected;
},
selectedCounter() {
return this.search.selected.length;
},
selectedAndSuggested() {
this.addPriorSuggestion();
const uniqBy = (a, key) => [
...new Map(a.map((x) => [key(x), x])).values(),
];
let union = [
...new Set([
...this.suggested.slice().reverse(),
...this.selected.slice().reverse(),
]),
];
return uniqBy(union, (k) => k.key);
},
getClassButton() {
let size =
typeof this.options.button !== "undefined" &&
typeof this.options.button.size !== "undefined"
? this.options.button.size
: "";
let type =
typeof this.options.button !== "undefined" &&
typeof this.options.button.type !== "undefined"
? this.options.button.type
: "btn-create";
return size ? size + " " + type : type;
},
displayTextButton() {
return typeof this.options.button !== "undefined" &&
typeof this.options.button.display !== "undefined"
? this.options.button.display
: true;
},
checkUniq() {
if (this.options.uniq === true) {
return "radio";
}
return "checkbox";
},
priorSuggestion() {
return this.search.priorSuggestion;
},
hasPriorSuggestion() {
return this.search.priorSuggestion.key ? true : false;
},
},
methods: {
openModal() {
this.modal.showModal = true;
this.$nextTick(function () {
this.$refs.search.focus();
});
},
setQuery(query) {
this.search.query = query;
import {
trans,
ADD_PERSONS_SUGGESTED_COUNTER,
ADD_PERSONS_SEARCH_SOME_PERSONS,
ADD_PERSONS_SELECTED_COUNTER,
ONTHEFLY_CREATE_BUTTON,
ACTION_CHECK_ALL,
ACTION_RESET,
ACTION_ADD,
} from "translator";
import {
Suggestion,
Search,
AddPersonResult as OriginalResult,
SearchOptions,
} from "ChillPersonAssets/types";
setTimeout(
function () {
if (query === "") {
this.loadSuggestions([]);
return;
}
if (query === this.search.query) {
if (this.search.currentSearchQueryController !== null) {
this.search.currentSearchQueryController.abort();
}
this.search.currentSearchQueryController =
new AbortController();
searchEntities(
{ query, options: this.options },
this.search.currentSearchQueryController.signal,
)
.then(
(suggested) =>
new Promise((resolve) => {
this.loadSuggestions(suggested.results);
resolve();
}),
)
.catch((error) => {
if (error instanceof DOMException) {
if (error.name === "AbortError") {
console.log(
"request aborted due to user continue typing",
);
return;
}
}
// Extend Result type to include optional addressId
type Result = OriginalResult & { addressId?: number };
throw error;
});
}
}.bind(this),
query.length > 3 ? 300 : 700,
const props = defineProps({
suggested: { type: Array as () => Suggestion[], default: () => [] },
selected: { type: Array as () => Suggestion[], default: () => [] },
buttonTitle: { type: String, required: true },
modalTitle: { type: String, required: true },
options: { type: Object as () => SearchOptions, required: true },
});
defineEmits(["addNewPersons"]);
const showModal = ref(false);
const modalDialogClass = ref("modal-dialog-scrollable modal-xl");
const modal = shallowRef({
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
});
const search = reactive({
query: "" as string,
previousQuery: "" as string,
currentSearchQueryController: null as AbortController | null,
suggested: props.suggested as Suggestion[],
selected: props.selected as Suggestion[],
priorSuggestion: {} as Partial<Suggestion>,
});
const searchRef = ref<HTMLInputElement | null>(null);
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
const query = computed({
get: () => search.query,
set: (val) => setQuery(val),
});
const queryLength = computed(() => search.query.length);
const suggestedCounter = computed(() => search.suggested.length);
const selectedComputed = computed(() => search.selected);
const selectedCounter = computed(() => search.selected.length);
const getClassButton = computed(() => {
let size = props.options?.button?.size ?? "";
let type = props.options?.button?.type ?? "btn-create";
return size ? size + " " + type : type;
});
const displayTextButton = computed(() =>
props.options?.button?.display !== undefined
? props.options.button.display
: true,
);
const checkUniq = computed(() =>
props.options.uniq === true ? "radio" : "checkbox",
);
const priorSuggestion = computed(() => search.priorSuggestion);
const hasPriorSuggestion = computed(() => !!search.priorSuggestion.key);
const itemKey = (item: Suggestion) => item.result.type + item.result.id;
function addPriorSuggestion() {
if (hasPriorSuggestion.value) {
// Type assertion is safe here due to the checks above
search.suggested.unshift(priorSuggestion.value as Suggestion);
search.selected.unshift(priorSuggestion.value as Suggestion);
newPriorSuggestion(null);
}
}
const selectedAndSuggested = computed(() => {
addPriorSuggestion();
const uniqBy = (a: Suggestion[], key: (item: Suggestion) => string) => [
...new Map(a.map((x) => [key(x), x])).values(),
];
let union = [
...new Set([
...search.suggested.slice().reverse(),
...search.selected.slice().reverse(),
]),
];
return uniqBy(union, (k: Suggestion) => k.key);
});
function openModal() {
showModal.value = true;
nextTick(() => {
if (searchRef.value) searchRef.value.focus();
});
}
function closeModal() {
showModal.value = false;
}
function setQuery(q: string) {
search.query = q;
// Clear previous search if any
if (search.currentSearchQueryController) {
search.currentSearchQueryController.abort();
search.currentSearchQueryController = null;
}
if (q === "") {
loadSuggestions([]);
return;
}
// Debounce delay based on query length
const delay = q.length > 3 ? 300 : 700;
setTimeout(() => {
// Only search if query hasn't changed in the meantime
if (q !== search.query) return;
search.currentSearchQueryController = new AbortController();
searchEntities(
{ query: q, options: props.options },
search.currentSearchQueryController.signal,
)
.then((suggested: Search) => {
loadSuggestions(suggested.results);
})
.catch((error: DOMException) => {
if (error instanceof DOMException && error.name === "AbortError") {
// Request was aborted, ignore
return;
}
throw error;
});
}, delay);
}
function loadSuggestions(suggestedArr: Suggestion[]) {
search.suggested = suggestedArr;
search.suggested.forEach((item) => {
item.key = itemKey(item);
});
}
function updateSelected(value: Suggestion[]) {
search.selected = value;
}
function resetSuggestion() {
search.query = "";
search.suggested = [];
}
function resetSelection() {
search.selected = [];
}
function resetSearch() {
resetSelection();
resetSuggestion();
}
function selectAll() {
search.suggested.forEach((item) => {
search.selected.push(item);
});
}
function newPriorSuggestion(entity: Result | null) {
if (entity !== null) {
let suggestion = {
key: entity.type + entity.id,
relevance: 0.5,
result: entity,
};
search.priorSuggestion = suggestion;
} else {
search.priorSuggestion = {};
}
}
async function saveFormOnTheFly({
type,
data,
}: {
type: string;
data: Result;
}) {
try {
if (type === "person") {
const responsePerson: Result = await makeFetch(
"POST",
"/api/1.0/person/person.json",
data,
);
newPriorSuggestion(responsePerson);
if (onTheFly.value) onTheFly.value.closeModal();
if (data.addressId != null) {
const household = { type: "household" };
const address = { id: data.addressId };
try {
const responseHousehold: Result = await makeFetch(
"POST",
"/api/1.0/person/household.json",
household,
);
const member = {
concerned: [
{
person: {
type: "person",
id: responsePerson.id,
},
start_date: {
datetime: `${new Date().toISOString().split("T")[0]}T00:00:00+02:00`,
},
holder: false,
comment: null,
},
],
destination: {
type: "household",
id: responseHousehold.id,
},
composition: null,
};
await makeFetch(
"POST",
"/api/1.0/person/household/members/move.json",
member,
);
try {
const _response = await makeFetch(
"POST",
`/api/1.0/person/household/${responseHousehold.id}/address.json`,
address,
);
},
loadSuggestions(suggested) {
this.search.suggested = suggested;
this.search.suggested.forEach(function (item) {
item.key = this.itemKey(item);
}, this);
},
updateSelected(value) {
//console.log('value', value);
this.search.selected = value;
},
resetSearch() {
this.resetSelection();
this.resetSuggestion();
},
resetSuggestion() {
this.search.query = "";
this.search.suggested = [];
},
resetSelection() {
this.search.selected = [];
},
selectAll() {
this.search.suggested.forEach(function (item) {
this.search.selected.push(item);
}, this);
},
itemKey(item) {
return item.result.type + item.result.id;
},
addPriorSuggestion() {
// console.log('prior suggestion', this.priorSuggestion);
if (this.hasPriorSuggestion) {
// console.log('addPriorSuggestion',);
this.suggested.unshift(this.priorSuggestion);
this.selected.unshift(this.priorSuggestion);
console.log(_response);
} catch (error) {
console.error(error);
}
} catch (error) {
console.error(error);
}
}
} else if (type === "thirdparty") {
const response: Result = await makeFetch(
"POST",
"/api/1.0/thirdparty/thirdparty.json",
data,
);
newPriorSuggestion(response);
if (onTheFly.value) onTheFly.value.closeModal();
}
} catch (error) {
console.error(error);
}
}
this.newPriorSuggestion(null);
}
},
newPriorSuggestion(entity) {
// console.log('newPriorSuggestion', entity);
if (entity !== null) {
let suggestion = {
key: entity.type + entity.id,
relevance: 0.5,
result: entity,
};
this.search.priorSuggestion = suggestion;
// console.log('search priorSuggestion', this.search.priorSuggestion);
this.addPriorSuggestion(suggestion);
} else {
this.search.priorSuggestion = {};
}
},
saveFormOnTheFly({ type, data }) {
console.log(
"saveFormOnTheFly from addPersons, type",
type,
", data",
data,
);
if (type === "person") {
makeFetch("POST", "/api/1.0/person/person.json", data)
.then((responsePerson) => {
this.newPriorSuggestion(responsePerson);
this.$refs.onTheFly.closeModal();
watch(
() => props.selected,
(newSelected) => {
search.selected = newSelected;
},
{ deep: true },
);
if (null !== data.addressId) {
const household = {
type: "household",
};
const address = {
id: data.addressId,
};
makeFetch(
"POST",
"/api/1.0/person/household.json",
household,
)
.then((responseHousehold) => {
const member = {
concerned: [
{
person: {
type: "person",
id: responsePerson.id,
},
start_date: {
// TODO: use date.ts methods (low priority)
datetime: `${new Date().toISOString().split("T")[0]}T00:00:00+02:00`,
},
holder: false,
comment: null,
},
],
destination: {
type: "household",
id: responseHousehold.id,
},
composition: null,
};
return makeFetch(
"POST",
"/api/1.0/person/household/members/move.json",
member,
)
.then(() => {
makeFetch(
"POST",
`/api/1.0/person/household/${responseHousehold.id}/address.json`,
address,
)
.then((_response) => {
console.log(_response);
})
.catch((error) => {
if (
error.name ===
"ValidationException"
) {
for (let v of error.violations) {
this.$toast.open({
message: v,
});
}
} else {
this.$toast.open({
message:
"An error occurred",
});
}
});
})
.catch((error) => {
if (
error.name ===
"ValidationException"
) {
for (let v of error.violations) {
this.$toast.open({
message: v,
});
}
} else {
this.$toast.open({
message:
"An error occurred",
});
}
});
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({
message: "An error occurred",
});
}
});
}
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
} else if (type === "thirdparty") {
makeFetch("POST", "/api/1.0/thirdparty/thirdparty.json", data)
.then((response) => {
this.newPriorSuggestion(response);
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
}
},
},
};
watch(
() => props.suggested,
(newSuggested) => {
search.suggested = newSuggested;
},
{ deep: true },
);
watch(
() => modal,
(val) => {
showModal.value = val.value.showModal;
modalDialogClass.value = val.value.modalDialogClass;
},
{ deep: true },
);
defineExpose({
resetSearch,
showModal,
});
</script>
<style lang="scss">
li.add-persons {
a {
cursor: pointer;
}
a {
cursor: pointer;
}
}
div.body-head {
overflow-y: unset;
div.modal-body:first-child {
margin: auto 4em;
div.search {
position: relative;
input {
width: 100%;
padding: 1.2em 1.5em 1.2em 2.5em;
//margin: 1em 0;
}
i {
position: absolute;
opacity: 0.5;
padding: 0.65em 0;
top: 50%;
}
i.fa-search {
left: 0.5em;
}
i.fa-times {
right: 1em;
padding: 0.75em 0;
cursor: pointer;
}
}
overflow-y: unset;
div.modal-body:first-child {
margin: auto 4em;
div.search {
position: relative;
input {
width: 100%;
padding: 1.2em 1.5em 1.2em 2.5em;
//margin: 1em 0;
}
i {
position: absolute;
opacity: 0.5;
padding: 0.65em 0;
top: 50%;
}
i.fa-search {
left: 0.5em;
}
i.fa-times {
right: 1em;
padding: 0.75em 0;
cursor: pointer;
}
}
div.modal-body:last-child {
padding-bottom: 0;
}
div.count {
margin: -0.5em 0 0.7em;
display: flex;
justify-content: space-between;
a {
cursor: pointer;
}
}
div.modal-body:last-child {
padding-bottom: 0;
}
div.count {
margin: -0.5em 0 0.7em;
display: flex;
justify-content: space-between;
a {
cursor: pointer;
}
}
}
.create-button > a {
margin-top: 0.5em;
margin-left: 2.6em;
margin-top: 0.5em;
margin-left: 2.6em;
}
</style>

View File

@@ -1,136 +1,124 @@
<template>
<div class="list-item" :class="{ checked: isChecked }">
<label>
<div>
<input
v-bind:type="type"
v-model="selected"
name="item"
v-bind:id="item"
v-bind:value="setValueByType(item, type)"
/>
</div>
<div class="list-item" :class="{ checked: isChecked }">
<label>
<div>
<input
:type="type"
v-model="selected"
name="item"
:id="item.key"
:value="setValueByType(item, type)"
/>
</div>
<suggestion-person
v-if="item.result.type === 'person'"
v-bind:item="item"
>
</suggestion-person>
<suggestion-person
v-if="item.result.type === 'person'"
:item="item"
></suggestion-person>
<suggestion-third-party
v-if="item.result.type === 'thirdparty'"
@newPriorSuggestion="newPriorSuggestion"
v-bind:item="item"
>
</suggestion-third-party>
<suggestion-third-party
v-if="item.result.type === 'thirdparty'"
@newPriorSuggestion="newPriorSuggestion"
:item="item"
></suggestion-third-party>
<suggestion-user
v-if="item.result.type === 'user'"
v-bind:item="item"
>
</suggestion-user>
<suggestion-user
v-if="item.result.type === 'user'"
:item="item"
></suggestion-user>
<suggestion-user-group
v-if="item.result.type === 'user_group'"
v-bind:item="item"
>
></suggestion-user-group
>
<suggestion-user-group
v-if="item.result.type === 'user_group'"
:item="item"
></suggestion-user-group>
<suggestion-household
v-if="item.result.type === 'household'"
v-bind:item="item"
>
</suggestion-household>
</label>
</div>
<suggestion-household
v-if="item.result.type === 'household'"
:item="item"
></suggestion-household>
</label>
</div>
</template>
<script>
import SuggestionPerson from "./TypePerson";
import SuggestionThirdParty from "./TypeThirdParty";
import SuggestionUser from "./TypeUser";
import SuggestionHousehold from "./TypeHousehold";
import SuggestionUserGroup from "./TypeUserGroup";
<script setup lang="ts">
import { computed } from "vue";
export default {
name: "PersonSuggestion",
components: {
SuggestionPerson,
SuggestionThirdParty,
SuggestionUser,
SuggestionHousehold,
SuggestionUserGroup,
},
props: ["item", "search", "type"],
emits: ["updateSelected", "newPriorSuggestion"],
computed: {
selected: {
set(value) {
//console.log('value', value);
this.$emit("updateSelected", value);
},
get() {
return this.search.selected;
},
},
isChecked() {
return this.search.selected.indexOf(this.item) === -1
? false
: true;
},
},
methods: {
setValueByType(value, type) {
return type === "radio" ? [value] : value;
},
newPriorSuggestion(response) {
this.$emit("newPriorSuggestion", response);
},
},
};
// Components
import SuggestionPerson from "./TypePerson.vue";
import SuggestionThirdParty from "./TypeThirdParty.vue";
import SuggestionUser from "./TypeUser.vue";
import SuggestionHousehold from "./TypeHousehold.vue";
import SuggestionUserGroup from "./TypeUserGroup.vue";
// Types
import { Result, Suggestion } from "ChillPersonAssets/types";
const props = defineProps<{
item: Suggestion;
search: { selected: Suggestion[] };
type: string;
}>();
const emit = defineEmits(["updateSelected", "newPriorSuggestion"]);
// v-model for selected
const selected = computed({
get: () => props.search.selected,
set: (value) => emit("updateSelected", value),
});
const isChecked = computed(
() => props.search.selected.indexOf(props.item) !== -1,
);
function setValueByType(value: Suggestion, type: string) {
return type === "radio" ? [value] : value;
}
function newPriorSuggestion(response: Result) {
emit("newPriorSuggestion", response);
}
</script>
<style lang="scss">
div.results {
div.list-item {
padding: 0.4em 0.8em;
display: flex;
flex-direction: row;
&.checked {
background-color: #ececec;
border-bottom: 1px dotted #8b8b8b;
}
label {
display: inline-flex;
width: 100%;
div.container:not(.household) {
& > input {
margin-right: 0.8em;
}
> span:not(.name) {
margin-left: 0.5em;
opacity: 0.5;
font-size: 90%;
font-style: italic;
}
}
div.right_actions {
margin: 0 0 0 auto;
display: flex;
align-items: flex-end;
& > * {
margin-left: 0.5em;
align-self: baseline;
}
a.btn {
border: 1px solid lightgrey;
font-size: 70%;
padding: 4px;
}
}
}
div.list-item {
padding: 0.4em 0.8em;
display: flex;
flex-direction: row;
&.checked {
background-color: #ececec;
border-bottom: 1px dotted #8b8b8b;
}
label {
display: inline-flex;
width: 100%;
div.container:not(.household) {
& > input {
margin-right: 0.8em;
}
> span:not(.name) {
margin-left: 0.5em;
opacity: 0.5;
font-size: 90%;
font-style: italic;
}
}
div.right_actions {
margin: 0 0 0 auto;
display: flex;
align-items: flex-end;
& > * {
margin-left: 0.5em;
align-self: baseline;
}
a.btn {
border: 1px solid lightgrey;
font-size: 70%;
padding: 4px;
}
}
}
}
}
</style>

View File

@@ -1,26 +1,25 @@
<template>
<div class="container household">
<household-render-box
:household="item.result"
:is-address-multiline="false"
/>
</div>
<div class="container household">
<HouseholdRenderBox
:household="item.result"
:is-address-multiline="false"
/>
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
</div>
<div class="right_actions">
<BadgeEntity :entity="item.result" :options="{ displayLong: true }" />
</div>
</template>
<script>
<script setup lang="ts">
import { defineProps } from "vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import HouseholdRenderBox from "ChillPersonAssets/vuejs/_components/Entity/HouseholdRenderBox.vue";
import { Suggestion } from "ChillPersonAssets/types";
export default {
name: "SuggestionHousehold",
components: {
BadgeEntity,
HouseholdRenderBox,
},
props: ["item"],
};
interface TypeHouseholdProps {
item: Suggestion;
}
defineProps<TypeHouseholdProps>();
</script>

View File

@@ -1,43 +1,48 @@
<template>
<div class="container">
<span class="name">
<person-text :person="item.result" />
</span>
<span class="birthday" v-if="hasBirthdate">
{{ $d(item.result.birthdate.datetime, "short") }}
</span>
<span class="location" v-if="hasAddress">
{{ item.result.current_household_address.text }} -
{{ item.result.current_household_address.postcode.name }}
</span>
</div>
<div class="container">
<span class="name">
<person-text :person="item.result" />
</span>
<span class="birthday" v-if="hasBirthdate">
{{ formatDate(item.result.birthdate?.datetime, "short") }}
</span>
<span class="location" v-if="hasAddress">
{{ item.result.current_household_address?.text }} -
{{ item.result.current_household_address?.postcode?.name }}
</span>
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
<on-the-fly type="person" :id="item.result.id" action="show" />
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
<on-the-fly type="person" :id="item.result.id" action="show" />
</div>
</template>
<script>
<script lang="ts" setup>
import { computed, defineProps } from "vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
import { Person } from "ChillPersonAssets/types";
export default {
name: "SuggestionPerson",
components: {
OnTheFly,
BadgeEntity,
PersonText,
},
props: ["item"],
computed: {
hasBirthdate() {
return this.item.result.birthdate !== null;
},
hasAddress() {
return this.item.result.current_household_address !== null;
},
},
};
function formatDate(dateString: string | undefined, format: string) {
if (!dateString) return "";
// Use Intl.DateTimeFormat or your preferred formatting here
const date = new Date(dateString);
if (format === "short") {
return date.toLocaleDateString();
}
return date.toString();
}
const props = defineProps<{
item: {
result: Person; // add other fields as needed
};
}>();
const hasBirthdate = computed(() => props.item.result.birthdate !== null);
const hasAddress = computed(
() => props.item.result.current_household_address !== null,
);
</script>

View File

@@ -1,126 +1,137 @@
<template>
<div class="container tpartycontainer">
<div class="tparty-identification">
<span v-if="item.result.profession" class="profession">{{
item.result.profession
}}</span>
<span class="name"> {{ item.result.text }}&nbsp; </span>
<span class="location">
<template v-if="hasAddress">
{{ getAddress.text }} -
{{ getAddress.postcode.name }}
</template>
</span>
</div>
<div class="tpartyparent" v-if="hasParent">
<span class="name"> > {{ item.result.parent.text }} </span>
</div>
<div class="container tpartycontainer">
<div class="tparty-identification">
<span v-if="item.result.profession" class="profession">{{
item.result.profession
}}</span>
<span class="name"> {{ item.result.text }}&nbsp; </span>
<span class="location">
<template v-if="hasAddress">
{{ getAddress?.text }} -
{{ getAddress?.postcode?.name }}
</template>
</span>
</div>
<div class="tpartyparent" v-if="hasParent">
<span class="name"> &gt; {{ item.result.parent?.text }} </span>
</div>
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
<on-the-fly
v-if="item.result.kind === 'company'"
:parent="item.result"
@save-form-on-the-fly="saveFormOnTheFly"
action="addContact"
ref="onTheFly"
/>
<on-the-fly type="thirdparty" :id="item.result.id" action="show" />
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
<on-the-fly
v-if="item.result.kind === 'company'"
:parent="item.result"
@save-form-on-the-fly="saveFormOnTheFly"
action="addContact"
ref="onTheFly"
/>
<on-the-fly type="thirdparty" :id="item.result.id" action="show" />
</div>
</template>
<script>
<script lang="ts" setup>
import { computed, ref } from "vue";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { useToast } from "vue-toast-notification";
import { Result, Suggestion } from "ChillPersonAssets/types";
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
interface TypeThirdPartyProps {
item: Suggestion;
}
const props = defineProps<TypeThirdPartyProps>();
const emit = defineEmits<(e: "newPriorSuggestion", payload: unknown) => void>();
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
const toast = useToast();
const hasAddress = computed(() => {
if (props.item.result.address !== null) {
return true;
}
if (props.item.result.parent !== null) {
if (props.item.result.parent) {
return props.item.result.parent.address !== null;
}
}
return false;
});
const hasParent = computed(() => {
return props.item.result.parent !== null;
});
const getAddress = computed(() => {
if (props.item.result.address !== null) {
return props.item.result.address;
}
if (props.item.result.parent && props.item.result.parent.address !== null) {
return props.item.result.parent.address;
}
return null;
});
function saveFormOnTheFly({ data }: { data: Thirdparty }) {
makeFetch("POST", "/api/1.0/thirdparty/thirdparty.json", data)
.then((response: unknown) => {
const result = response as Result;
emit("newPriorSuggestion", result);
if (onTheFly.value) onTheFly.value.closeModal();
})
.catch((error: unknown) => {
const errorResponse = error as { name: string; violations: string[] };
if (errorResponse.name === "ValidationException") {
for (let v of errorResponse.violations) {
if (toast) toast.open({ message: v });
}
} else {
if (toast) toast.open({ message: "An error occurred" });
}
});
}
// i18n config (if needed elsewhere)
const i18n = {
messages: {
fr: {
thirdparty: {
contact: "Personne physique",
company: "Personne morale",
child: "Personne de contact",
},
},
},
};
export default {
name: "SuggestionThirdParty",
components: {
OnTheFly,
BadgeEntity,
},
props: ["item"],
emits: ["newPriorSuggestion"],
i18n,
computed: {
hasAddress() {
if (this.$props.item.result.address !== null) {
return true;
}
if (this.$props.item.result.parent !== null) {
return this.$props.item.result.parent.address !== null;
}
return false;
},
hasParent() {
return this.$props.item.result.parent !== null;
},
getAddress() {
if (this.$props.item.result.address !== null) {
return this.$props.item.result.address;
}
if (this.$props.item.result.parent.address !== null) {
return this.$props.item.result.parent.address;
}
return null;
},
},
methods: {
saveFormOnTheFly({ data }) {
makeFetch("POST", "/api/1.0/thirdparty/thirdparty.json", data)
.then((response) => {
this.$emit("newPriorSuggestion", response);
this.$refs.onTheFly.closeModal();
})
.catch((error) => {
if (error.name === "ValidationException") {
for (let v of error.violations) {
this.$toast.open({ message: v });
}
} else {
this.$toast.open({ message: "An error occurred" });
}
});
},
messages: {
fr: {
thirdparty: {
contact: "Personne physique",
company: "Personne morale",
child: "Personne de contact",
},
},
},
};
defineExpose({
i18n,
});
</script>
<style lang="scss" scoped>
.tpartycontainer {
.tpartyparent {
.name {
font-weight: bold;
font-variant: all-small-caps;
}
.tpartyparent {
.name {
font-weight: bold;
font-variant: all-small-caps;
}
.tparty-identification {
span:not(.name) {
margin-left: 0.5em;
opacity: 0.5;
font-size: 90%;
font-style: italic;
}
.profession {
font-weight: 800;
color: black;
font-style: normal !important;
}
}
.tparty-identification {
span:not(.name) {
margin-left: 0.5em;
opacity: 0.5;
font-size: 90%;
font-style: italic;
}
.profession {
font-weight: 800;
color: black;
font-style: normal !important;
}
}
}
</style>

View File

@@ -1,40 +1,40 @@
<template>
<div class="container usercontainer">
<div class="user-identification">
<user-render-box-badge :user="item.result" />
</div>
</div>
<div class="right_actions">
<badge-entity :entity="item.result" :options="{ displayLong: true }" />
<div class="container usercontainer">
<div class="user-identification">
<UserRenderBoxBadge :user="item.result" />
</div>
</div>
<div class="right_actions">
<BadgeEntity :entity="item.result" :options="{ displayLong: true }" />
</div>
</template>
<script>
<script lang="ts" setup>
import { computed, defineProps } from "vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
import { Suggestion } from "ChillPersonAssets/types";
export default {
name: "SuggestionUser",
components: {
UserRenderBoxBadge,
BadgeEntity,
},
props: ["item"],
computed: {
hasParent() {
return this.$props.item.result.parent !== null;
},
},
};
interface TypeUserProps {
item: Suggestion;
}
const props = defineProps<TypeUserProps>();
const hasParent = computed(() => props.item.result.parent !== null);
defineExpose({
hasParent,
});
</script>
<style lang="scss" scoped>
.usercontainer {
.userparent {
.name {
font-weight: bold;
font-variant: all-small-caps;
}
.userparent {
.name {
font-weight: bold;
font-variant: all-small-caps;
}
}
}
</style>

View File

@@ -1,32 +1,24 @@
<template>
<div class="container user-group-container">
<div class="user-group-identification">
<user-group-render-box :user-group="props.item.result as UserGroup" />
</div>
</div>
<div class="right_actions">
<span class="badge rounded-pill bg-user-group"> Groupe d'utilisateur </span>
</div>
</template>
<script setup lang="ts">
import {
ResultItem,
UserGroup,
} from "../../../../../../ChillMainBundle/Resources/public/types";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
import { UserGroup } from "../../../../../../ChillMainBundle/Resources/public/types";
import UserGroupRenderBox from "ChillMainAssets/vuejs/_components/Entity/UserGroupRenderBox.vue";
import { Suggestion } from "ChillPersonAssets/types";
interface TypeUserGroupProps {
item: ResultItem<UserGroup>;
item: Suggestion;
}
const props = defineProps<TypeUserGroupProps>();
</script>
<template>
<div class="container user-group-container">
<div class="user-group-identification">
<user-group-render-box
:user-group="props.item.result"
></user-group-render-box>
</div>
</div>
<div class="right_actions">
<span class="badge rounded-pill bg-user-group">
Groupe d'utilisateur
</span>
</div>
</template>
<style scoped lang="scss"></style>

View File

@@ -1,168 +1,161 @@
<template>
<section class="chill-entity entity-household">
<div class="item-row">
<div class="item-col">
<!-- identifier -->
<div v-if="isHouseholdNew()" class="h4">
<i class="fa fa-home" />
{{ $t("new_household") }}
</div>
<div v-else class="h4">
<i class="fa fa-home" />
{{ $t("household_number", { number: household.id }) }}
</div>
</div>
<div class="item-col">
<ul class="list-content">
<!-- member part -->
<li
v-if="hasCurrentMembers"
class="members"
:title="$t('current_members')"
>
<span
v-for="m in currentMembers()"
:key="m.id"
class="m"
:class="{ is_new: m.is_new === true }"
>
<person-render-box
render="badge"
:person="m.person"
:options="{
isHolder: m.holder,
addLink: true,
}"
>
<template #post-badge v-if="m.is_new === true">
<span class="post-badge is_new"
><i class="fa fa-sign-in"
/></span>
</template>
</person-render-box>
</span>
</li>
<li v-else class="members" :title="$t('current_members')">
<p class="chill-no-data-statement">
{{ $t("no_members_yet") }}
</p>
</li>
<!-- address part -->
<li v-if="hasAddress()">
<address-render-box
:address="household.current_address"
:is-multiline="isMultiline"
/>
</li>
<li v-else>
<span class="chill-no-data-statement">{{
$t("no_current_address")
}}</span>
</li>
</ul>
</div>
<section class="chill-entity entity-household">
<div class="item-row">
<div class="item-col">
<!-- identifier -->
<div v-if="isHouseholdNew()" class="h4">
<i class="fa fa-home" />
{{ trans(RENDERBOX_NEW_HOUSEHOLD) }}
</div>
</section>
</template>
<div v-else class="h4">
<i class="fa fa-home" />
{{ trans(RENDERBOX_HOUSEHOLD_NUMBER, { number: household.id }) }}
</div>
</div>
<div class="item-col">
<ul class="list-content">
<!-- member part -->
<li
v-if="hasCurrentMembers"
class="members"
:title="trans(RENDERBOX_CURRENT_MEMBERS)"
>
<span
v-for="m in currentMembers()"
:key="m.id"
class="m"
:class="{ is_new: m.is_new === true }"
>
<person-render-box
render="badge"
:person="m.person"
:options="{
isHolder: m.holder,
addLink: true,
}"
>
<template #post-badge v-if="m.is_new === true">
<span class="post-badge is_new"
><i class="fa fa-sign-in"
/></span>
</template>
</person-render-box>
</span>
</li>
<li v-else class="members" :title="trans(RENDERBOX_CURRENT_MEMBERS)">
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_MEMBERS_YET) }}
</p>
</li>
<script>
<!-- address part -->
<li v-if="hasAddress()">
<address-render-box
:address="household.current_address"
:is-multiline="isMultiline"
/>
</li>
<li v-else>
<span class="chill-no-data-statement">{{
trans(RENDERBOX_NO_CURRENT_ADDRESS)
}}</span>
</li>
</ul>
</div>
</div>
</section>
</template>
<script setup>
import { computed } from "vue";
import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import {
trans,
RENDERBOX_NEW_HOUSEHOLD,
RENDERBOX_HOUSEHOLD_NUMBER,
RENDERBOX_CURRENT_MEMBERS,
RENDERBOX_NO_MEMBERS_YET,
RENDERBOX_NO_CURRENT_ADDRESS,
} from "translator";
const i18n = {
messages: {
fr: {
household_number: "Ménage n°{number}",
current_members: "Membres actuels",
no_current_address: "Sans adresse actuellement",
new_household: "Nouveau ménage",
no_members_yet: "Aucun membre actuellement",
holder: "titulaire",
},
},
};
const props = defineProps({
household: { type: Object, required: true },
isAddressMultiline: { type: Boolean, default: false },
});
export default {
name: "HouseholdRenderBox",
props: ["household", "isAddressMultiline"],
components: {
PersonRenderBox,
AddressRenderBox,
},
i18n,
computed: {
isMultiline() {
return typeof this.isAddressMultiline !== "undefined"
? this.isAddressMultiline
: false;
},
},
methods: {
hasCurrentMembers() {
return this.household.current_members_id.length > 0;
},
currentMembers() {
let members = this.household.members
.filter((m) => this.household.current_members_id.includes(m.id))
.sort((a, b) => {
const orderA = a.position ? a.position.ordering : 0;
const orderB = b.position ? b.position.ordering : 0;
const isMultiline = computed(() =>
typeof props.isAddressMultiline !== "undefined"
? props.isAddressMultiline
: false,
);
if (orderA < orderB) {
return -1;
}
if (orderA > orderB) {
return 1;
}
if (a.holder && !b.holder) {
return -1;
}
if (!a.holder && b.holder) {
return 1;
}
return 0;
});
function hasCurrentMembers() {
return props.household.current_members_id.length > 0;
}
if (this.household.new_members !== undefined) {
this.household.new_members
.map((m) => {
m.is_new = true;
return m;
})
.forEach((m) => {
members.push(m);
});
}
function currentMembers() {
let members = props.household.members
.filter((m) => props.household.current_members_id.includes(m.id))
.sort((a, b) => {
const orderA = a.position ? a.position.ordering : 0;
const orderB = b.position ? b.position.ordering : 0;
return members;
},
currentMembersLength() {
return this.household.current_members_id.length;
},
isHouseholdNew() {
return !Number.isInteger(this.household.id);
},
hasAddress() {
return this.household.current_address !== null;
},
},
};
if (orderA < orderB) {
return -1;
}
if (orderA > orderB) {
return 1;
}
if (a.holder && !b.holder) {
return -1;
}
if (!a.holder && b.holder) {
return 1;
}
return 0;
});
if (props.household.new_members !== undefined) {
props.household.new_members
.map((m) => {
m.is_new = true;
return m;
})
.forEach((m) => {
members.push(m);
});
}
return members;
}
function currentMembersLength() {
return props.household.current_members_id.length;
}
function isHouseholdNew() {
return !Number.isInteger(props.household.id);
}
function hasAddress() {
return props.household.current_address !== null;
}
defineExpose(currentMembersLength);
</script>
<style lang="scss">
section.chill-entity {
&.entity-household {
ul.list-content li::marker {
content: "";
}
.members {
.post-badge.is_new {
margin-left: 0.5rem;
color: var(--bs-chill-green);
}
}
&.entity-household {
ul.list-content li::marker {
content: "";
}
.members {
.post-badge.is_new {
margin-left: 0.5rem;
color: var(--bs-chill-green);
}
}
}
}
</style>

View File

@@ -1,493 +1,418 @@
<template>
<div v-if="render === 'bloc'" class="item-bloc">
<section class="chill-entity entity-person">
<div class="item-row entity-bloc">
<div class="item-col">
<div class="entity-label">
<div :class="'denomination h' + options.hLevel">
<a v-if="options.addLink === true" :href="getUrl">
<!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{
person.firstName
}}</span>
<span class="lastname">{{
person.lastName
}}</span>
<span
v-if="person.suffixText"
class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<span
v-if="
person.altNames &&
options.addAltNames == true
"
class="altnames"
>
<span
:class="'altname altname-' + altNameKey"
>{{ altNameLabel }}</span
>
</span>
</a>
<div v-if="render === 'bloc'" class="item-bloc">
<section class="chill-entity entity-person">
<div class="item-row entity-bloc">
<div class="item-col">
<div class="entity-label">
<div :class="'denomination h' + options.hLevel">
<a v-if="options.addLink === true" :href="getUrl">
<!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{ person.firstName }}</span>
<span class="lastname">{{ person.lastName }}</span>
<span v-if="person.suffixText" class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<span
v-if="person.altNames && options.addAltNames == true"
class="altnames"
>
<span :class="'altname altname-' + altNameKey">{{
altNameLabel
}}</span>
</span>
</a>
<!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{
person.firstName
}}</span>
<span class="lastname">{{ person.lastName }}</span>
<span v-if="person.suffixText" class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<span v-if="person.deathdate" class="deathdate">
(‡)</span
>
<span
v-if="
person.altNames &&
options.addAltNames == true
"
class="altnames"
>
<span
:class="'altname altname-' + altNameKey"
>{{ altNameLabel }}</span
>
</span>
<!-- use person-text here to avoid code duplication ? TODO -->
<span class="firstname">{{ person.firstName + " " }}</span>
<span class="lastname">{{ person.lastName }}</span>
<span v-if="person.suffixText" class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<span v-if="person.deathdate" class="deathdate"> ()</span>
<span
v-if="person.altNames && options.addAltNames == true"
class="altnames"
>
<span :class="'altname altname-' + altNameKey">{{
altNameLabel
}}</span>
</span>
<span
v-if="options.addId == true"
class="id-number"
:title="'n° ' + person.id"
>{{ person.id }}</span
>
<span
v-if="options.addId == true"
class="id-number"
:title="'n° ' + person.id"
>{{ person.id }}</span
>
<badge-entity
v-if="options.addEntity === true"
:entity="person"
:options="{
displayLong: options.entityDisplayLong,
}"
/>
</div>
<p v-if="options.addInfo === true" class="moreinfo">
<gender-icon-render-box
v-if="person.gender"
:gender="person.gender"
/>
<time
v-if="person.birthdate && !person.deathdate"
:datetime="person.birthdate"
:title="birthdate"
>
{{
$t(
person.gender
? `renderbox.birthday.${person.gender.genderTranslation}`
: "renderbox.birthday.neutral",
) +
" " +
$d(birthdate, "text")
}}
</time>
<time
v-else-if="person.birthdate && person.deathdate"
:datetime="person.deathdate"
:title="person.deathdate"
>
{{ $d(birthdate) }} - {{ $d(deathdate) }}
</time>
<time
v-else-if="person.deathdate"
:datetime="person.deathdate"
:title="person.deathdate"
>
{{
$t("renderbox.deathdate") + " " + deathdate
}}
</time>
<span
v-if="options.addAge && person.birthdate"
class="age"
>{{
$tc("renderbox.years_old", person.age)
}}</span
>
</p>
</div>
</div>
<div class="item-col">
<div class="float-button bottom">
<div class="box">
<div class="action">
<slot name="record-actions" />
</div>
<ul class="list-content fa-ul">
<li v-if="person.current_household_id">
<i class="fa fa-li fa-map-marker" />
<address-render-box
v-if="person.current_household_address"
:address="
person.current_household_address
"
:is-multiline="isMultiline"
/>
<p v-else class="chill-no-data-statement">
{{
$t(
"renderbox.household_without_address",
)
}}
</p>
<a
v-if="options.addHouseholdLink === true"
:href="getCurrentHouseholdUrl"
:title="
$t(
'persons_associated.show_household_number',
{
id: person.current_household_id,
},
)
"
>
<span
class="badge rounded-pill bg-chill-beige"
>
<i
class="fa fa-fw fa-home"
/><!--{{ $t('persons_associated.show_household') }}-->
</span>
</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-map-marker" />
<p class="chill-no-data-statement">
{{ $t("renderbox.no_data") }}
</p>
</li>
<template
v-if="
this.showResidentialAddresses &&
(
person.current_residential_addresses ||
[]
).length > 0
"
>
<li
v-for="(
addr, i
) in person.current_residential_addresses"
:key="i"
>
<i class="fa fa-li fa-map-marker" />
<div v-if="addr.address">
<span class="item-key"
>{{
$t(
"renderbox.residential_address",
)
}}:</span
>
<div style="margin-top: -1em">
<address-render-box
:address="addr.address"
:is-multiline="isMultiline"
/>
</div>
</div>
<div
v-else-if="addr.hostPerson"
class="mt-3"
>
<p>
{{
$t("renderbox.located_at")
}}:
</p>
<span
class="chill-entity entity-person badge-person"
>
<person-text
v-if="addr.hostPerson"
:person="addr.hostPerson"
/>
</span>
<address-render-box
v-if="addr.hostPerson.address"
:address="
addr.hostPerson.address
"
:is-multiline="isMultiline"
/>
</div>
<div
v-else-if="addr.hostThirdParty"
class="mt-3"
>
<p>
{{
$t("renderbox.located_at")
}}:
</p>
<span
class="chill-entity entity-person badge-thirdparty"
>
<third-party-text
v-if="addr.hostThirdParty"
:thirdparty="
addr.hostThirdParty
"
/>
</span>
<address-render-box
v-if="
addr.hostThirdParty.address
"
:address="
addr.hostThirdParty.address
"
:is-multiline="isMultiline"
/>
</div>
</li>
</template>
<li v-if="person.email">
<i class="fa fa-li fa-envelope-o" />
<a :href="'mailto: ' + person.email">{{
person.email
}}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-envelope-o" />
<p class="chill-no-data-statement">
{{ $t("renderbox.no_data") }}
</p>
</li>
<li v-if="person.mobilenumber">
<i class="fa fa-li fa-mobile" />
<a :href="'tel: ' + person.mobilenumber">{{
person.mobilenumber
}}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-mobile" />
<p class="chill-no-data-statement">
{{ $t("renderbox.no_data") }}
</p>
</li>
<li v-if="person.phonenumber">
<i class="fa fa-li fa-phone" />
<a :href="'tel: ' + person.phonenumber">{{
person.phonenumber
}}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-phone" />
<p class="chill-no-data-statement">
{{ $t("renderbox.no_data") }}
</p>
</li>
<li
v-if="
person.centers !== undefined &&
person.centers.length > 0 &&
options.addCenter
"
>
<i class="fa fa-li fa-long-arrow-right" />
<template v-for="c in person.centers">
{{ c.name }}
</template>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-long-arrow-right" />
<p class="chill-no-data-statement">
{{ $t("renderbox.no_data") }}
</p>
</li>
<slot name="custom-zone" />
</ul>
</div>
</div>
</div>
<badge-entity
v-if="options.addEntity === true"
:entity="person"
:options="{ displayLong: options.entityDisplayLong }"
/>
</div>
<slot name="end-bloc" />
</section>
</div>
<p v-if="options.addInfo === true" class="moreinfo">
<gender-icon-render-box
v-if="person.gender"
:gender="person.gender"
/>
<time
v-if="person.birthdate && !person.deathdate"
:datetime="person.birthdate"
:title="birthdate"
>
{{
trans(birthdateTranslation) +
" " +
new Intl.DateTimeFormat("fr-FR", {
dateStyle: "long",
}).format(birthdate)
}}
</time>
<span
v-if="render === 'badge'"
class="chill-entity entity-person badge-person"
>
<a v-if="options.addLink === true" :href="getUrl">
<span
v-if="options.isHolder"
class="fa-stack fa-holder"
:title="$t('renderbox.holder')"
>
<i class="fa fa-circle fa-stack-1x text-success" />
<i class="fa fa-stack-1x">T</i>
</span>
<time
v-else-if="person.birthdate && person.deathdate"
:datetime="person.deathdate"
:title="person.deathdate"
>
{{
new Intl.DateTimeFormat("fr-FR", {
dateStyle: "long",
}).format(birthdate)
}}
-
{{
new Intl.DateTimeFormat("fr-FR", {
dateStyle: "long",
}).format(deathdate)
}}
</time>
<person-text :person="person" />
</a>
<span v-else>
<span
v-if="options.isHolder"
class="fa-stack fa-holder"
:title="$t('renderbox.holder')"
>
<i class="fa fa-circle fa-stack-1x text-success" />
<i class="fa fa-stack-1x">T</i>
</span>
<person-text :person="person" />
</span>
<slot name="post-badge" />
<time
v-else-if="person.deathdate"
:datetime="person.deathdate"
:title="person.deathdate"
>
{{
trans(RENDERBOX_DEATHDATE) +
" " +
new Intl.DateTimeFormat("fr-FR", {
dateStyle: "long",
}).format(deathdate)
}}
</time>
<span v-if="options.addAge && person.birthdate" class="age">
({{ trans(RENDERBOX_YEARS_OLD, { n: person.age }) }})
</span>
</p>
</div>
</div>
<div class="item-col">
<div class="float-button bottom">
<div class="box">
<div class="action">
<slot name="record-actions" />
</div>
<ul class="list-content fa-ul">
<li v-if="person.current_household_id">
<i class="fa fa-li fa-map-marker" />
<address-render-box
v-if="person.current_household_address"
:address="person.current_household_address"
:is-multiline="isMultiline"
/>
<p v-else class="chill-no-data-statement">
{{ trans(RENDERBOX_HOUSEHOLD_WITHOUT_ADDRESS) }}
</p>
<a
v-if="options.addHouseholdLink === true"
:href="getCurrentHouseholdUrl"
:title="
trans(PERSONS_ASSOCIATED_SHOW_HOUSEHOLD_NUMBER, {
id: person.current_household_id,
})
"
>
<span class="badge rounded-pill bg-chill-beige">
<i
class="fa fa-fw fa-home"
/><!--{{ trans(PERSONS_ASSOCIATED_SHOW_HOUSEHOLD) }}-->
</span>
</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-map-marker" />
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_DATA) }}
</p>
</li>
<template
v-if="
this.showResidentialAddresses &&
(person.current_residential_addresses || []).length > 0
"
>
<li
v-for="(addr, i) in person.current_residential_addresses"
:key="i"
>
<i class="fa fa-li fa-map-marker" />
<div v-if="addr.address">
<span class="item-key">
{{ trans(RENDERBOX_RESIDENTIAL_ADDRESS) }}:
</span>
<div style="margin-top: -1em">
<address-render-box
:address="addr.address"
:is-multiline="isMultiline"
/>
</div>
</div>
<div v-else-if="addr.hostPerson" class="mt-3">
<p>{{ trans(RENDERBOX_LOCATED_AT) }}:</p>
<span class="chill-entity entity-person badge-person">
<person-text
v-if="addr.hostPerson"
:person="addr.hostPerson"
/>
</span>
<address-render-box
v-if="addr.hostPerson.address"
:address="addr.hostPerson.address"
:is-multiline="isMultiline"
/>
</div>
<div v-else-if="addr.hostThirdParty" class="mt-3">
<p>{{ trans(RENDERBOX_LOCATED_AT) }}:</p>
<span class="chill-entity entity-person badge-thirdparty">
<third-party-text
v-if="addr.hostThirdParty"
:thirdparty="addr.hostThirdParty"
/>
</span>
<address-render-box
v-if="addr.hostThirdParty.address"
:address="addr.hostThirdParty.address"
:is-multiline="isMultiline"
/>
</div>
</li>
</template>
<li v-if="person.email">
<i class="fa fa-li fa-envelope-o" />
<a :href="'mailto: ' + person.email">{{ person.email }}</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-envelope-o" />
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_DATA) }}
</p>
</li>
<li v-if="person.mobilenumber">
<i class="fa fa-li fa-mobile" />
<a :href="'tel: ' + person.mobilenumber">
{{ person.mobilenumber }}
</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-mobile" />
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_DATA) }}
</p>
</li>
<li v-if="person.phonenumber">
<i class="fa fa-li fa-phone" />
<a :href="'tel: ' + person.phonenumber">
{{ person.phonenumber }}
</a>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-phone" />
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_DATA) }}
</p>
</li>
<li
v-if="
person.centers !== undefined &&
person.centers.length > 0 &&
options.addCenter
"
>
<i class="fa fa-li fa-long-arrow-right" />
<template v-for="c in person.centers">
{{ c.name }}
</template>
</li>
<li v-else-if="options.addNoData">
<i class="fa fa-li fa-long-arrow-right" />
<p class="chill-no-data-statement">
{{ trans(RENDERBOX_NO_DATA) }}
</p>
</li>
<slot name="custom-zone" />
</ul>
</div>
</div>
</div>
</div>
<slot name="end-bloc" />
</section>
</div>
<span
v-if="render === 'badge'"
class="chill-entity entity-person badge-person"
>
<a v-if="options.addLink === true" :href="getUrl">
<span
v-if="options.isHolder"
class="fa-stack fa-holder"
:title="trans(RENDERBOX_HOLDER)"
>
<i class="fa fa-circle fa-stack-1x text-success" />
<i class="fa fa-stack-1x">T</i>
</span>
<person-text :person="person" />
</a>
<span v-else>
<span
v-if="options.isHolder"
class="fa-stack fa-holder"
:title="trans(RENDERBOX_HOLDER)"
>
<i class="fa fa-circle fa-stack-1x text-success" />
<i class="fa fa-stack-1x">T</i>
</span>
<person-text :person="person" />
</span>
<slot name="post-badge" />
</span>
</template>
<script>
<script setup>
import { computed } from "vue";
import { ISOToDate } from "ChillMainAssets/chill/js/date";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import GenderIconRenderBox from "ChillMainAssets/vuejs/_components/Entity/GenderIconRenderBox.vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
import ThirdPartyText from "ChillThirdPartyAssets/vuejs/_components/Entity/ThirdPartyText.vue";
import {
trans,
RENDERBOX_HOLDER,
RENDERBOX_NO_DATA,
RENDERBOX_DEATHDATE,
RENDERBOX_HOUSEHOLD_WITHOUT_ADDRESS,
RENDERBOX_RESIDENTIAL_ADDRESS,
RENDERBOX_LOCATED_AT,
RENDERBOX_BIRTHDAY_MAN,
RENDERBOX_BIRTHDAY_WOMAN,
RENDERBOX_BIRTHDAY_UNKNOWN,
RENDERBOX_BIRTHDAY_NEUTRAL,
PERSONS_ASSOCIATED_SHOW_HOUSEHOLD_NUMBER,
RENDERBOX_YEARS_OLD,
} from "translator";
export default {
name: "PersonRenderBox",
components: {
AddressRenderBox,
GenderIconRenderBox,
BadgeEntity,
PersonText,
ThirdPartyText,
},
props: {
person: {
required: true,
},
options: {
type: Object,
required: false,
},
render: {
type: String,
},
returnPath: {
type: String,
},
showResidentialAddresses: {
type: Boolean,
default: false,
},
},
computed: {
isMultiline: function () {
if (this.options.isMultiline) {
return this.options.isMultiline;
} else {
return false;
}
},
birthdate: function () {
if (
this.person.birthdate !== null ||
this.person.birthdate === "undefined"
) {
return ISOToDate(this.person.birthdate.datetime);
} else {
return "";
}
},
deathdate: function () {
if (
this.person.deathdate !== null ||
this.person.birthdate === "undefined"
) {
return new Date(this.person.deathdate.datetime);
} else {
return "";
}
},
altNameLabel: function () {
let altNameLabel = "";
this.person.altNames.forEach(
(altName) => (altNameLabel += altName.label),
);
return altNameLabel;
},
altNameKey: function () {
let altNameKey = "";
this.person.altNames.forEach(
(altName) => (altNameKey += altName.key),
);
return altNameKey;
},
getUrl: function () {
return `/fr/person/${this.person.id}/general`;
},
getCurrentHouseholdUrl: function () {
let returnPath = this.returnPath
? `?returnPath=${this.returnPath}`
: ``;
return `/fr/person/household/${this.person.current_household_id}/summary${returnPath}`;
},
},
};
const props = defineProps({
person: {
required: true,
},
options: {
type: Object,
required: false,
},
render: {
type: String,
},
returnPath: {
type: String,
},
showResidentialAddresses: {
type: Boolean,
default: false,
},
});
const birthdateTranslation = computed(() => {
if (props.person.gender) {
const { genderTranslation } = props.person.gender;
switch (genderTranslation) {
case "man":
return RENDERBOX_BIRTHDAY_MAN;
case "woman":
return RENDERBOX_BIRTHDAY_WOMAN;
case "neutral":
return RENDERBOX_BIRTHDAY_NEUTRAL;
case "unknown":
return RENDERBOX_BIRTHDAY_UNKNOWN;
default:
return RENDERBOX_BIRTHDAY_UNKNOWN;
}
} else {
return RENDERBOX_BIRTHDAY_UNKNOWN;
}
});
const isMultiline = computed(() => {
return props.options?.isMultiline || false;
});
const birthdate = computed(() => {
if (
props.person.birthdate !== null &&
props.person.birthdate !== undefined &&
props.person.birthdate.datetime
) {
return ISOToDate(props.person.birthdate.datetime);
} else {
return "";
}
});
const deathdate = computed(() => {
if (
props.person.deathdate !== null &&
props.person.deathdate !== undefined &&
props.person.deathdate.datetime
) {
return new Date(props.person.deathdate.datetime);
} else {
return "";
}
});
const altNameLabel = computed(() => {
let altNameLabel = "";
(props.person.altNames || []).forEach(
(altName) => (altNameLabel += altName.label),
);
return altNameLabel;
});
const altNameKey = computed(() => {
let altNameKey = "";
(props.person.altNames || []).forEach(
(altName) => (altNameKey += altName.key),
);
return altNameKey;
});
const getUrl = computed(() => {
return `/fr/person/${props.person.id}/general`;
});
const getCurrentHouseholdUrl = computed(() => {
let returnPath = props.returnPath ? `?returnPath=${props.returnPath}` : ``;
return `/fr/person/household/${props.person.current_household_id}/summary${returnPath}`;
});
</script>
<style lang="scss" scoped>
@import "ChillMainAssets/module/bootstrap/shared";
@import "ChillPersonAssets/chill/scss/mixins";
@import "ChillMainAssets/chill/scss/chill_variables";
.lastname:before {
content: " ";
}
div.flex-table {
div.item-bloc {
div.item-row {
div.item-col:first-child {
width: 33%;
}
@include media-breakpoint-down(sm) {
div.item-col:first-child {
width: unset;
}
}
div.item-col:last-child {
justify-content: flex-start;
}
}
}
}
.age {
margin-left: 0.5em;
&:before {
content: "(";
}
&:after {
content: ")";
}
}
</style>

View File

@@ -1,75 +1,51 @@
<template>
<span v-if="isCut">{{ cutText }}</span>
<span v-else class="person-text">
<span class="firstname">{{ person.firstName }}</span>
<!-- display: inline -->
<span class="lastname">&nbsp;{{ person.lastName }}</span>
<span
v-if="person.altNames && person.altNames.length > 0"
class="altnames"
>
<!-- display: inline -->
<span :class="'altname altname-' + altNameKey"
>&nbsp;({{ altNameLabel }})</span
>
</span>
<!-- display: inline -->
<span v-if="person.suffixText" class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<!-- display: inline -->
<span
class="age"
v-if="
this.addAge &&
person.birthdate !== null &&
person.deathdate === null
"
>&nbsp;{{ $tc("renderbox.years_old", person.age) }}</span
>
<span v-else-if="this.addAge && person.deathdate !== null"
>&nbsp;(‡)</span
>
<span v-if="isCut">{{ cutText }}</span>
<span v-else class="person-text">
<span class="firstname">{{ person.firstName }}</span>
<span class="lastname">&nbsp;{{ person.lastName }}</span>
<span v-if="person.altNames && person.altNames.length > 0" class="altnames">
<span :class="'altname altname-' + altNameKey"
>&nbsp;({{ altNameLabel }})</span
>
</span>
<span v-if="person.suffixText" class="suffixtext"
>&nbsp;{{ person.suffixText }}</span
>
<span
class="age"
v-if="addAge && person.birthdate !== null && person.deathdate === null"
>&nbsp;{{ trans(RENDERBOX_YEARS_OLD, person.age) }}</span
>
<span v-else-if="addAge && person.deathdate !== null">&nbsp;()</span>
</span>
</template>
<script>
export default {
name: "PersonText",
props: {
person: {
required: true,
},
isCut: {
type: Boolean,
required: false,
default: false,
},
addAge: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
altNameLabel: function () {
let altNameLabel = "";
this.person.altNames.forEach(
(altName) => (altNameLabel += altName.label),
);
return altNameLabel;
},
altNameKey: function () {
let altNameKey = "";
this.person.altNames.forEach(
(altName) => (altNameKey += altName.key),
);
return altNameKey;
},
cutText: function () {
let more = this.person.text.length > 15 ? "…" : "";
return this.person.text.slice(0, 15) + more;
},
},
};
<script lang="ts" setup>
import { computed, toRefs } from "vue";
import { trans, RENDERBOX_YEARS_OLD } from "translator";
import { AltName, Person } from "ChillPersonAssets/types";
const props = defineProps<{
person: Person;
isCut?: boolean;
addAge?: boolean;
}>();
const { person, isCut = false, addAge = true } = toRefs(props);
const altNameLabel = computed(() => {
if (!person.value.altNames) return "";
return person.value.altNames.map((a: AltName) => a.label).join("");
});
const altNameKey = computed(() => {
if (!person.value.altNames) return "";
return person.value.altNames.map((a: AltName) => a.key).join("");
});
const cutText = computed(() => {
if (!person.value.text) return "";
const more = person.value.text.length > 15 ? "…" : "";
return person.value.text.slice(0, 15) + more;
});
</script>

View File

@@ -1,530 +1,466 @@
<template>
<div v-if="action === 'show'">
<div class="flex-table">
<person-render-box
render="bloc"
:person="person"
:options="{
addInfo: true,
addEntity: false,
addAltNames: true,
addAge: true,
addId: true,
addLink: false,
hLevel: 3,
addCenter: true,
addNoData: true,
isMultiline: true,
}"
:show-residential-addresses="true"
></person-render-box>
</div>
<div v-if="action === 'show'">
<div class="flex-table">
<person-render-box
render="bloc"
:person="person"
:options="{
addInfo: true,
addEntity: false,
addAltNames: true,
addAge: true,
addId: true,
addLink: false,
hLevel: 3,
addCenter: true,
addNoData: true,
isMultiline: true,
}"
:show-residential-addresses="true"
/>
</div>
</div>
<div v-else-if="action === 'edit' || action === 'create'">
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="lastname"
v-model="lastName"
:placeholder="trans(PERSON_MESSAGES_PERSON_LASTNAME)"
@change="checkErrors"
/>
<label for="lastname">{{ trans(PERSON_MESSAGES_PERSON_LASTNAME) }}</label>
</div>
<div v-else-if="action === 'edit' || action === 'create'">
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="lastname"
v-model="lastName"
:placeholder="$t('person.lastname')"
@change="checkErrors"
/>
<label for="lastname">{{ $t("person.lastname") }}</label>
</div>
<div v-if="queryItems">
<ul class="list-suggest add-items inline">
<li
v-for="(qi, i) in queryItems"
:key="i"
@click="addQueryItem('lastName', qi)"
>
<span class="person-text">{{ qi }}</span>
</li>
</ul>
</div>
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="firstname"
v-model="firstName"
:placeholder="$t('person.firstname')"
@change="checkErrors"
/>
<label for="firstname">{{ $t("person.firstname") }}</label>
</div>
<div v-if="queryItems">
<ul class="list-suggest add-items inline">
<li
v-for="(qi, i) in queryItems"
:key="i"
@click="addQueryItem('firstName', qi)"
>
<span class="person-text">{{ qi }}</span>
</li>
</ul>
</div>
<div
v-for="(a, i) in config.altNames"
:key="a.key"
class="form-floating mb-3"
<div v-if="queryItems">
<ul class="list-suggest add-items inline">
<li
v-for="(qi, i) in queryItems"
:key="i"
@click="addQueryItem('lastName', qi)"
>
<input
class="form-control form-control-lg"
:id="a.key"
:value="personAltNamesLabels[i]"
@input="onAltNameInput"
/>
<label :for="a.key">{{ localizeString(a.labels) }}</label>
</div>
<span class="person-text">{{ qi }}</span>
</li>
</ul>
</div>
<!-- TODO fix placeholder if undefined
<div class="form-floating mb-3">
<input
class="form-control form-control-lg"
id="firstname"
v-model="firstName"
:placeholder="trans(PERSON_MESSAGES_PERSON_FIRSTNAME)"
@change="checkErrors"
/>
<label for="firstname">{{
trans(PERSON_MESSAGES_PERSON_FIRSTNAME)
}}</label>
</div>
<div v-if="queryItems">
<ul class="list-suggest add-items inline">
<li
v-for="(qi, i) in queryItems"
:key="i"
@click="addQueryItem('firstName', qi)"
>
<span class="person-text">{{ qi }}</span>
</li>
</ul>
</div>
<div
v-for="(a, i) in config.altNames"
:key="a.key"
class="form-floating mb-3"
>
<input
class="form-control form-control-lg"
:id="a.key"
:value="personAltNamesLabels[i]"
@input="onAltNameInput"
/>
<label :for="a.key">{{ localizeString(a.labels) }}</label>
</div>
<!-- TODO fix placeholder if undefined
-->
<div class="form-floating mb-3">
<select
class="form-select form-select-lg"
id="gender"
v-model="gender"
>
<option selected disabled>
{{ $t("person.gender.placeholder") }}
</option>
<option v-for="g in config.genders" :value="g.id" :key="g.id">
{{ g.label }}
</option>
</select>
<label>{{ $t("person.gender.title") }}</label>
</div>
<div
class="form-floating mb-3"
v-if="showCenters && config.centers.length > 1"
>
<select
class="form-select form-select-lg"
id="center"
v-model="center"
>
<option selected disabled>
{{ $t("person.center.placeholder") }}
</option>
<option v-for="c in config.centers" :value="c" :key="c.id">
{{ c.name }}
</option>
</select>
<label>{{ $t("person.center.title") }}</label>
</div>
<div class="form-floating mb-3">
<select
class="form-select form-select-lg"
id="civility"
v-model="civility"
>
<option selected disabled>
{{ $t("person.civility.placeholder") }}
</option>
<option
v-for="c in config.civilities"
:value="c.id"
:key="c.id"
>
{{ localizeString(c.name) }}
</option>
</select>
<label>{{ $t("person.civility.title") }}</label>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="birthdate"
><i class="fa fa-fw fa-birthday-cake"></i
></span>
<input
type="date"
class="form-control form-control-lg"
id="chill_personbundle_person_birthdate"
name="chill_personbundle_person[birthdate]"
v-model="birthDate"
aria-describedby="birthdate"
/>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="phonenumber"
><i class="fa fa-fw fa-phone"></i
></span>
<input
class="form-control form-control-lg"
v-model="phonenumber"
:placeholder="$t('person.phonenumber')"
:aria-label="$t('person.phonenumber')"
aria-describedby="phonenumber"
/>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="mobilenumber"
><i class="fa fa-fw fa-mobile"></i
></span>
<input
class="form-control form-control-lg"
v-model="mobilenumber"
:placeholder="$t('person.mobilenumber')"
:aria-label="$t('person.mobilenumber')"
aria-describedby="mobilenumber"
/>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="email"
><i class="fa fa-fw fa-at"></i
></span>
<input
class="form-control form-control-lg"
v-model="email"
:placeholder="$t('person.email')"
:aria-label="$t('person.email')"
aria-describedby="email"
/>
</div>
<div v-if="action === 'create'" class="input-group mb-3 form-check">
<input
class="form-check-input"
type="checkbox"
v-model="showAddressForm"
name="showAddressForm"
/>
<label class="form-check-label">{{
$t("person.address.show_address_form")
}}</label>
</div>
<div
v-if="action === 'create' && showAddressFormValue"
class="form-floating mb-3"
>
<p>{{ $t("person.address.warning") }}</p>
<add-address
:context="addAddress.context"
:options="addAddress.options"
:addressChangedCallback="submitNewAddress"
ref="addAddress"
>
</add-address>
</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 class="form-floating mb-3">
<select class="form-select form-select-lg" id="gender" v-model="gender">
<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>{{ trans(PERSON_MESSAGES_PERSON_GENDER_TITLE) }}</label>
</div>
</template>
<script>
<div
class="form-floating mb-3"
v-if="showCenters && config.centers.length > 1"
>
<select class="form-select form-select-lg" id="center" v-model="center">
<option selected disabled>
{{ trans(PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER) }}
</option>
<option v-for="c in config.centers" :value="c" :key="c.id">
{{ c.name }}
</option>
</select>
<label>{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
</div>
<div class="form-floating mb-3">
<select
class="form-select form-select-lg"
id="civility"
v-model="civility"
>
<option selected disabled>
{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER) }}
</option>
<option v-for="c in config.civilities" :value="c.id" :key="c.id">
{{ localizeString(c.name) }}
</option>
</select>
<label>{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="phonenumber">
<i class="fa fa-fw fa-phone"></i>
</span>
<input
class="form-control form-control-lg"
v-model="phonenumber"
:placeholder="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
:aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
aria-describedby="phonenumber"
/>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="mobilenumber">
<i class="fa fa-fw fa-mobile"></i>
</span>
<input
class="form-control form-control-lg"
v-model="mobilenumber"
:placeholder="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
:aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
aria-describedby="mobilenumber"
/>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="email">
<i class="fa fa-fw fa-at"></i>
</span>
<input
class="form-control form-control-lg"
v-model="email"
:placeholder="trans(PERSON_MESSAGES_PERSON_EMAIL)"
:aria-label="trans(PERSON_MESSAGES_PERSON_EMAIL)"
aria-describedby="email"
/>
</div>
<div v-if="action === 'create'" class="input-group mb-3 form-check">
<input
class="form-check-input"
type="checkbox"
v-model="showAddressForm"
name="showAddressForm"
/>
<label class="form-check-label">
{{ trans(PERSON_MESSAGES_PERSON_ADDRESS_SHOW_ADDRESS_FORM) }}
</label>
</div>
<div
v-if="action === 'create' && showAddressFormValue"
class="form-floating mb-3"
>
<p>{{ trans(PERSON_MESSAGES_PERSON_ADDRESS_WARNING) }}</p>
<AddAddress
:context="addAddress.context"
:options="addAddress.options"
:addressChangedCallback="submitNewAddress"
ref="addAddress"
/>
</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>
</template>
<script setup>
import { ref, reactive, computed, onMounted } from "vue";
import {
getCentersForPersonCreation,
getCivilities,
getGenders,
getPerson,
getPersonAltNames,
getCentersForPersonCreation,
getCivilities,
getGenders,
getPerson,
getPersonAltNames,
} from "../../_api/OnTheFly";
import PersonRenderBox from "../Entity/PersonRenderBox.vue";
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import {
trans,
PERSON_MESSAGES_PERSON_LASTNAME,
PERSON_MESSAGES_PERSON_FIRSTNAME,
PERSON_MESSAGES_PERSON_GENDER_PLACEHOLDER,
PERSON_MESSAGES_PERSON_GENDER_TITLE,
PERSON_MESSAGES_PERSON_CENTER_PLACEHOLDER,
PERSON_MESSAGES_PERSON_CENTER_TITLE,
PERSON_MESSAGES_PERSON_CIVILITY_PLACEHOLDER,
PERSON_MESSAGES_PERSON_CIVILITY_TITLE,
PERSON_MESSAGES_PERSON_PHONENUMBER,
PERSON_MESSAGES_PERSON_MOBILENUMBER,
PERSON_MESSAGES_PERSON_EMAIL,
PERSON_MESSAGES_PERSON_ADDRESS_SHOW_ADDRESS_FORM,
PERSON_MESSAGES_PERSON_ADDRESS_WARNING,
} from "translator";
export default {
name: "OnTheFlyPerson",
props: ["id", "type", "action", "query"],
//emits: ['createAction'],
components: {
PersonRenderBox,
AddAddress,
},
data() {
return {
person: {
type: "person",
lastName: "",
firstName: "",
altNames: [],
addressId: null,
center: null,
},
config: {
altNames: [],
civilities: [],
centers: [],
genders: [],
},
showCenters: false, // NOTE: must remains false if the form is not in create mode
showAddressFormValue: false,
addAddress: {
options: {
button: {
text: { create: "person.address.create_address" },
size: "btn-sm",
},
title: { create: "person.address.create_address" },
},
context: {
target: {}, // boilerplate for getting the address id
edit: false,
addressId: null,
defaults: window.addaddress,
},
},
errors: [],
};
},
computed: {
firstName: {
set(value) {
this.person.firstName = value;
},
get() {
return this.person.firstName;
},
},
lastName: {
set(value) {
this.person.lastName = value;
},
get() {
return this.person.lastName;
},
},
gender: {
set(value) {
this.person.gender = { id: value, type: "chill_main_gender" };
},
get() {
return this.person.gender ? this.person.gender.id : null;
},
},
civility: {
set(value) {
this.person.civility = {
id: value,
type: "chill_main_civility",
};
},
get() {
return this.person.civility ? this.person.civility.id : null;
},
},
birthDate: {
set(value) {
if (this.person.birthdate) {
this.person.birthdate.datetime = value + "T00:00:00+0100";
} else {
this.person.birthdate = {
datetime: value + "T00:00:00+0100",
};
}
},
get() {
return this.person.birthdate
? this.person.birthdate.datetime.split("T")[0]
: "";
},
},
phonenumber: {
set(value) {
this.person.phonenumber = value;
},
get() {
return this.person.phonenumber;
},
},
mobilenumber: {
set(value) {
this.person.mobilenumber = value;
},
get() {
return this.person.mobilenumber;
},
},
email: {
set(value) {
this.person.email = value;
},
get() {
return this.person.email;
},
},
showAddressForm: {
set(value) {
this.showAddressFormValue = value;
},
get() {
return this.showAddressFormValue;
},
},
center: {
set(value) {
console.log("will set center", value);
this.person.center = { id: value.id, type: value.type };
},
get() {
const center = this.config.centers.find(
(c) =>
this.person.center !== null &&
this.person.center.id === c.id,
);
const props = defineProps({
id: [String, Number],
type: String,
action: String,
query: String,
});
console.log("center get", center);
const person = reactive({
type: "person",
lastName: "",
firstName: "",
altNames: [],
addressId: null,
center: null,
gender: null,
civility: null,
birthdate: null,
phonenumber: "",
mobilenumber: "",
email: "",
});
return typeof center === "undefined" ? null : center;
},
},
genderClass() {
switch (this.person.gender) {
case "woman":
return "fa-venus";
case "man":
return "fa-mars";
case "both":
return "fa-neuter";
case "unknown":
return "fa-genderless";
default:
return "fa-genderless";
}
},
genderTranslation() {
switch (this.person.gender.genderTranslation) {
case "woman":
return "person.gender.woman";
case "man":
return "person.gender.man";
case "neutral":
return "person.gender.neutral";
case "unknown":
return "person.gender.unknown";
default:
return "person.gender.unknown";
}
},
feminized() {
return this.person.gender === "woman" ? "e" : "";
},
personAltNamesLabels() {
return this.person.altNames.map((a) => (a ? a.label : ""));
},
queryItems() {
return this.query ? this.query.split(" ") : null;
},
const config = reactive({
altNames: [],
civilities: [],
centers: [],
genders: [],
});
const showCenters = ref(false);
const showAddressFormValue = ref(false);
const errors = ref([]);
const addAddress = reactive({
options: {
button: {
text: { create: "person.address.create_address" },
size: "btn-sm",
},
mounted() {
getPersonAltNames().then((altNames) => {
this.config.altNames = altNames;
});
getCivilities().then((civilities) => {
if ("results" in civilities) {
this.config.civilities = civilities.results;
}
});
getGenders().then((genders) => {
if ("results" in genders) {
console.log("genders", genders.results);
this.config.genders = genders.results;
}
});
if (this.action !== "create") {
this.loadData();
} else {
// console.log('show centers', this.showCenters);
getCentersForPersonCreation().then((params) => {
this.config.centers = params.centers.filter((c) => c.isActive);
this.showCenters = params.showCenters;
// console.log('centers', this.config.centers)
// console.log('show centers inside', this.showCenters);
if (this.showCenters && this.config.centers.length === 1) {
this.person.center = this.config.centers[0];
}
});
}
},
methods: {
localizeString,
checkErrors() {
this.errors = [];
if (this.person.lastName === "") {
this.errors.push("Le nom ne doit pas être vide.");
}
if (this.person.firstName === "") {
this.errors.push("Le prénom ne doit pas être vide.");
}
if (!this.person.gender) {
this.errors.push("Le genre doit être renseigné");
}
if (this.showCenters && this.person.center === null) {
this.errors.push("Le centre doit être renseigné");
}
},
loadData() {
getPerson(this.id).then(
(person) =>
new Promise((resolve) => {
this.person = person;
//console.log('get person', this.person);
resolve();
}),
);
},
onAltNameInput(event) {
const key = event.target.id;
const label = event.target.value;
let updateAltNames = this.person.altNames.filter(
(a) => a.key !== key,
);
updateAltNames.push({ key: key, label: label });
this.person.altNames = updateAltNames;
},
addQueryItem(field, queryItem) {
switch (field) {
case "lastName":
this.person.lastName = this.person.lastName
? (this.person.lastName += ` ${queryItem}`)
: queryItem;
break;
case "firstName":
this.person.firstName = this.person.firstName
? (this.person.firstName += ` ${queryItem}`)
: queryItem;
break;
}
},
submitNewAddress(payload) {
this.person.addressId = payload.addressId;
},
},
};
title: { create: "person.address.create_address" },
},
context: {
target: {},
edit: false,
addressId: null,
defaults: window.addaddress,
},
});
const firstName = computed({
get: () => person.firstName,
set: (value) => {
person.firstName = value;
},
});
const lastName = computed({
get: () => person.lastName,
set: (value) => {
person.lastName = value;
},
});
const gender = computed({
get: () => (person.gender ? person.gender.id : null),
set: (value) => {
person.gender = { id: value, type: "chill_main_gender" };
},
});
const civility = computed({
get: () => (person.civility ? person.civility.id : null),
set: (value) => {
person.civility = { id: value, type: "chill_main_civility" };
},
});
const birthDate = computed({
get: () => (person.birthdate ? person.birthdate.datetime.split("T")[0] : ""),
set: (value) => {
if (person.birthdate) {
person.birthdate.datetime = value + "T00:00:00+0100";
} else {
person.birthdate = { datetime: value + "T00:00:00+0100" };
}
},
});
const phonenumber = computed({
get: () => person.phonenumber,
set: (value) => {
person.phonenumber = value;
},
});
const mobilenumber = computed({
get: () => person.mobilenumber,
set: (value) => {
person.mobilenumber = value;
},
});
const email = computed({
get: () => person.email,
set: (value) => {
person.email = value;
},
});
const showAddressForm = computed({
get: () => showAddressFormValue.value,
set: (value) => {
showAddressFormValue.value = value;
},
});
const center = computed({
get: () => {
const c = config.centers.find(
(c) => person.center !== null && person.center.id === c.id,
);
return typeof c === "undefined" ? null : c;
},
set: (value) => {
person.center = { id: value.id, type: value.type };
},
});
const genderClass = computed(() => {
switch (person.gender && person.gender.id) {
case "woman":
return "fa-venus";
case "man":
return "fa-mars";
case "both":
return "fa-neuter";
case "unknown":
return "fa-genderless";
default:
return "fa-genderless";
}
});
const genderTranslation = computed(() => {
switch (person.gender && person.gender.genderTranslation) {
case "woman":
return PERSON_MESSAGES_PERSON_GENDER_WOMAN;
case "man":
return PERSON_MESSAGES_PERSON_GENDER_MAN;
case "neutral":
return PERSON_MESSAGES_PERSON_GENDER_NEUTRAL;
case "unknown":
return PERSON_MESSAGES_PERSON_GENDER_UNKNOWN;
default:
return PERSON_MESSAGES_PERSON_GENDER_UNKNOWN;
}
});
const feminized = computed(() =>
person.gender && person.gender.id === "woman" ? "e" : "",
);
const personAltNamesLabels = computed(() =>
person.altNames.map((a) => (a ? a.label : "")),
);
const queryItems = computed(() =>
props.query ? props.query.split(" ") : null,
);
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é");
}
}
function loadData() {
getPerson(props.id).then((p) => {
Object.assign(person, p);
});
}
function onAltNameInput(event) {
const key = event.target.id;
const label = event.target.value;
let updateAltNames = person.altNames.filter((a) => a.key !== key);
updateAltNames.push({ key: key, label: label });
person.altNames = updateAltNames;
}
function addQueryItem(field, queryItem) {
switch (field) {
case "lastName":
person.lastName = person.lastName
? (person.lastName += ` ${queryItem}`)
: queryItem;
break;
case "firstName":
person.firstName = person.firstName
? (person.firstName += ` ${queryItem}`)
: queryItem;
break;
}
}
function submitNewAddress(payload) {
person.addressId = payload.addressId;
}
onMounted(() => {
getPersonAltNames().then((altNames) => {
config.altNames = altNames;
});
getCivilities().then((civilities) => {
if ("results" in civilities) {
config.civilities = civilities.results;
}
});
getGenders().then((genders) => {
if ("results" in genders) {
config.genders = genders.results;
}
});
if (props.action !== "create") {
loadData();
} else {
getCentersForPersonCreation().then((params) => {
config.centers = params.centers.filter((c) => c.isActive);
showCenters.value = params.showCenters;
if (showCenters.value && config.centers.length === 1) {
person.center = config.centers[0];
}
});
}
});
defineExpose(genderClass, genderTranslation, feminized, birthDate);
</script>
<style lang="scss" scoped>
div.flex-table {
div.item-bloc {
div.item-row {
div.item-col:last-child {
justify-content: flex-start;
}
}
}
}
dl {
dd {
margin-left: 1em;
}
}
div.form-check {
label {
margin-left: 0.5em !important;
}
}
</style>