Merge branch 'ticket-app-master' into 1849-1848-1920-1921-fix-bugs

This commit is contained in:
Boris Waaub
2025-12-09 17:21:13 +01:00
69 changed files with 4643 additions and 4700 deletions

View File

@@ -11,7 +11,6 @@
"@hotwired/stimulus": "^3.0.0",
"@luminateone/eslint-baseline": "^1.0.9",
"@symfony/stimulus-bridge": "^3.2.0",
"@symfony/ux-translator": "file:vendor/symfony/ux-translator/assets",
"@symfony/webpack-encore": "^4.1.0",
"@tsconfig/node20": "^20.1.4",
"@types/dompurify": "^3.0.5",

View File

@@ -49,9 +49,7 @@
</div>
<div class="col-8">
<div v-if="actionIsLoading === true">
<i
class="chill-green fa fa-circle-o-notch fa-spin fa-lg"
></i>
<i class="chill-green fa fa-circle-o-notch fa-spin fa-lg"></i>
</div>
<span
@@ -64,8 +62,7 @@
<template
v-else-if="
socialActionsList.length > 0 &&
(socialIssuesSelected.length ||
socialActionsSelected.length)
(socialIssuesSelected.length || socialActionsSelected.length)
"
>
<div
@@ -88,9 +85,7 @@
</template>
<span
v-else-if="
actionAreLoaded && socialActionsList.length === 0
"
v-else-if="actionAreLoaded && socialActionsList.length === 0"
class="inline-choice chill-no-data-statement mt-3"
>
{{ trans(ACTIVITY_SOCIAL_ACTION_LIST_EMPTY) }}
@@ -190,8 +185,7 @@ export default {
/* Add in list the issues already associated (if not yet listed) */
this.socialIssuesSelected.forEach((issue) => {
if (
this.socialIssuesList.filter((i) => i.id === issue.id)
.length !== 1
this.socialIssuesList.filter((i) => i.id === issue.id).length !== 1
) {
this.$store.commit("addIssueInList", issue);
}

View File

@@ -47,7 +47,8 @@ span.badge {
word-wrap: break-word;
word-break: break-word;
display: inline-block;
max-width: 100%;margin-bottom: 5px;
max-width: 100%;
margin-bottom: 5px;
margin-right: 1em;
text-align: left;
line-height: 1.2em;

View File

@@ -45,8 +45,10 @@ span.badge {
word-wrap: break-word;
word-break: break-word;
display: inline-block;
max-width: 100%;margin-bottom: 5px;
margin-right: 1em;text-align: left;
max-width: 100%;
margin-bottom: 5px;
margin-right: 1em;
text-align: left;
&::before {
position: absolute;

View File

@@ -61,11 +61,7 @@
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select
v-model="slotDuration"
id="slotDuration"
class="form-select"
>
<select v-model="slotDuration" id="slotDuration" class="form-select">
<option value="00:05:00">5 minutes</option>
<option value="00:10:00">10 minutes</option>
<option value="00:15:00">15 minutes</option>
@@ -74,11 +70,7 @@
<option value="00:60:00">60 minutes</option>
</select>
<label class="input-group-text" for="slotMinTime">De</label>
<select
v-model="slotMinTime"
id="slotMinTime"
class="form-select"
>
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
<option value="00:00:00">0h</option>
<option value="01:00:00">1h</option>
<option value="02:00:00">2h</option>
@@ -94,11 +86,7 @@
<option value="12:00:00">12h</option>
</select>
<label class="input-group-text" for="slotMaxTime">À</label>
<select
v-model="slotMaxTime"
id="slotMaxTime"
class="form-select"
>
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
<option value="12:00:00">12h</option>
<option value="13:00:00">13h</option>
<option value="14:00:00">14h</option>
@@ -126,9 +114,7 @@
v-model="hideWeekends"
/>
</span>
<label
for="showHideWE"
class="form-check-label input-group-text"
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
</div>
@@ -144,9 +130,7 @@
<b v-else-if="arg.event.extendedProps.is === 'range'"
>{{ arg.timeText }}
{{ arg.event.extendedProps.locationName }}
<small>{{
arg.event.extendedProps.userLabel
}}</small></b
<small>{{ arg.event.extendedProps.userLabel }}</small></b
>
<b v-else-if="arg.event.extendedProps.is === 'current'"
>{{ arg.timeText }} {{ $t("current_selected") }}
@@ -154,9 +138,7 @@
<b v-else-if="arg.event.extendedProps.is === 'local'">{{
arg.event.title
}}</b>
<b v-else
>{{ arg.timeText }} {{ $t("current_selected") }}
</b>
<b v-else>{{ arg.timeText }} {{ $t("current_selected") }} </b>
</span>
</template>
</FullCalendar>
@@ -270,9 +252,7 @@ export default {
this.$store.state.activity.endDate !== null)
) {
if (
!window.confirm(
this.$t("change_main_user_will_reset_event_data"),
)
!window.confirm(this.$t("change_main_user_will_reset_event_data"))
) {
return;
}
@@ -280,13 +260,9 @@ export default {
// add the previous user, if any, in the previous user list (in use for suggestion)
if (null !== this.$store.getters.getMainUser) {
const suggestedUids = new Set(
this.$data.previousUser.map((u) => u.id),
);
const suggestedUids = new Set(this.$data.previousUser.map((u) => u.id));
if (!suggestedUids.has(this.$store.getters.getMainUser.id)) {
this.$data.previousUser.push(
this.$store.getters.getMainUser,
);
this.$data.previousUser.push(this.$store.getters.getMainUser);
}
}
@@ -316,8 +292,7 @@ export default {
// show an alert if changing mainUser
if (
(this.$store.getters.getMainUser !== null &&
this.$store.state.me.id !==
this.$store.getters.getMainUser.id) ||
this.$store.state.me.id !== this.$store.getters.getMainUser.id) ||
this.$store.getters.getMainUser === null
) {
if (!window.confirm(this.$t("will_change_main_user_for_me"))) {
@@ -361,9 +336,7 @@ export default {
this.$store.getters.getMainUser.id
) {
if (
!window.confirm(
this.$t("this_calendar_range_will_change_main_user"),
)
!window.confirm(this.$t("this_calendar_range_will_change_main_user"))
) {
return;
}

View File

@@ -23,11 +23,7 @@
<label class="input-group-text" for="slotDuration"
>Durée des créneaux</label
>
<select
v-model="slotDuration"
id="slotDuration"
class="form-select"
>
<select v-model="slotDuration" id="slotDuration" class="form-select">
<option value="00:05:00">5 minutes</option>
<option value="00:10:00">10 minutes</option>
<option value="00:15:00">15 minutes</option>
@@ -36,11 +32,7 @@
<option value="00:60:00">60 minutes</option>
</select>
<label class="input-group-text" for="slotMinTime">De</label>
<select
v-model="slotMinTime"
id="slotMinTime"
class="form-select"
>
<select v-model="slotMinTime" id="slotMinTime" class="form-select">
<option value="00:00:00">0h</option>
<option value="01:00:00">1h</option>
<option value="02:00:00">2h</option>
@@ -56,11 +48,7 @@
<option value="12:00:00">12h</option>
</select>
<label class="input-group-text" for="slotMaxTime">À</label>
<select
v-model="slotMaxTime"
id="slotMaxTime"
class="form-select"
>
<select v-model="slotMaxTime" id="slotMaxTime" class="form-select">
<option value="12:00:00">12h</option>
<option value="13:00:00">13h</option>
<option value="14:00:00">14h</option>
@@ -88,9 +76,7 @@
v-model="showWeekends"
/>
</span>
<label
for="showHideWE"
class="form-check-label input-group-text"
<label for="showHideWE" class="form-check-label input-group-text"
>Week-ends</label
>
</div>
@@ -100,9 +86,7 @@
<FullCalendar :options="calendarOptions" ref="calendarRef">
<template v-slot:eventContent="{ event }: { event: EventApi }">
<span :class="eventClasses">
<b v-if="event.extendedProps.is === 'remote'">{{
event.title
}}</b>
<b v-if="event.extendedProps.is === 'remote'">{{ event.title }}</b>
<b v-else-if="event.extendedProps.is === 'range'"
>{{ formatDate(event.startStr, "time") }} -
{{ formatDate(event.endStr, "time") }}:
@@ -132,11 +116,7 @@
<h6 class="chill-red">{{ $t("copy_range_from_to") }}</h6>
</div>
<div class="col-xs-12 col-sm-9 col-md-2">
<select
v-model="dayOrWeek"
id="dayOrWeek"
class="form-select"
>
<select v-model="dayOrWeek" id="dayOrWeek" class="form-select">
<option value="day">{{ $t("from_day_to_day") }}</option>
<option value="week">
{{ $t("from_week_to_week") }}
@@ -145,27 +125,16 @@
</div>
<template v-if="dayOrWeek === 'day'">
<div class="col-xs-12 col-sm-3 col-md-3">
<input
class="form-control"
type="date"
v-model="copyFrom"
/>
<input class="form-control" type="date" v-model="copyFrom" />
</div>
<div class="col-xs-12 col-sm-1 col-md-1 copy-chevron">
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<input
class="form-control"
type="date"
v-model="copyTo"
/>
<input class="form-control" type="date" v-model="copyTo" />
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button
class="btn btn-action float-end"
@click="copyDay"
>
<button class="btn btn-action float-end" @click="copyDay">
{{ $t("copy_range") }}
</button>
</div>
@@ -177,11 +146,7 @@
id="copyFromWeek"
class="form-select"
>
<option
v-for="w in lastWeeks"
:value="w.value"
:key="w.value"
>
<option v-for="w in lastWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
@@ -190,25 +155,14 @@
<i class="fa fa-angle-double-right"></i>
</div>
<div class="col-xs-12 col-sm-3 col-md-3">
<select
v-model="copyToWeek"
id="copyToWeek"
class="form-select"
>
<option
v-for="w in nextWeeks"
:value="w.value"
:key="w.value"
>
<select v-model="copyToWeek" id="copyToWeek" class="form-select">
<option v-for="w in nextWeeks" :value="w.value" :key="w.value">
{{ w.text }}
</option>
</select>
</div>
<div class="col-xs-12 col-sm-5 col-md-1">
<button
class="btn btn-action float-end"
@click="copyWeek"
>
<button class="btn btn-action float-end" @click="copyWeek">
{{ $t("copy_range") }}
</button>
</div>

View File

@@ -46,8 +46,7 @@ export interface StoredObjectVersionCreated extends StoredObjectVersion {
persisted: false;
}
export interface StoredObjectVersionPersisted
extends StoredObjectVersionCreated {
export interface StoredObjectVersionPersisted extends StoredObjectVersionCreated {
version: number;
id: number;
createdAt: DateTime | null;
@@ -61,8 +60,7 @@ export interface StoredObjectStatusChange {
type: string;
}
export interface StoredObjectVersionWithPointInTime
extends StoredObjectVersionPersisted {
export interface StoredObjectVersionWithPointInTime extends StoredObjectVersionPersisted {
"point-in-times": StoredObjectPointInTime[];
"from-restored": StoredObjectVersionPersisted | null;
}

View File

@@ -49,8 +49,7 @@ const isRestored = computed<boolean>(
);
const isDuplicated = computed<boolean>(
() =>
props.version.version === 0 && null !== props.version["from-restored"],
() => props.version.version === 0 && null !== props.version["from-restored"],
);
const classes = computed<{
@@ -70,16 +69,9 @@ const classes = computed<{
<div :class="classes">
<div
class="col-12 tags"
v-if="
isCurrent ||
isKeptBeforeConversion ||
isRestored ||
isDuplicated
"
>
<span class="badge bg-success" v-if="isCurrent"
>Version actuelle</span
v-if="isCurrent || isKeptBeforeConversion || isRestored || isDuplicated"
>
<span class="badge bg-success" v-if="isCurrent">Version actuelle</span>
<span class="badge bg-info" v-if="isKeptBeforeConversion"
>Conservée avant conversion dans un autre format</span
>
@@ -96,21 +88,17 @@ const classes = computed<{
<span
><strong>&nbsp;#{{ version.version + 1 }}&nbsp;</strong></span
>
<template
v-if="version.createdBy !== null && version.createdAt !== null"
<template v-if="version.createdBy !== null && version.createdAt !== null"
><strong v-if="version.version == 0">créé par</strong
><strong v-else>modifié par</strong>
<span class="badge-user"
><UserRenderBoxBadge
:user="version.createdBy"
></UserRenderBoxBadge
><UserRenderBoxBadge :user="version.createdBy"></UserRenderBoxBadge
></span>
<strong>à</strong>
{{
$d(ISOToDatetime(version.createdAt.datetime8601), "long")
}}</template
><template
v-if="version.createdBy === null && version.createdAt !== null"
><template v-if="version.createdBy === null && version.createdAt !== null"
><strong v-if="version.version == 0">Créé le</strong
><strong v-else>modifié le</strong>
{{

View File

@@ -61,9 +61,7 @@ export const ISOToDatetime = (str: string | null): Date | null => {
[time, timezone] = times.split(times.charAt(8)),
[hours, minutes, seconds] = time.split(":").map((s) => parseInt(s));
if ("0000" === timezone) {
return new Date(
Date.UTC(year, month - 1, date, hours, minutes, seconds),
);
return new Date(Date.UTC(year, month - 1, date, hours, minutes, seconds));
}
return new Date(year, month - 1, date, hours, minutes, seconds);
@@ -158,9 +156,7 @@ export const intervalISOToDays = (str: string | null): number | null => {
vstring = "";
break;
default:
throw Error(
"this character should not appears: " + str.charAt(i),
);
throw Error("this character should not appears: " + str.charAt(i));
}
}
@@ -180,4 +176,3 @@ export function getTimezoneOffsetString(date: Date, timeZone: string): string {
return `${sign}${hours}:${minutes}`;
}

View File

@@ -3,7 +3,7 @@ import {
Scope,
ValidationExceptionInterface,
ValidationProblemFromMap,
ViolationFromMap
ViolationFromMap,
} from "../../types";
export type body = Record<string, boolean | string | number | null>;
@@ -68,7 +68,10 @@ export class ValidationException<
this.byProperty = problem.violations.reduce(
(acc, v) => {
const key = v.propertyPath.replace('/\[\d+\]$/', "") as Extract<keyof M, string>;
const key = v.propertyPath.replace("/\[\d+\]$/", "") as Extract<
keyof M,
string
>;
(acc[key] ||= []).push(v.title);
return acc;
},
@@ -80,19 +83,18 @@ export class ValidationException<
}
}
violationsByNormalizedProperty(property: Extract<keyof M, string>): ViolationFromMap<M>[] {
return this.violationsList.filter((v) => v.propertyPath.replace(/\[\d+\]$/, "") === property);
violationsByNormalizedProperty(
property: Extract<keyof M, string>,
): ViolationFromMap<M>[] {
return this.violationsList.filter(
(v) => v.propertyPath.replace(/\[\d+\]$/, "") === property,
);
}
violationsByNormalizedPropertyAndParams<
P extends Extract<keyof M, string>,
K extends Extract<keyof M[P], string>
>(
property: P,
param: K,
param_value: M[P][K]
): ViolationFromMap<M>[]
{
K extends Extract<keyof M[P], string>,
>(property: P, param: K, param_value: M[P][K]): ViolationFromMap<M>[] {
const list = this.violationsByNormalizedProperty(property);
return list.filter(
@@ -101,7 +103,7 @@ export class ValidationException<
// `with_parameter in v.parameters` check indexing
param in v.parameters &&
// the cast is safe, because we have overloading that bind the types
(v.parameters as M[P])[param] === param_value
(v.parameters as M[P])[param] === param_value,
);
}
}
@@ -110,9 +112,9 @@ export class ValidationException<
* Check that the exception is a ValidationExceptionInterface
* @param x
*/
export function isValidationException<M extends Record<string, Record<string, string|number>>>(
x: unknown,
): x is ValidationExceptionInterface<M> {
export function isValidationException<
M extends Record<string, Record<string, string | number>>,
>(x: unknown): x is ValidationExceptionInterface<M> {
return (
x instanceof ValidationException ||
(typeof x === "object" &&
@@ -147,8 +149,7 @@ export interface AccessExceptionInterface extends TransportExceptionInterface {
violations: string[];
}
export interface NotFoundExceptionInterface
extends TransportExceptionInterface {
export interface NotFoundExceptionInterface extends TransportExceptionInterface {
name: "NotFoundException";
}
@@ -159,8 +160,7 @@ export interface ServerExceptionInterface extends TransportExceptionInterface {
body: string;
}
export interface ConflictHttpExceptionInterface
extends TransportExceptionInterface {
export interface ConflictHttpExceptionInterface extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
}

View File

@@ -7,8 +7,7 @@ import {Gender, GenderTranslation} from "ChillMainAssets/types";
* @return {GenderTranslation} Returns the gender translation string corresponding to the provided gender,
* or "unknown" if the gender is null.
*/
export function toGenderTranslation(gender: Gender|null): GenderTranslation
{
export function toGenderTranslation(gender: Gender | null): GenderTranslation {
if (null === gender) {
return "unknown";
}

View File

@@ -38,7 +38,6 @@ export interface SetCivility {
id: number;
}
/**
* Gender translation.
*
@@ -380,13 +379,16 @@ export type DynamicKeys<M extends Record<string, Record<string, unknown>>> =
type NormalizeKey<K extends string> = K extends `${infer B}[${number}]` ? B : K;
export type ViolationFromMap<M extends Record<string, Record<string, unknown>>> = {
[K in DynamicKeys<M> & string]: { // <- note le "& string" ici
export type ViolationFromMap<
M extends Record<string, Record<string, unknown>>,
> = {
[K in DynamicKeys<M> & string]: {
// <- note le "& string" ici
propertyPath: K;
title: string;
parameters?: M[NormalizeKey<K>];
type?: string;
}
};
}[DynamicKeys<M> & string];
export type ValidationProblemFromMap<
@@ -418,15 +420,17 @@ export interface ValidationExceptionInterface<
/** Indexing by property (useful for display by field) */
byProperty: Record<Extract<keyof M, string>, string[]>;
violationsByNormalizedProperty(property: Extract<keyof M, string>): ViolationFromMap<M>[];
violationsByNormalizedProperty(
property: Extract<keyof M, string>,
): ViolationFromMap<M>[];
violationsByNormalizedPropertyAndParams<
P extends Extract<keyof M, string>,
K extends Extract<keyof M[P], string>
K extends Extract<keyof M[P], string>,
>(
property: P,
param: K,
param_value: M[P][K]
param_value: M[P][K],
): ViolationFromMap<M>[];
}
@@ -435,8 +439,7 @@ export interface AccessExceptionInterface extends TransportExceptionInterface {
violations: string[];
}
export interface NotFoundExceptionInterface
extends TransportExceptionInterface {
export interface NotFoundExceptionInterface extends TransportExceptionInterface {
name: "NotFoundException";
}
@@ -447,8 +450,7 @@ export interface ServerExceptionInterface extends TransportExceptionInterface {
body: string;
}
export interface ConflictHttpExceptionInterface
extends TransportExceptionInterface {
export interface ConflictHttpExceptionInterface extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
}
@@ -496,16 +498,16 @@ export interface TabDefinition {
counter: () => number;
}
export type CreateComponentConfigGeneral = {
action: 'create';
export interface CreateComponentConfigGeneral {
action: "create";
allowedTypes: CreatableEntityType[];
query: string;
parent: null;
}
export type CreateComponentThirdPartyAddContact = {
action: 'addContact';
allowedTypes: readonly ['thirdparty'];
export interface CreateComponentThirdPartyAddContact {
action: "addContact";
allowedTypes: readonly ["thirdparty"];
query: string;
parent: ThirdpartyCompany;
}
@@ -513,8 +515,9 @@ export type CreateComponentThirdPartyAddContact = {
/**
* Configuration for the CreateModal and Create component
*/
export type CreateComponentConfig = CreateComponentConfigGeneral | CreateComponentThirdPartyAddContact;
export type CreateComponentConfig =
| CreateComponentConfigGeneral
| CreateComponentThirdPartyAddContact;
/**
* Possible states for the WaitingScreen Component.

View File

@@ -69,10 +69,9 @@ const props = withDefaults(defineProps<CreateComponentConfig>(), {
parent: null,
});
const emit =
defineEmits<{
(e: "onPersonCreated", payload: { person: Person }): void,
(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }): void,
const emit = defineEmits<{
(e: "onPersonCreated", payload: { person: Person }): void;
(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }): void;
}>();
const type = ref<CreatableEntityType | null>(null);
@@ -106,7 +105,7 @@ const containsThirdParty = computed<boolean>(() =>
props.allowedTypes.includes("thirdparty"),
);
const containsPerson = computed<boolean>(() => {
if (props.action === 'addContact') {
if (props.action === "addContact") {
return false;
}
return props.allowedTypes.includes("person");
@@ -115,7 +114,10 @@ const containsPerson = computed<boolean>(() => {
function save(): void {
if (radioType.value === "person" && castPerson.value !== null) {
castPerson.value.postPerson();
} else if (radioType.value === "thirdparty" && castThirdparty.value !== null) {
} else if (
radioType.value === "thirdparty" &&
castThirdparty.value !== null
) {
castThirdparty.value.postThirdParty();
}
}

View File

@@ -24,9 +24,13 @@ const onPersonCreated = ({person}: {person: Person}): void => {
emit("onPersonCreated", { person });
};
const onThirdPartyCreated = ({thirdParty}: {thirdParty: Thirdparty}): void => {
const onThirdPartyCreated = ({
thirdParty,
}: {
thirdParty: Thirdparty;
}): void => {
emit("onThirdPartyCreated", { thirdParty: thirdParty });
}
};
function save(): void {
console.log("save from CreateModal");

View File

@@ -177,14 +177,19 @@ const props = withDefaults(defineProps<OnTheFlyComponentProps>(), {
query: "",
});
const emit = defineEmits<{
(e: "saveFormOnTheFly", payload: { type: string | undefined; data: any }): void;
}>();
const emit =
defineEmits<
(
e: "saveFormOnTheFly",
payload: { type: string | undefined; data: any },
) => void
>();
type castEditPersonType = InstanceType<typeof PersonEdit>;
type castEditThirdPartyType = InstanceType<typeof ThirdPartyEdit>;
const castEditPerson = useTemplateRef<castEditPersonType>('castEditPerson')
const castEditThirdParty = useTemplateRef<castEditThirdPartyType>('castEditThirdParty');
const castEditPerson = useTemplateRef<castEditPersonType>("castEditPerson");
const castEditThirdParty =
useTemplateRef<castEditThirdPartyType>("castEditThirdParty");
const modal = ref<{ showModal: boolean; modalDialogClass: string }>({
showModal: false,
@@ -234,35 +239,35 @@ const titleAction = computed<string>(() => {
const titleCreate = computed<string>(() => {
if (typeof props.allowedTypes === "undefined") {
return trans(ONTHEFLY_CREATE_TITLE_DEFAULT)
return trans(ONTHEFLY_CREATE_TITLE_DEFAULT);
}
return props.allowedTypes.every((t: EntityType) => t === "person")
? (trans(ONTHEFLY_CREATE_TITLE_PERSON))
? trans(ONTHEFLY_CREATE_TITLE_PERSON)
: props.allowedTypes.every((t: EntityType) => t === "thirdparty")
? (trans(ONTHEFLY_CREATE_TITLE_THIRDPARTY))
: (trans(ONTHEFLY_CREATE_TITLE_DEFAULT));
? trans(ONTHEFLY_CREATE_TITLE_THIRDPARTY)
: trans(ONTHEFLY_CREATE_TITLE_DEFAULT);
});
const titleModal = computed<string>(() => {
switch (props.action) {
case "show":
if (props.type == "person") {
return trans(ONTHEFLY_SHOW_PERSON)
return trans(ONTHEFLY_SHOW_PERSON);
} else if (props.type == "thirdparty") {
return trans(ONTHEFLY_SHOW_THIRDPARTY)
return trans(ONTHEFLY_SHOW_THIRDPARTY);
}
break;
case "edit":
if (props.type == "person") {
return trans(ONTHEFLY_EDIT_PERSON)
return trans(ONTHEFLY_EDIT_PERSON);
} else if (props.type == "thirdparty") {
return trans(ONTHEFLY_EDIT_THIRDPARTY)
return trans(ONTHEFLY_EDIT_THIRDPARTY);
}
break;
case "create":
return titleCreate.value;
case "addContact":
return trans(THIRDPARTY_ADDCONTACT_TITLE)
return trans(THIRDPARTY_ADDCONTACT_TITLE);
default:
break;
}
@@ -311,7 +316,10 @@ function openModal(): void {
modal.value.showModal = true;
}
function buildLocation(id: string | number | undefined, type: EntityType | undefined): string | undefined {
function buildLocation(
id: string | number | undefined,
type: EntityType | undefined,
): string | undefined {
if (type === "person") {
return encodeURI(`/fr/person/${id}/general${getReturnPath.value}`);
} else if (type === "thirdparty") {
@@ -324,17 +332,16 @@ async function saveAction() {
if (props.type === "person") {
const person = await castEditPerson.value?.postPerson();
if (null !== person) {
emit("saveFormOnTheFly", {type: props.type, data: person})
emit("saveFormOnTheFly", { type: props.type, data: person });
}
} else if (props.type === 'thirdparty') {
} else if (props.type === "thirdparty") {
const thirdParty = await castEditThirdParty.value?.postThirdParty();
if (null !== thirdParty) {
emit("saveFormOnTheFly", {type: props.type, data: thirdParty })
emit("saveFormOnTheFly", { type: props.type, data: thirdParty });
}
}
}
defineExpose({
openModal,
closeModal,

View File

@@ -19,7 +19,8 @@
>
{{ p.text }}
</span>
<span v-else
<span
v-else
:class="getBadgeClass(p)"
class="chill_denomination"
:style="getBadgeStyle(p)"
@@ -61,12 +62,14 @@
@click="addNewSuggested(s)"
style="margin: 0"
>
<span v-if="!isEntityHousehold(s)" :class="getBadgeClass(s)" :style="getBadgeStyle(s)">
<span
v-if="!isEntityHousehold(s)"
:class="getBadgeClass(s)"
:style="getBadgeStyle(s)"
>
{{ s.text }}
</span>
<span v-else>
Ménage n°{{ s.id }}
</span>
<span v-else> Ménage n°{{ s.id }} </span>
</li>
</ul>
</div>
@@ -85,7 +88,8 @@ import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import {
Entities,
EntitiesOrMe,
EntityType, isEntityHousehold,
EntityType,
isEntityHousehold,
SearchOptions,
Suggestion,
} from "ChillPersonAssets/types";

View File

@@ -47,9 +47,7 @@ const canRemove = computed<boolean>((): boolean => {
</p>
<div v-else class="flex-table">
<div v-for="a in notNullAttachments" :key="a.id" class="item-bloc">
<generic-doc-item-box
:generic-doc="a.genericDoc"
></generic-doc-item-box>
<generic-doc-item-box :generic-doc="a.genericDoc"></generic-doc-item-box>
<div class="item-row separator">
<ul class="record_actions">
<li>

View File

@@ -49,9 +49,8 @@ onMounted(async () => {
genericDocs.value = fetchedGenericDocs.filter(
(doc) =>
!documentClasses.includes(
props.workflow?.relatedEntityClass || "",
) || props.workflow?.relatedEntityId !== doc.identifiers.id,
!documentClasses.includes(props.workflow?.relatedEntityClass || "") ||
props.workflow?.relatedEntityId !== doc.identifiers.id,
);
loaded.value = true;
});
@@ -124,9 +123,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
return false;
}
return needles.every((n: string) =>
title.toLowerCase().includes(n),
);
return needles.every((n: string) => title.toLowerCase().includes(n));
})
.filter((genericDoc: GenericDocForAccompanyingPeriod) => {
if (placesFilter.value.length === 0) {
@@ -167,8 +164,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
aria-controls="filterOrderCollapse"
>
<strong
><i class="fa fa-fw fa-filter"></i>Filtrer la
liste</strong
><i class="fa fa-fw fa-filter"></i>Filtrer la liste</strong
>
</button>
</h2>
@@ -193,10 +189,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
placeholder="Chercher dans la liste"
class="form-control"
/>
<button
type="submit"
class="btn btn-misc"
>
<button type="submit" class="btn btn-misc">
<i class="fa fa-search"></i>
</button>
</div>
@@ -204,9 +197,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
</div>
<div class="row my-2">
<legend
class="col-form-label col-sm-4 required"
>
<legend class="col-form-label col-sm-4 required">
Date du document
</legend>
<div class="col-sm-8 pt-1">
@@ -232,15 +223,9 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
</div>
<div class="row my-2">
<div class="col-sm-4 col-form-label">
Filtrer par
</div>
<div class="col-sm-4 col-form-label">Filtrer par</div>
<div class="col-sm-8 pt-2">
<div
class="form-check"
v-for="p in availablePlaces"
:key="p"
>
<div class="form-check" v-for="p in availablePlaces" :key="p">
<input
type="checkbox"
v-model="placesFilter"
@@ -248,9 +233,7 @@ const filteredDocuments = computed<GenericDocForAccompanyingPeriod[]>(() => {
class="form-check-input"
:value="p"
/>
<label class="form-check-label">{{
placeTrans(p)
}}</label>
<label class="form-check-label">{{ placeTrans(p) }}</label>
</div>
</div>
</div>

View File

@@ -100,8 +100,8 @@ defineExpose({ openModal, closeModal });
@click="onConfirm"
>
<template v-if="numberOfPicked > 1">
<i class="fa fa-plus"></i> Ajouter
{{ numberOfPicked }} pièces jointes
<i class="fa fa-plus"></i> Ajouter {{ numberOfPicked }} pièces
jointes
</template>
<template v-else>
<i class="fa fa-plus"></i> Ajouter une pièce jointe

View File

@@ -5,10 +5,7 @@
:class="{ active: subscriberFinal }"
type="button"
@click="
subscribeTo(
subscriberFinal ? 'unsubscribe' : 'subscribe',
'final',
)
subscribeTo(subscriberFinal ? 'unsubscribe' : 'subscribe', 'final')
"
>
<i
@@ -21,12 +18,7 @@
class="btn btn-outline-primary text-start d-flex align-items-center"
:class="{ active: subscriberStep }"
type="button"
@click="
subscribeTo(
subscriberStep ? 'unsubscribe' : 'subscribe',
'step',
)
"
@click="subscribeTo(subscriberStep ? 'unsubscribe' : 'subscribe', 'step')"
>
<i
class="fa fa-fw me-2"

View File

@@ -23,10 +23,7 @@
<slot name="body"></slot>
</div>
<div class="modal-footer" v-if="!hideFooter">
<button
class="btn btn-cancel"
@click="emits('close')"
>
<button class="btn btn-cancel" @click="emits('close')">
{{ trans(MODAL_ACTION_CLOSE) }}
</button>
<slot name="footer"></slot>
@@ -53,10 +50,10 @@ import { trans, MODAL_ACTION_CLOSE } from "translator";
import { defineProps } from "vue";
defineSlots<{
header?: () => any,
body?: () => any,
footer?: () => any,
"body-head"?: () => any,
header?: () => any;
body?: () => any;
footer?: () => any;
"body-head"?: () => any;
}>();
export interface ModalProps {

View File

@@ -1,7 +1,9 @@
import { ref } from "vue";
import { ValidationExceptionInterface } from "ChillMainAssets/types";
export function useViolationList<T extends Record<string, Record<string, string>>>() {
export function useViolationList<
T extends Record<string, Record<string, string>>,
>() {
type ViolationKey = Extract<keyof T, string>;
const violationsList = ref<ValidationExceptionInterface<T> | null>(null);
@@ -9,15 +11,15 @@ export function useViolationList<T extends Record<string, Record<string, string>
if (null === violationsList.value) {
return [];
}
const r = violationsList.value.violationsByNormalizedProperty(property).map((v) => v.title);
const r = violationsList.value
.violationsByNormalizedProperty(property)
.map((v) => v.title);
return r;
}
function violationTitlesWithParameter<
P extends ViolationKey,
Param extends Extract<keyof T[P], string>
Param extends Extract<keyof T[P], string>,
>(
property: P,
with_parameter: Param,
@@ -26,26 +28,38 @@ export function useViolationList<T extends Record<string, Record<string, string>
if (violationsList.value === null) {
return [];
}
return violationsList.value.violationsByNormalizedPropertyAndParams(property, with_parameter, with_parameter_value)
return violationsList.value
.violationsByNormalizedPropertyAndParams(
property,
with_parameter,
with_parameter_value,
)
.map((v) => v.title);
}
function hasViolation<P extends ViolationKey>(property: P): boolean {
return violationTitles(property).length > 0;
}
function hasViolationWithParameter<
P extends ViolationKey,
Param extends Extract<keyof T[P], string>
Param extends Extract<keyof T[P], string>,
>(
property: P,
with_parameter: Param,
with_parameter_value: T[P][Param],
): boolean {
return violationTitlesWithParameter(property, with_parameter, with_parameter_value).length > 0;
return (
violationTitlesWithParameter(
property,
with_parameter,
with_parameter_value,
).length > 0
);
}
function setValidationException<V extends ValidationExceptionInterface<T>>(validationException: V): void {
function setValidationException<V extends ValidationExceptionInterface<T>>(
validationException: V,
): void {
violationsList.value = validationException;
}
@@ -53,5 +67,12 @@ export function useViolationList<T extends Record<string, Record<string, string>
violationsList.value = null;
}
return {violationTitles, violationTitlesWithParameter, setValidationException, cleanException, hasViolationWithParameter, hasViolation};
return {
violationTitles,
violationTitlesWithParameter,
setValidationException,
cleanException,
hasViolationWithParameter,
hasViolation,
};
}

View File

@@ -14,7 +14,8 @@ import {
DateTimeWrite,
SetGender,
SetCenter,
SetCivility, Gender,
SetCivility,
Gender,
} from "ChillMainAssets/types";
import { StoredObject } from "ChillDocStoreAssets/types";
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
@@ -299,7 +300,7 @@ export interface AccompanyingPeriodWorkGoal {
}
export interface AccompanyingPeriodWorkEvaluation {
type: 'accompanying_period_work_evaluation';
type: "accompanying_period_work_evaluation";
accompanyingPeriodWork: AccompanyingPeriodWork | null;
comment: string;
createdAt: DateTime | null;
@@ -432,7 +433,7 @@ export type EntityType =
| "user"
| "household";
export type Entities = (UserGroup | User | Person | Thirdparty | Household);
export type Entities = UserGroup | User | Person | Thirdparty | Household;
export function isEntityHousehold(e: Entities): e is Household {
return e.type === "household";
@@ -440,25 +441,34 @@ export function isEntityHousehold(e: Entities): e is Household {
export type EntitiesOrMe = "me" | Entities;
// Type guards to discriminate Suggestions by their result kind
export function isSuggestionForUserGroup(s: Suggestion): s is Suggestion & { result: UserGroup } {
export function isSuggestionForUserGroup(
s: Suggestion,
): s is Suggestion & { result: UserGroup } {
return (s as any)?.result?.type === "user_group";
}
export function isSuggestionForUser(s: Suggestion): s is Suggestion & { result: User } {
export function isSuggestionForUser(
s: Suggestion,
): s is Suggestion & { result: User } {
return (s as any)?.result?.type === "user";
}
export function isSuggestionForPerson(s: Suggestion): s is Suggestion & { result: Person } {
export function isSuggestionForPerson(
s: Suggestion,
): s is Suggestion & { result: Person } {
return (s as any)?.result?.type === "person";
}
export function isSuggestionForThirdParty(s: Suggestion): s is Suggestion & { result: Thirdparty } {
export function isSuggestionForThirdParty(
s: Suggestion,
): s is Suggestion & { result: Thirdparty } {
return (s as any)?.result?.type === "thirdparty";
}
export function isSuggestionForHousehold(s: Suggestion): s is Suggestion & { result: Household } {
export function isSuggestionForHousehold(
s: Suggestion,
): s is Suggestion & { result: Household } {
return (s as any)?.result?.type === "household";
}
@@ -482,7 +492,7 @@ export interface SearchPagination {
export interface Search {
count: number;
pagination: SearchPagination;
results: {relevance: number, result: Entities}[];
results: { relevance: number; result: Entities }[];
}
export interface SearchOptions {
@@ -498,7 +508,11 @@ export interface SearchOptions {
};
}
type PersonIdentifierPresence = 'NOT_EDITABLE' | 'ON_EDIT' | 'ON_CREATION' | 'REQUIRED';
type PersonIdentifierPresence =
| "NOT_EDITABLE"
| "ON_EDIT"
| "ON_CREATION"
| "REQUIRED";
export interface PersonIdentifierWorker {
type: "person_identifier_worker";

View File

@@ -42,9 +42,7 @@
</a>
</li>
<li>
<button
class="btn btn-save"
@click="modal.showModal = true">
<button class="btn btn-save" @click="modal.showModal = true">
{{ $t("confirm.ok") }}
</button>
</li>

View File

@@ -84,8 +84,12 @@
<ul class="record_actions">
<li class="add-persons">
<add-persons
:button-title="trans(ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON)"
:modal-title="trans(ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON)"
:button-title="
trans(ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON)
"
:modal-title="
trans(ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON)
"
:key="addPersons.key"
:options="addPersons.options"
@add-new-persons="addNewPersons"
@@ -108,7 +112,10 @@ import { mapGetters, mapState } from "vuex";
import ParticipationItem from "./PersonsAssociated/ParticipationItem.vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import PersonText from "ChillPersonAssets/vuejs/_components/Entity/PersonText.vue";
import {ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON, trans} from "translator";
import {
ACCOMPANYING_COURSE_PERSONS_ASSOCIATED_ADD_PERSON,
trans,
} from "translator";
export default {
name: "PersonsAssociated",

View File

@@ -55,9 +55,7 @@
<!-- 1. Goals with results that were already selected/saved to the entity -->
<div v-for="g in goalsPicked" :key="g.goal.id">
<div class="item-title" @click="removeGoal(g)">
<span class="removable">{{
localizeString(g.goal.title)
}}</span>
<span class="removable">{{ localizeString(g.goal.title) }}</span>
</div>
<div>
<add-result :goal="g.goal" destination="goal"></add-result>
@@ -90,14 +88,8 @@
<!-- 3. Selector for objectives with results -->
<div class="accordion" id="expandedSuggestions">
<div
v-if="availableForCheckGoal.length > 0"
class="accordion-item"
>
<h2
class="accordion-header"
id="heading_expanded_suggestions"
>
<div v-if="availableForCheckGoal.length > 0" class="accordion-item">
<h2 class="accordion-header" id="heading_expanded_suggestions">
<button
v-if="isExpanded"
class="accordion-button"
@@ -138,10 +130,7 @@
</ul>
</template>
</div>
<p
v-if="goalsPicked.length === 0"
class="chill-no-data-statement"
>
<p v-if="goalsPicked.length === 0" class="chill-no-data-statement">
Aucun objectif associé
</p>
</div>
@@ -188,10 +177,7 @@
</li>
</ul>
</div>
<ul
class="record_actions"
v-if="evaluationsForAction.length > 0"
>
<ul class="record_actions" v-if="evaluationsForAction.length > 0">
<li>
<button
:title="$t('add_an_evaluation')"
@@ -223,10 +209,7 @@
class="me-2 form-check-input"
:id="'person_check' + p.id"
/>
<label
:for="'person_check' + p.id"
class="form-check-label"
>
<label :for="'person_check' + p.id" class="form-check-label">
<person-text :person="p"></person-text>
</label>
</div>
@@ -244,10 +227,7 @@
class="me-2 form-check-input"
:id="'person_check' + p.id"
/>
<label
:for="'person_check' + p.id"
class="form-check-label"
>
<label :for="'person_check' + p.id" class="form-check-label">
<person-text :person="p"></person-text>
</label>
</div>
@@ -310,9 +290,7 @@
<add-persons
ref="handlingThirdPartyPicker"
v-bind:key="handlingThirdPartyPicker.key"
v-bind:buttonTitle="
$t('precise_handling_thirdparty')
"
v-bind:buttonTitle="$t('precise_handling_thirdparty')"
v-bind:modalTitle="$t('choose_a_thirdparty')"
v-bind:options="handlingThirdPartyPicker.options"
@addNewPersons="setHandlingThirdParty"
@@ -458,23 +436,16 @@
>
&nbsp;
</button>
<ul
class="dropdown-menu"
aria-labelledby="btnGroupNotifyButtons"
>
<ul class="dropdown-menu" aria-labelledby="btnGroupNotifyButtons">
<li>
<a
class="dropdown-item"
@click="goToGenerateNotification(true)"
>{{ $t("notification_notify_referrer") }}</a
>
<a class="dropdown-item" @click="goToGenerateNotification(true)">{{
$t("notification_notify_referrer")
}}</a>
</li>
<li>
<a
class="dropdown-item"
@click="goToGenerateNotification(false)"
>{{ $t("notification_notify_any") }}</a
>
<a class="dropdown-item" @click="goToGenerateNotification(false)">{{
$t("notification_notify_any")
}}</a>
</li>
</ul>
</template>
@@ -523,8 +494,7 @@ const i18n = {
available_goals_text:
"Motifs, objectifs et dispositifs disponibles pour ajout :",
results_title: "Orientations - résultats",
results_without_objective:
"Résultats - orientations sans objectifs",
results_without_objective: "Résultats - orientations sans objectifs",
add_objectif: "Ajouter un motif - objectif - dispositif",
add_an_objective: "Ajouter un objectif",
Evaluations: "Évaluations",
@@ -725,10 +695,7 @@ export default {
this.showAddEvaluation = !this.showAddEvaluation;
},
setHandlingThirdParty({ selected, modal }) {
this.$store.commit(
"setHandlingThirdParty",
selected.shift().result,
);
this.$store.commit("setHandlingThirdParty", selected.shift().result);
this.$refs.handlingThirdPartyPicker.resetSearch();
modal.showModal = false;
},
@@ -840,9 +807,7 @@ export default {
});
},
scrollToElement(docAnchorId) {
const documentEl = document.getElementById(
`document_${docAnchorId}`,
);
const documentEl = document.getElementById(`document_${docAnchorId}`);
if (documentEl) {
documentEl.scrollIntoView({ behavior: "smooth" });
}

View File

@@ -6,15 +6,11 @@
class="item-bloc"
v-for="(d, i) in documents"
:key="d.id"
:class="[
parseInt(docAnchorId) === d.id ? 'bg-blink' : 'nothing',
]"
:class="[parseInt(docAnchorId) === d.id ? 'bg-blink' : 'nothing']"
>
<div :id="'document_' + d.id" class="item-row">
<div class="input-group input-group-lg mb-3 row">
<label class="col-sm-3 col-form-label"
>Titre du document:</label
>
<label class="col-sm-3 col-form-label">Titre du document:</label>
<div class="col-sm-9">
<input
class="form-control document-title"
@@ -32,9 +28,7 @@
<p v-if="d.createdBy" class="createdBy">
Créé par {{ d.createdBy.text }}<br />
Le
{{
$d(ISOToDatetime(d.createdAt.datetime), "long")
}}
{{ $d(ISOToDatetime(d.createdAt.datetime), "long") }}
</p>
</div>
</div>
@@ -43,8 +37,7 @@
<ul class="record_actions">
<li
v-if="
d.workflows_availables.length > 0 ||
d.workflows.length > 0
d.workflows_availables.length > 0 || d.workflows.length > 0
"
>
<list-workflow-modal
@@ -52,9 +45,7 @@
:allowCreate="true"
relatedEntityClass="Chill\PersonBundle\Entity\AccompanyingPeriod\AccompanyingPeriodWorkEvaluationDocument"
:relatedEntityId="d.id"
:workflowsAvailables="
d.workflows_availables
"
:workflowsAvailables="d.workflows_availables"
:preventDefaultMoveToGenerate="true"
:goToGenerateWorkflowPayload="{ doc: d }"
@go-to-generate-workflow="
@@ -66,22 +57,14 @@
<button
v-if="AmIRefferer"
class="btn btn-notify"
@click="
$emit(
'goToGenerateNotification',
d,
false,
)
"
@click="$emit('goToGenerateNotification', d, false)"
></button>
<template v-else>
<button
id="btnGroupNotifyButtons"
type="button"
class="btn btn-notify dropdown-toggle"
:title="
trans(EVALUATION_NOTIFICATION_SEND)
"
:title="trans(EVALUATION_NOTIFICATION_SEND)"
data-bs-toggle="dropdown"
aria-expanded="false"
>
@@ -94,35 +77,17 @@
<li>
<a
class="dropdown-item"
@click="
goToGenerateDocumentNotification(
d,
true,
)
"
@click="goToGenerateDocumentNotification(d, true)"
>
{{
trans(
EVALUATION_NOTIFICATION_NOTIFY_REFERRER,
)
}}
{{ trans(EVALUATION_NOTIFICATION_NOTIFY_REFERRER) }}
</a>
</li>
<li>
<a
class="dropdown-item"
@click="
goToGenerateDocumentNotification(
d,
false,
)
"
@click="goToGenerateDocumentNotification(d, false)"
>
{{
trans(
EVALUATION_NOTIFICATION_NOTIFY_ANY,
)
}}
{{ trans(EVALUATION_NOTIFICATION_NOTIFY_ANY) }}
</a>
</li>
</ul>
@@ -133,15 +98,10 @@
:stored-object="d.storedObject"
:filename="d.title"
:can-edit="true"
:execute-before-leave="
submitBeforeLeaveToEditor
"
:davLink="
d.storedObject._links?.dav_link.href
"
:execute-before-leave="submitBeforeLeaveToEditor"
:davLink="d.storedObject._links?.dav_link.href"
:davLinkExpiration="
d.storedObject._links?.dav_link
.expiration
d.storedObject._links?.dav_link.expiration
"
@on-stored-object-status-change="
$emit('statusDocumentChanged', $event)
@@ -151,8 +111,7 @@
<!--replace document-->
<li
v-if="
Number.isInteger(d.id) &&
d.storedObject._permissions.canEdit
Number.isInteger(d.id) && d.storedObject._permissions.canEdit
"
>
<drop-file-modal
@@ -183,56 +142,28 @@
<li v-if="d.workflows.length === 0">
<a
class="dropdown-item"
@click="
$emit('removeDocument', d)
"
@click="$emit('removeDocument', d)"
>
<i
class="fa fa-trash-o"
aria-hidden="true"
></i>
{{
trans(
EVALUATION_DOCUMENT_DELETE,
)
}}
<i class="fa fa-trash-o" aria-hidden="true"></i>
{{ trans(EVALUATION_DOCUMENT_DELETE) }}
</a>
</li>
<!--duplicate document-->
<li>
<a
class="dropdown-item"
@click="
$emit(
'duplicateDocument',
d,
)
"
@click="$emit('duplicateDocument', d)"
>
<i
class="fa fa-copy"
aria-hidden="true"
></i>
{{
trans(
EVALUATION_DOCUMENT_DUPLICATE_HERE,
)
}}
<i class="fa fa-copy" aria-hidden="true"></i>
{{ trans(EVALUATION_DOCUMENT_DUPLICATE_HERE) }}
</a>
</li>
<li>
<a
class="dropdown-item"
@click="
prepareDocumentDuplicationToWork(
d,
)
"
@click="prepareDocumentDuplicationToWork(d)"
>
<i
class="fa fa-copy"
aria-hidden="true"
></i>
<i class="fa fa-copy" aria-hidden="true"></i>
{{
trans(
EVALUATION_DOCUMENT_DUPLICATE_TO_OTHER_EVALUATION,
@@ -241,27 +172,13 @@
>
</li>
<!--move document-->
<li
v-if="
d.storedObject._permissions
.canEdit
"
>
<li v-if="d.storedObject._permissions.canEdit">
<a
class="dropdown-item"
@click="
prepareDocumentMoveToWork(d)
"
@click="prepareDocumentMoveToWork(d)"
>
<i
class="fa fa-arrows"
aria-hidden="true"
></i>
{{
trans(
EVALUATION_DOCUMENT_MOVE,
)
}}</a
<i class="fa fa-arrows" aria-hidden="true"></i>
{{ trans(EVALUATION_DOCUMENT_MOVE) }}</a
>
</li>
</ul>
@@ -427,9 +344,7 @@ async function goToGenerateDocumentNotification(document, tos) {
buildLinkCreateNotification(
"Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument",
updatedDocument.id,
tos === true
? store.state.work.accompanyingPeriod.user?.id
: null,
tos === true ? store.state.work.accompanyingPeriod.user?.id : null,
window.location.pathname +
window.location.search +
window.location.hash,

View File

@@ -29,9 +29,7 @@
@inputDocumentTitle="onInputDocumentTitle"
@removeDocument="removeDocument"
@duplicateDocument="duplicateDocument"
@duplicate-document-to-evaluation="
duplicateDocumentToEvaluation
"
@duplicate-document-to-evaluation="duplicateDocumentToEvaluation"
@move-document-to-evaluation="moveDocumentToEvaluation"
@statusDocumentChanged="onStatusDocumentChanged"
@goToGenerateNotification="goToGenerateDocumentNotification"
@@ -381,9 +379,7 @@ function goToGenerateDocumentNotification(document, tos) {
buildLinkCreateNotification(
"Chill\\PersonBundle\\Entity\\AccompanyingPeriod\\AccompanyingPeriodWorkEvaluationDocument",
updatedDocument.id,
tos === true
? store.state.work.accompanyingPeriod.user.id
: null,
tos === true ? store.state.work.accompanyingPeriod.user.id : null,
window.location.pathname +
window.location.search +
window.location.hash,

View File

@@ -2,7 +2,8 @@ import { fetchResults, makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { Center, Civility, Gender, SetCenter } from "ChillMainAssets/types";
import {
AltName,
Person, PersonIdentifier,
Person,
PersonIdentifier,
PersonIdentifierWorker,
PersonWrite,
} from "ChillPersonAssets/types";
@@ -26,21 +27,43 @@ export const personToWritePerson = (person: Person): PersonWrite => {
type: "person",
firstName: person.firstName,
lastName: person.lastName,
altNames: person.altNames.map((altName) => ({key: altName.key, value: altName.label})),
altNames: person.altNames.map((altName) => ({
key: altName.key,
value: altName.label,
})),
addressId: null,
birthdate: null === person.birthdate ? null : {datetime: person.birthdate.datetime8601},
deathdate: null === person.deathdate ? null : {datetime: person.deathdate.datetime8601},
birthdate:
null === person.birthdate
? null
: { datetime: person.birthdate.datetime8601 },
deathdate:
null === person.deathdate
? null
: { datetime: person.deathdate.datetime8601 },
phonenumber: person.phonenumber,
mobilenumber: person.mobilenumber,
center: null === person.centers ? null : person.centers
center:
null === person.centers
? null
: person.centers
.map((center): SetCenter => ({ id: center.id, type: "center" }))
.find(() => true) || null,
email: person.email,
civility: null === person.civility ? null : {id: person.civility.id, type: "chill_main_civility"},
gender: null === person.gender ? null : {id: person.gender.id, type: "chill_main_gender"},
identifiers: person.identifiers.map((identifier: PersonIdentifier) => ({type: "person_identifier", definition_id: identifier.definition.id, value: identifier.value})),
}
}
civility:
null === person.civility
? null
: { id: person.civility.id, type: "chill_main_civility" },
gender:
null === person.gender
? null
: { id: person.gender.id, type: "chill_main_gender" },
identifiers: person.identifiers.map((identifier: PersonIdentifier) => ({
type: "person_identifier",
definition_id: identifier.definition.id,
value: identifier.value,
})),
};
};
export const getPersonAltNames = async (): Promise<AltName[]> =>
fetch("/api/1.0/person/config/alt_names.json").then((response) => {
@@ -65,10 +88,12 @@ export const getPersonIdentifiers = async (): Promise<
PersonIdentifierWorker[]
> => fetchResults("/api/1.0/person/identifiers/workers");
export interface WritePersonViolationMap
extends Record<string, Record<string, string>> {
export interface WritePersonViolationMap extends Record<
string,
Record<string, string>
> {
firstName: {
"{{ value }}": string
"{{ value }}": string;
};
lastName: {
"{{ value }}": string;
@@ -96,7 +121,7 @@ export interface WritePersonViolationMap
birthdate: {};
identifiers: {
"{{ value }}": string;
"definition_id": string;
definition_id: string;
};
}
export const createPerson = async (person: PersonWrite): Promise<Person> => {
@@ -107,10 +132,13 @@ export const createPerson = async (person: PersonWrite): Promise<Person> => {
);
};
export const editPerson = async (person: PersonWrite, personId: number): Promise<Person> => {
export const editPerson = async (
person: PersonWrite,
personId: number,
): Promise<Person> => {
return makeFetch<PersonWrite, Person, WritePersonViolationMap>(
"PATCH",
`/api/1.0/person/person/${personId}.json`,
person,
);
}
};

View File

@@ -15,11 +15,7 @@
<ul class="small_in_title columns mt-1">
<li>
<span class="item-key">
{{
trans(
ACCOMPANYING_COURSE_WORK_START_DATE,
)
}}
{{ trans(ACCOMPANYING_COURSE_WORK_START_DATE) }}
:
</span>
<b>{{ formatDate(eval.startDate) }}</b>
@@ -27,9 +23,7 @@
<li v-if="eval.endDate">
<span class="item-key">
{{
trans(ACCOMPANYING_COURSE_WORK_END_DATE)
}}
{{ trans(ACCOMPANYING_COURSE_WORK_END_DATE) }}
:
</span>
<b>{{ formatDate(eval.endDate) }}</b>

View File

@@ -34,8 +34,10 @@
<template #body>
<accompanying-period-work-list
v-if="evaluations.length === 0":accompanying-period-works="accompanyingPeriodWorks"
v-model:selectedAcpw="selectedAcpw"/>
v-if="evaluations.length === 0"
:accompanying-period-works="accompanyingPeriodWorks"
v-model:selectedAcpw="selectedAcpw"
/>
<accompanying-period-work-evaluation-list
v-if="evaluations.length > 0"
:evaluations="evaluations"
@@ -59,9 +61,10 @@ import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import AccompanyingPeriodWorkList from "./AccompanyingPeriodWorkList.vue";
import { AccompanyingPeriodWork } from "../../../types";
import {
trans,ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
ACPW_DUPLICATE_SELECT_AN_EVALUATION,CONFIRM,
trans,
ACPW_DUPLICATE_SELECT_ACCOMPANYING_PERIOD_WORK,
ACPW_DUPLICATE_SELECT_AN_EVALUATION,
CONFIRM,
} from "translator";
import { fetchResults } from "ChillMainAssets/lib/api/apiMethods";
import AccompanyingPeriodWorkEvaluationList from "ChillPersonAssets/vuejs/_components/AccompanyingPeriodWorkSelector/AccompanyingPeriodWorkEvaluationList.vue";
@@ -147,14 +150,14 @@ const confirmSelection = () => {
if (!props.isEvaluationSelector) {
if (selectedAcpw.value) {
// only emit if something is actually selected!emit("pickWork", { work: selectedAcpw.value });
closeModal();}
closeModal();
}
// optionally show some error or warning if not selected
return;
}
if (selectedAcpw.value && props.isEvaluationSelector) {
evaluations.value =
selectedAcpw.value.accompanyingPeriodWorkEvaluations;
evaluations.value = selectedAcpw.value.accompanyingPeriodWorkEvaluations;
}
if (selectedEvaluation.value && props.isEvaluationSelector) {

View File

@@ -25,7 +25,11 @@
/>
<CreateModal
v-if="creatableEntityTypes.length > 0 && showModalCreate && null == thirdPartyParentAddContact"
v-if="
creatableEntityTypes.length > 0 &&
showModalCreate &&
null == thirdPartyParentAddContact
"
action="create"
:allowed-types="creatableEntityTypes"
:query="query"
@@ -62,7 +66,10 @@ import type {
import { marked } from "marked";
import options = marked.options;
import CreateModal from "ChillMainAssets/vuejs/OnTheFly/components/CreateModal.vue";
import {Thirdparty, ThirdpartyCompany} from "../../../../../ChillThirdPartyBundle/Resources/public/types";
import {
Thirdparty,
ThirdpartyCompany,
} from "../../../../../ChillThirdPartyBundle/Resources/public/types";
interface AddPersonsConfig {
suggested?: Suggestion[];
@@ -80,13 +87,13 @@ const props = withDefaults(defineProps<AddPersonsConfig>(), {
});
const emit =
defineEmits<{
(e: "addNewPersons", payload: { selected: Suggestion[] }): void;
}
defineEmits<
(e: "addNewPersons", payload: { selected: Suggestion[] }) => void
>();
type PersonChooseModalType = InstanceType<typeof PersonChooseModal>;
const personChooseModal = useTemplateRef<PersonChooseModalType>('personChooseModal');
const personChooseModal =
useTemplateRef<PersonChooseModalType>("personChooseModal");
/**
* Flag to show/hide the modal "choose".
@@ -156,7 +163,7 @@ function closeModalChoose(): void {
}
function closeModalCreate(): void {
if (null !== thirdPartyParentAddContact) {
if (null !== thirdPartyParentAddContact.value) {
thirdPartyParentAddContact.value = null;
}
showModalCreate.value = false;
@@ -165,7 +172,10 @@ function closeModalCreate(): void {
/**
* Called by PersonSuggestion's updateSelection event, when an element is checked/unchecked
*/
function updateSelected(payload: {suggestion: Suggestion, isSelected: boolean}): void {
function updateSelected(payload: {
suggestion: Suggestion;
isSelected: boolean;
}): void {
if (payload.isSelected) {
addSuggestionToSelected(payload.suggestion);
} else {
@@ -236,7 +246,7 @@ function resetSearch(): void {
personChooseModal.value?.resetSearch();
}
defineExpose({resetSearch})
defineExpose({ resetSearch });
</script>
<style lang="scss" scoped>

View File

@@ -82,7 +82,11 @@
v-if="props.allowCreate && query.length > 0"
class="create-button"
>
<button type="button" class="btn btn-submit" @click="emit('onAskForCreate', { query })">
<button
type="button"
class="btn btn-submit"
@click="emit('onAskForCreate', { query })"
>
{{ trans(ONTHEFLY_CREATE_BUTTON, { q: query }) }}
</button>
</div>
@@ -147,7 +151,10 @@ const emit = defineEmits<{
(e: "onPickEntities"): void;
(e: "onAskForCreate", payload: { query: string }): void;
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }): void;
(e: "updateSelected", payload: {suggestion: Suggestion, isSelected: boolean}): void;
(
e: "updateSelected",
payload: { suggestion: Suggestion; isSelected: boolean },
): void;
(e: "cleanSelected"): void;
}>();
@@ -181,11 +188,10 @@ const queryLength = computed(() => search.query.length);
const suggestedCounter = computed(() => suggested.value.length);
const selectedCounter = computed(() => props.selected.size);
const checkUniq = computed(() =>
props.options.uniq ? "radio" : "checkbox",
);
const checkUniq = computed(() => (props.options.uniq ? "radio" : "checkbox"));
const selectedAndSuggested = computed<(Suggestion & {isSelected: boolean})[]>(() => {
const selectedAndSuggested = computed<(Suggestion & { isSelected: boolean })[]>(
() => {
const selectedAndSuggested = [];
// add selected that are not in the search results
@@ -195,13 +201,19 @@ const selectedAndSuggested = computed<(Suggestion & {isSelected: boolean})[]>(()
}
}
for (const suggestion of suggested.value) {
selectedAndSuggested.push({...suggestion, isSelected: props.selected.has(suggestion.key)})
selectedAndSuggested.push({
...suggestion,
isSelected: props.selected.has(suggestion.key),
});
}
return selectedAndSuggested;
});
},
);
const hasNoResult = computed(() => search.hasPreviousQuery && suggested.value.length === 0);
const hasNoResult = computed(
() => search.hasPreviousQuery && suggested.value.length === 0,
);
function setQuery(q: string) {
search.query = q;
@@ -240,14 +252,15 @@ function setQuery(q: string) {
}, delay);
}
function loadSuggestions(suggestedArr: {relevance: number, result: Entities}[]): void {
function loadSuggestions(
suggestedArr: { relevance: number; result: Entities }[],
): void {
suggested.value = suggestedArr.map((item) => {
return {
key: item.result.type + item.result.id,
relevance: item.relevance,
result: item.result
}
result: item.result,
};
});
}
@@ -267,7 +280,7 @@ function resetSearch() {
function selectAll() {
suggested.value.forEach((suggestion: Suggestion) => {
emit("updateSelected", {suggestion, isSelected: true})
emit("updateSelected", { suggestion, isSelected: true });
});
}
@@ -279,7 +292,7 @@ function triggerAddContact(payload: {parent: ThirdpartyCompany}) {
* Triggered when the user clicks on the "add" button.
*/
function pickEntities(): void {
emit("onPickEntities", );
emit("onPickEntities");
search.query = "";
emit("close");
}

View File

@@ -53,9 +53,10 @@ import SuggestionUserGroup from "./TypeUserGroup.vue";
import {
isSuggestionForHousehold,
isSuggestionForPerson,
isSuggestionForThirdParty, isSuggestionForUser,
isSuggestionForThirdParty,
isSuggestionForUser,
isSuggestionForUserGroup,
Suggestion
Suggestion,
} from "ChillPersonAssets/types";
import { ThirdpartyCompany } from "../../../../../../ChillThirdPartyBundle/Resources/public/types";
@@ -65,11 +66,14 @@ const props = defineProps<{
type: "radio" | "checkbox";
}>();
const emit = defineEmits<{
(e: "updateSelected", payload: {suggestion: Suggestion, isSelected: boolean}): void;
(
e: "updateSelected",
payload: { suggestion: Suggestion; isSelected: boolean },
): void;
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }): void;
}>();
const isChecked = computed<boolean>(() => props.isSelected)
const isChecked = computed<boolean>(() => props.isSelected);
const onUpdateValue = (event: Event) => {
const target = event?.target;
@@ -77,8 +81,11 @@ const onUpdateValue = (event: Event) => {
console.error("the value of checked is not an HTMLInputElement");
return;
}
emit("updateSelected", {suggestion: props.item, isSelected: props.type === "radio" ? true : target.checked});
}
emit("updateSelected", {
suggestion: props.item,
isSelected: props.type === "radio" ? true : target.checked,
});
};
function triggerAddContact(payload: { parent: ThirdpartyCompany }) {
emit("triggerAddContact", payload);

View File

@@ -36,7 +36,7 @@ function formatDate(dateString: string | undefined, format: string) {
}
const props = defineProps<{
item: Suggestion & { result: Person },
item: Suggestion & { result: Person };
}>();
const hasBirthdate = computed(() => props.item.result.birthdate !== null);

View File

@@ -1,9 +1,15 @@
<template>
<div class="container tpartycontainer">
<div class="tparty-identification">
<span v-if="(isThirdpartyChild(item.result) || isThirdpartyContact(item.result)) && item.result.profession" class="profession">{{
<span
v-if="
(isThirdpartyChild(item.result) ||
isThirdpartyContact(item.result)) &&
item.result.profession
}}</span>
"
class="profession"
>{{ item.result.profession }}</span
>
<span class="name"> {{ item.result.text }}&nbsp; </span>
<span class="location">
<template v-if="hasAddress">
@@ -12,7 +18,10 @@
</template>
</span>
</div>
<div class="tpartyparent" v-if="isThirdpartyChild(item.result) && null !== item.result.parent">
<div
class="tpartyparent"
v-if="isThirdpartyChild(item.result) && null !== item.result.parent"
>
<span class="name"> &gt; {{ item.result.parent.text }} </span>
</div>
</div>
@@ -23,7 +32,8 @@
v-if="item.result.type === 'thirdparty' && item.result.kind === 'company'"
class="btn btn-tpchild"
@click="emit('triggerAddContact', { parent: item.result })"
><i class="bi bi-person-fill-add"></i></a>
><i class="bi bi-person-fill-add"></i
></a>
<on-the-fly type="thirdparty" :id="item.result.id" action="show" />
</div>
</template>
@@ -38,7 +48,8 @@ import { Result, Suggestion } from "ChillPersonAssets/types";
import {
isThirdpartyChild,
isThirdpartyContact,
Thirdparty, ThirdpartyCompany
Thirdparty,
ThirdpartyCompany,
} from "./../../../../../../ChillThirdPartyBundle/Resources/public/types";
interface TypeThirdPartyProps {
@@ -47,7 +58,10 @@ interface TypeThirdPartyProps {
const props = defineProps<TypeThirdPartyProps>();
const emit = defineEmits<(e: "triggerAddContact", payload: {parent: ThirdpartyCompany}) => void>();
const emit =
defineEmits<
(e: "triggerAddContact", payload: { parent: ThirdpartyCompany }) => void
>();
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);
const toast = useToast();
@@ -56,7 +70,10 @@ const hasAddress = computed(() => {
if (props.item.result.address !== null) {
return true;
}
if (isThirdpartyChild(props.item.result) && props.item.result.parent !== null) {
if (
isThirdpartyChild(props.item.result) &&
props.item.result.parent !== null
) {
return props.item.result.parent.address !== null;
}
@@ -67,7 +84,11 @@ const getAddress = computed(() => {
if (props.item.result.address !== null) {
return props.item.result.address;
}
if (isThirdpartyChild(props.item.result) && props.item.result.parent !== null && props.item.result.parent.address !== null) {
if (
isThirdpartyChild(props.item.result) &&
props.item.result.parent !== null &&
props.item.result.parent.address !== null
) {
return props.item.result.parent.address;
}
return null;

View File

@@ -21,7 +21,6 @@ interface TypeUserProps {
}
const props = defineProps<TypeUserProps>();
</script>
<style lang="scss" scoped>

View File

@@ -23,9 +23,7 @@
</div>
<p>
<span
v-if="options.addId == true"
:title="person.personId"
<span v-if="options.addId == true" :title="person.personId"
><i class="bi bi-info-circle"></i> {{ person.personId }}</span
>
</p>
@@ -34,10 +32,14 @@
<gender-icon-render-box
v-if="person.gender"
:gender="person.gender"
/> <span
v-if="person.birthdate"
>
{{ trans(RENDERBOX_BIRTHDAY_STATEMENT, {gender: toGenderTranslation(person.gender), birthdate: ISOToDate(person.birthdate?.datetime)}) }}
/>
<span v-if="person.birthdate">
{{
trans(RENDERBOX_BIRTHDAY_STATEMENT, {
gender: toGenderTranslation(person.gender),
birthdate: ISOToDate(person.birthdate?.datetime),
})
}}
</span>
<span v-if="options.addAge && person.birthdate" class="age">
({{ trans(RENDERBOX_YEARS_OLD, { n: person.age }) }})
@@ -45,13 +47,15 @@
</p>
<p>
<span
v-if="person.deathdate"
>
{{ trans(RENDERBOX_DEATHDATE_STATEMENT, {gender: toGenderTranslation(person.gender), deathdate: ISOToDate(person.deathdate?.datetime)}) }}
<span v-if="person.deathdate">
{{
trans(RENDERBOX_DEATHDATE_STATEMENT, {
gender: toGenderTranslation(person.gender),
deathdate: ISOToDate(person.deathdate?.datetime),
})
}}
</span>
</p>
</div>
</div>
@@ -302,7 +306,7 @@ const props = withDefaults(defineProps<Props>(), {
addNoData: true,
isMultiline: true,
isHolder: false,
addHouseholdLink: true
addHouseholdLink: true,
}),
});

View File

@@ -81,11 +81,12 @@
value=""
@input="onAltNameInput($event, a.key)"
/>
<label :for="'label_' + a.key">{{ localizeString(a.labels) }}</label>
<label :for="'label_' + a.key">{{
localizeString(a.labels)
}}</label>
</div>
</div>
</div>
</template>
<template v-if="action === 'create'">
@@ -98,18 +99,30 @@
<div class="form-floating">
<input
class="form-control form-control-lg"
:class="{'is-invalid': violations.hasViolationWithParameter('identifiers', 'definition_id', worker.definition_id.toString())}"
:class="{
'is-invalid': violations.hasViolationWithParameter(
'identifiers',
'definition_id',
worker.definition_id.toString(),
),
}"
type="text"
:name="'worker_' + worker.definition_id"
:placeholder="localizeString(worker.label)"
@input="onIdentifierInput($event, worker.definition_id)"
/>
<label :for="'worker_' + worker.definition_id" :class="{required: worker.presence == 'REQUIRED'}">{{
localizeString(worker.label)
}}</label>
<label
:for="'worker_' + worker.definition_id"
:class="{ required: worker.presence == 'REQUIRED' }"
>{{ localizeString(worker.label) }}</label
>
</div>
<div
v-for="err in violations.violationTitlesWithParameter('identifiers', 'definition_id', worker.definition_id.toString())"
v-for="err in violations.violationTitlesWithParameter(
'identifiers',
'definition_id',
worker.definition_id.toString(),
)"
class="invalid-feedback was-validated-force"
>
{{ err }}
@@ -163,7 +176,9 @@
{{ c.name }}
</option>
</select>
<label for="center" class="required">{{ trans(PERSON_MESSAGES_PERSON_CENTER_TITLE) }}</label>
<label for="center" class="required">{{
trans(PERSON_MESSAGES_PERSON_CENTER_TITLE)
}}</label>
</div>
<div
v-for="err in violations.violationTitles('center')"
@@ -190,7 +205,9 @@
{{ localizeString(c.name) }}
</option>
</select>
<label for="civility">{{ trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE) }}</label>
<label for="civility">{{
trans(PERSON_MESSAGES_PERSON_CIVILITY_TITLE)
}}</label>
</div>
<div
v-for="err in violations.violationTitles('civility')"
@@ -241,7 +258,9 @@
:aria-label="trans(PERSON_MESSAGES_PERSON_PHONENUMBER)"
aria-describedby="phonenumber"
/>
<label for="phonenumber">{{ trans(PERSON_MESSAGES_PERSON_PHONENUMBER) }}</label>
<label for="phonenumber">{{
trans(PERSON_MESSAGES_PERSON_PHONENUMBER)
}}</label>
</div>
<div
v-for="err in violations.violationTitles('phonenumber')"
@@ -266,7 +285,9 @@
:aria-label="trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)"
aria-describedby="mobilenumber"
/>
<label for="mobilenumber">{{ trans(PERSON_MESSAGES_PERSON_MOBILENUMBER) }}</label>
<label for="mobilenumber">{{
trans(PERSON_MESSAGES_PERSON_MOBILENUMBER)
}}</label>
</div>
<div
v-for="err in violations.violationTitles('mobilenumber')"
@@ -326,11 +347,8 @@
ref="addAddress"
/>
</div>
</div>
<div v-else>
</div>
<div v-else></div>
</template>
<script setup lang="ts">
@@ -338,7 +356,8 @@ import { ref, reactive, computed, onMounted } from "vue";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
import {
createPerson, editPerson,
createPerson,
editPerson,
getCentersForPersonCreation,
getCivilities,
getGenders,
@@ -366,22 +385,19 @@ import {
PERSON_MESSAGES_PERSON_ADDRESS_SHOW_ADDRESS_FORM,
PERSON_MESSAGES_PERSON_ADDRESS_WARNING,
} from "translator";
import {
Center,
Civility,
Gender,
} from "ChillMainAssets/types";
import { Center, Civility, Gender } from "ChillMainAssets/types";
import {
AltName,
Person,
PersonWrite,
PersonIdentifierWorker,
} from "ChillPersonAssets/types";
import {
isValidationException,
} from "ChillMainAssets/lib/api/apiMethods";
import { isValidationException } from "ChillMainAssets/lib/api/apiMethods";
import { useToast } from "vue-toast-notification";
import {getTimezoneOffsetString, ISOToDate} from "ChillMainAssets/chill/js/date";
import {
getTimezoneOffsetString,
ISOToDate,
} from "ChillMainAssets/chill/js/date";
import { useViolationList } from "ChillMainAssets/vuejs/_composables/violationList";
interface PersonEditComponentConfig {
@@ -486,13 +502,16 @@ const birthDate = computed({
person.birthdate = null;
return;
}
const offset = getTimezoneOffsetString(date, Intl.DateTimeFormat().resolvedOptions().timeZone);
const offset = getTimezoneOffsetString(
date,
Intl.DateTimeFormat().resolvedOptions().timeZone,
);
if (person.birthdate) {
person.birthdate.datetime = value + "T00:00:00" + offset;
} else {
person.birthdate = { datetime: value + "T00:00:00" + offset };
}
}
},
});
const phonenumber = computed({
get: () => person.phonenumber,
@@ -566,7 +585,7 @@ async function loadData() {
const w = personToWritePerson(p);
person.firstName = w.firstName;
person.lastName = w.lastName;
person.altNames.push(...w.altNames)
person.altNames.push(...w.altNames);
person.civility = w.civility;
person.addressId = w.addressId;
person.birthdate = w.birthdate;
@@ -633,7 +652,7 @@ function submitNewAddress(payload: { addressId: number }) {
async function postPerson(): Promise<Person> {
try {
if (props.action === 'create') {
if (props.action === "create") {
const createdPerson = await createPerson(person);
emit("onPersonCreated", { person: createdPerson });
@@ -667,7 +686,7 @@ onMounted(() => {
getPersonIdentifiers().then((identifiers) => {
config.identifiers = identifiers.filter(
(w: PersonIdentifierWorker) =>
w.presence === 'ON_CREATION' || w.presence === 'REQUIRED'
w.presence === "ON_CREATION" || w.presence === "REQUIRED",
);
});
if (props.action !== "create") {

View File

@@ -2,8 +2,7 @@ const personMessages = {
fr: {
add_persons: {
title: "Ajouter des usagers",
suggested_counter:
"Pas de résultats | 1 résultat | {count} résultats",
suggested_counter: "Pas de résultats | 1 résultat | {count} résultats",
selected_counter: " 1 sélectionné | {count} sélectionnés",
search_some_persons: "Rechercher des personnes..",
},

View File

@@ -2,7 +2,9 @@ import {
Address,
Center,
Civility,
DateTime, SetAddress, SetCivility,
DateTime,
SetAddress,
SetCivility,
User,
} from "ChillMainAssets/types";
@@ -35,7 +37,10 @@ function isBaseThirdParty(t: unknown): t is BaseThirdParty {
(o as any).type === "thirdparty" &&
typeof o.id === "number" &&
typeof o.text === "string" &&
(o.kind === "" || o.kind === "contact" || o.kind === "child" || o.kind === "company") &&
(o.kind === "" ||
o.kind === "contact" ||
o.kind === "child" ||
o.kind === "company") &&
typeof o.active === "boolean"
);
}
@@ -51,13 +56,8 @@ export interface ThirdpartyCompany extends BaseThirdParty {
}
// Type guard to distinguish a ThirdpartyCompany
export function isThirdpartyCompany(
t: BaseThirdParty
): t is ThirdpartyCompany {
return (
t.type === "thirdparty" &&
t.kind === "company"
);
export function isThirdpartyCompany(t: BaseThirdParty): t is ThirdpartyCompany {
return t.type === "thirdparty" && t.kind === "company";
}
export interface ThirdpartyChild extends BaseThirdParty {
@@ -75,13 +75,8 @@ export interface ThirdpartyChild extends BaseThirdParty {
}
// Type guard to distinguish a ThirdpartyChild
export function isThirdpartyChild(
t: BaseThirdParty
): t is ThirdpartyChild {
return (
t.type === "thirdparty" &&
t.kind === "child"
);
export function isThirdpartyChild(t: BaseThirdParty): t is ThirdpartyChild {
return t.type === "thirdparty" && t.kind === "child";
}
export interface ThirdpartyContact extends BaseThirdParty {
@@ -99,24 +94,23 @@ export interface ThirdpartyContact extends BaseThirdParty {
}
// Type guard to distinguish a ThirdpartyContact
export function isThirdpartyContact(
t: BaseThirdParty
): t is ThirdpartyContact {
return (
t.type === "thirdparty" &&
t.kind === "contact"
);
export function isThirdpartyContact(t: BaseThirdParty): t is ThirdpartyContact {
return t.type === "thirdparty" && t.kind === "contact";
}
export type Thirdparty = ThirdpartyCompany | ThirdpartyContact | ThirdpartyChild;
export type Thirdparty =
| ThirdpartyCompany
| ThirdpartyContact
| ThirdpartyChild;
export function isThirdparty(t: unknown): t is Thirdparty {
if (!isBaseThirdParty(t)) {
return false;
}
return (isThirdpartyCompany(t) || isThirdpartyContact(t) || isThirdpartyChild(t));
return (
isThirdpartyCompany(t) || isThirdpartyContact(t) || isThirdpartyChild(t)
);
}
interface ThirdpartyType {

View File

@@ -1,7 +1,13 @@
/*
* GET a thirdparty by id
*/
import {isThirdpartyChild, isThirdpartyCompany, isThirdpartyContact, Thirdparty, ThirdPartyWrite} from '../../types';
import {
isThirdpartyChild,
isThirdpartyCompany,
isThirdpartyContact,
Thirdparty,
ThirdPartyWrite,
} from "../../types";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
export const getThirdparty = async (id: number): Promise<Thirdparty> => {
@@ -21,40 +27,41 @@ export const thirdpartyToWriteThirdParty = (t: Thirdparty): ThirdPartyWrite => {
const isChild = isThirdpartyChild(t);
return {
type: 'thirdparty',
type: "thirdparty",
kind: t.kind,
civility:
(isContact || isChild) && t.civility
? { type: 'chill_main_civility', id: t.civility.id }
? { type: "chill_main_civility", id: t.civility.id }
: null,
profession: (isContact || isChild) ? (t.profession ?? '') : '',
firstname: isCompany ? '' : (t.firstname ?? ''),
name: isCompany
? (t.nameCompany ?? '')
: (t.name ?? ''),
email: t.email ?? '',
telephone: t.telephone ?? '',
telephone2: t.telephone2 ?? '',
profession: isContact || isChild ? (t.profession ?? "") : "",
firstname: isCompany ? "" : (t.firstname ?? ""),
name: isCompany ? (t.nameCompany ?? "") : (t.name ?? ""),
email: t.email ?? "",
telephone: t.telephone ?? "",
telephone2: t.telephone2 ?? "",
address: null,
comment: isChild ? (t.comment ?? '') : '',
parent: isChild && t.parent ? { type: 'thirdparty', id: t.parent.id } : null,
comment: isChild ? (t.comment ?? "") : "",
parent:
isChild && t.parent ? { type: "thirdparty", id: t.parent.id } : null,
};
};
export interface WriteThirdPartyViolationMap
extends Record<string, Record<string, string>> {
export interface WriteThirdPartyViolationMap extends Record<
string,
Record<string, string>
> {
email: {
"{{ value }}": string;
},
};
name: {
"{{ value }}": string;
},
};
telephone: {
"{{ value }}": string;
}
};
telephone2: {
"{{ value }}": string;
}
};
}
/*
@@ -63,13 +70,16 @@ extends Record<string, Record<string, string>> {
export const createThirdParty = async (body: ThirdPartyWrite) => {
const url = `/api/1.0/thirdparty/thirdparty.json`;
return makeFetch<ThirdPartyWrite, Thirdparty>('POST', url, body);
return makeFetch<ThirdPartyWrite, Thirdparty>("POST", url, body);
};
/*
* PATCH an existing thirdparty
*/
export const patchThirdparty = async (id: number, body: ThirdPartyWrite): Promise<Thirdparty> => {
export const patchThirdparty = async (
id: number,
body: ThirdPartyWrite,
): Promise<Thirdparty> => {
const url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
return makeFetch('PATCH', url, body);
return makeFetch("PATCH", url, body);
};

View File

@@ -133,13 +133,14 @@ import { computed, defineAsyncComponent } from "vue";
import AddressRenderBox from "ChillMainAssets/vuejs/_components/Entity/AddressRenderBox.vue";
import Confidential from "ChillMainAssets/vuejs/_components/Confidential.vue";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import {isThirdpartyChild, isThirdpartyCompany, Thirdparty} from "../../../types";
import {
isThirdpartyChild,
isThirdpartyCompany,
Thirdparty,
} from "../../../types";
import { localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
import {
THIRDPARTY_MESSAGES_CHILD_OF,
trans
} from "translator";
import { THIRDPARTY_MESSAGES_CHILD_OF, trans } from "translator";
// Async to avoid recursive resolution issues
/*
@@ -160,7 +161,9 @@ interface RenderOptions {
const props = defineProps<{ thirdparty: Thirdparty; options: RenderOptions }>();
const isMultiline = computed<boolean>(() => props.options?.isMultiline ?? false);
const isMultiline = computed<boolean>(
() => props.options?.isMultiline ?? false,
);
const hasParent = computed<boolean>(() => {
return isThirdpartyChild(props.thirdparty);

View File

@@ -20,29 +20,34 @@
action === 'edit' || action === 'create' || action === 'addContact'
"
>
<ThirdPartyEdit :id="id" :type="type" :action="action" :query="query" :parent="parent" />
<ThirdPartyEdit
:id="id"
:type="type"
:action="action"
:query="query"
:parent="parent"
/>
</div>
</template>
<script setup lang="ts">
import {onMounted, ref} from 'vue'
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue'
import ThirdPartyEdit from './ThirdPartyEdit.vue'
import { getThirdparty } from '../../_api/OnTheFly'
import {Thirdparty, ThirdpartyCompany} from '../../../types'
import { onMounted, ref } from "vue";
import ThirdPartyRenderBox from "../Entity/ThirdPartyRenderBox.vue";
import ThirdPartyEdit from "./ThirdPartyEdit.vue";
import { getThirdparty } from "../../_api/OnTheFly";
import { Thirdparty, ThirdpartyCompany } from "../../../types";
type ThirdPartyProp = {
id: number,
type: 'thirdparty',
action: 'show'|'edit'|'create',
parent?: null,
interface ThirdPartyProp {
id: number;
type: "thirdparty";
action: "show" | "edit" | "create";
parent?: null;
}
type ThirdPartyAddContact = {
id: number,
type: 'thirdparty',
action: 'addContact',
parent: ThirdpartyCompany,
interface ThirdPartyAddContact {
id: number;
type: "thirdparty";
action: "addContact";
parent: ThirdpartyCompany;
}
type ThirdPartyProps = ThirdPartyProp | ThirdPartyAddContact;
@@ -58,10 +63,10 @@ async function loadData() {
}
onMounted(() => {
if (props.action === 'show' && props.id) {
loadData()
if (props.action === "show" && props.id) {
loadData();
}
})
});
</script>
<style lang="scss" scoped>

View File

@@ -4,7 +4,9 @@
<div class="parent-info">
<i class="fa fa-li fa-hand-o-right" />
<b class="me-2">{{ trans(THIRDPARTY_MESSAGES_CHILD_OF) }}</b>
<span class="chill-entity badge-thirdparty">{{ resolvedParent.text }}</span>
<span class="chill-entity badge-thirdparty">{{
resolvedParent.text
}}</span>
</div>
</div>
<div class="form-floating mb-3" v-else-if="props.action === 'create'">
@@ -77,7 +79,9 @@
{{ localizeString(civility.name) }}
</option>
</select>
<label for="civility">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY) }}</label>
<label for="civility">{{
trans(THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY)
}}</label>
</div>
</div>
</div>
@@ -93,7 +97,9 @@
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)"
aria-describedby="profession"
/>
<label for="profession">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION) }}</label>
<label for="profession">{{
trans(THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION)
}}</label>
</div>
</div>
</div>
@@ -224,7 +230,9 @@
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)"
aria-describedby="email"
/>
<label for="email">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL) }}</label>
<label for="email">{{
trans(THIRDPARTY_MESSAGES_THIRDPARTY_EMAIL)
}}</label>
</div>
</div>
<div
@@ -250,7 +258,9 @@
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)"
aria-describedby="phonenumber"
/>
<label for="phonenumber">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER) }}</label>
<label for="phonenumber">{{
trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER)
}}</label>
</div>
</div>
<div
@@ -263,7 +273,9 @@
<div class="mb-3">
<div class="input-group has-validation">
<span class="input-group-text" id="phonenumber2"><i class="fa fa-fw fa-phone"/></span>
<span class="input-group-text" id="phonenumber2"
><i class="fa fa-fw fa-phone"
/></span>
<div class="form-floating">
<input
class="form-control form-control-lg"
@@ -274,7 +286,9 @@
:aria-label="trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)"
aria-describedby="phonenumber2"
/>
<label for="phonenumber2">{{ trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2) }}</label>
<label for="phonenumber2">{{
trans(THIRDPARTY_MESSAGES_THIRDPARTY_PHONENUMBER2)
}}</label>
</div>
</div>
<div
@@ -301,17 +315,18 @@
</template>
<script setup lang="ts">
import {ref, reactive, computed, onMounted, getCurrentInstance} from 'vue'
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue'
import { ref, reactive, computed, onMounted, getCurrentInstance } from "vue";
import ThirdPartyRenderBox from "../Entity/ThirdPartyRenderBox.vue";
import AddAddress from "ChillMainAssets/vuejs/Address/components/AddAddress.vue";
import {
createThirdParty,
getThirdparty, patchThirdparty,
getThirdparty,
patchThirdparty,
thirdpartyToWriteThirdParty,
WriteThirdPartyViolationMap
} from '../../_api/OnTheFly'
import BadgeEntity from 'ChillMainAssets/vuejs/_components/BadgeEntity.vue'
import {localizeString as _localizeString} from 'ChillMainAssets/lib/localizationHelper/localizationHelper'
WriteThirdPartyViolationMap,
} from "../../_api/OnTheFly";
import BadgeEntity from "ChillMainAssets/vuejs/_components/BadgeEntity.vue";
import { localizeString as _localizeString } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
import {
trans,
THIRDPARTY_MESSAGES_THIRDPARTY_FIRSTNAME,
@@ -323,11 +338,13 @@ import {
THIRDPARTY_MESSAGES_THIRDPARTY_COMMENT,
THIRDPARTY_MESSAGES_THIRDPARTY_PROFESSION,
THIRDPARTY_MESSAGES_THIRDPARTY_CIVILITY,
THIRDPARTY_MESSAGES_CHILD_OF, PERSON_EDIT_ERROR_WHILE_SAVING,
} from 'translator'
THIRDPARTY_MESSAGES_CHILD_OF,
PERSON_EDIT_ERROR_WHILE_SAVING,
} from "translator";
import {
createPerson,
getCivilities, WritePersonViolationMap,
getCivilities,
WritePersonViolationMap,
} from "ChillPersonAssets/vuejs/_api/OnTheFly";
import {
isThirdpartyChild,
@@ -335,36 +352,41 @@ import {
Thirdparty,
ThirdpartyCompany,
ThirdPartyKind,
ThirdPartyWrite
ThirdPartyWrite,
} from "../../../types";
import { Civility, SetCivility } from "ChillMainAssets/types";
import { isValidationException } from "ChillMainAssets/lib/api/apiMethods";
import { useViolationList } from "ChillMainAssets/vuejs/_composables/violationList";
import { useToast } from "vue-toast-notification";
interface ThirdPartyEditWriteProps {
id?: number;
type?: 'thirdparty';
action: 'edit' | 'create'
type?: "thirdparty";
action: "edit" | "create";
query?: string;
parent?: null;
}
interface ThirdPartyAddContactProps {
id?: null;
type?: 'thirdparty';
action: 'addContact';
type?: "thirdparty";
action: "addContact";
query?: "";
parent: ThirdpartyCompany;
}
type ThirdPartyEditProps = ThirdPartyAddContactProps | ThirdPartyEditWriteProps;
const props = withDefaults(defineProps<ThirdPartyEditProps>(), {type: 'thirdparty', query: "", parent: null});
const props = withDefaults(defineProps<ThirdPartyEditProps>(), {
type: "thirdparty",
query: "",
parent: null,
});
const emit =
defineEmits<(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }) => void>();
defineEmits<
(e: "onThirdPartyCreated", payload: { thirdParty: Thirdparty }) => void
>();
defineExpose({ postThirdParty });
@@ -372,7 +394,7 @@ const toast = useToast();
const thirdParty = ref<ThirdPartyWrite>({
type: "thirdparty",
kind: 'company',
kind: "company",
address: null,
civility: null,
email: "",
@@ -401,17 +423,17 @@ const civility = computed<number | null>({
},
});
const civilities = ref<Civility[]>([])
const civilities = ref<Civility[]>([]);
const addAddress = reactive({
options: {
openPanesInModal: true,
onlyButton: false,
button: {size: 'btn-sm'},
title: {create: 'add_an_address_title', edit: 'edit_address'},
button: { size: "btn-sm" },
title: { create: "add_an_address_title", edit: "edit_address" },
},
})
const addAddressRef = ref<any>(null)
});
const addAddressRef = ref<any>(null);
/**
* We need a specific computed for the kind
@@ -421,7 +443,7 @@ const kind = computed<ThirdPartyKind>({
return thirdParty.value.kind;
},
set(v) {
thirdParty.value.kind = v
thirdParty.value.kind = v;
},
});
@@ -431,21 +453,24 @@ const context = computed(() => {
edit: false,
addressId: null as number | null,
defaults: (window as any).addaddress,
}
};
if (thirdParty.value.address) {
ctx.addressId = thirdParty.value.address.id
ctx.edit = true
ctx.addressId = thirdParty.value.address.id;
ctx.edit = true;
}
return ctx
})
return ctx;
});
const resolvedParent = computed<null | ThirdpartyCompany>(() => {
if (null !== originalThirdParty.value && isThirdpartyChild(originalThirdParty.value)) {
if (
null !== originalThirdParty.value &&
isThirdpartyChild(originalThirdParty.value)
) {
return originalThirdParty.value.parent;
}
return props.parent ?? null;
})
});
/**
* Find the query items to display for suggestion
@@ -461,7 +486,10 @@ const queryItems = computed(() => {
.trim()
.toLowerCase()
.split(" ");
const lastNameWords = (thirdParty.value.name || "").trim().toLowerCase().split(" ");
const lastNameWords = (thirdParty.value.name || "")
.trim()
.toLowerCase()
.split(" ");
return words
.filter((word) => !firstNameWords.includes(word.toLowerCase()))
@@ -469,20 +497,19 @@ const queryItems = computed(() => {
});
function localizeString(str: any) {
return _localizeString(str)
return _localizeString(str);
}
onMounted(() => {
getCivilities().then((cv) => {
civilities.value = cv;
});
if (props.action === "edit") {
loadData();
} else if (props.action === 'addContact') {
thirdParty.value.kind = 'child';
} else if (props.action === "addContact") {
thirdParty.value.kind = "child";
thirdParty.value.address = null;
thirdParty.value.parent = {id: props.parent.id, type: 'thirdparty'};
thirdParty.value.parent = { id: props.parent.id, type: "thirdparty" };
}
});
@@ -498,35 +525,35 @@ function submitAddress(payload: { addressId: number }) {
thirdParty.value.address = { id: payload.addressId };
}
function addQueryItem(field: 'name' | 'firstName', queryItem: string) {
function addQueryItem(field: "name" | "firstName", queryItem: string) {
switch (field) {
case 'name':
case "name":
if (thirdParty.value.name) {
thirdParty.value.name += ` ${queryItem}`
thirdParty.value.name += ` ${queryItem}`;
} else {
thirdParty.value.name = queryItem
thirdParty.value.name = queryItem;
}
break
case 'firstName':
thirdParty.value.firstname = queryItem
break
break;
case "firstName":
thirdParty.value.firstname = queryItem;
break;
}
}
function addQuery(query: string) {
thirdParty.value.name = query
thirdParty.value.name = query;
}
const violations = useViolationList<WriteThirdPartyViolationMap>();
async function postThirdParty(): Promise<Thirdparty> {
try {
if (props.action === 'edit' && props.id) {
if (props.action === "edit" && props.id) {
const tp = await patchThirdparty(props.id, thirdParty.value);
return Promise.resolve(tp);
} else if (props.action === 'addContact' || props.action === 'create') {
} else if (props.action === "addContact" || props.action === "create") {
const tp = await createThirdParty(thirdParty.value);
emit('onThirdPartyCreated', {thirdParty: tp});
emit("onThirdPartyCreated", { thirdParty: tp });
return Promise.resolve(tp);
}
} catch (e: unknown) {
@@ -539,7 +566,6 @@ async function postThirdParty(): Promise<Thirdparty> {
}
throw "'action' is not edit with and id, or addContact or create";
}
</script>
<style scoped lang="scss">

View File

@@ -148,9 +148,8 @@ export type TicketHistoryLine =
| CallerStateEvent;
interface BaseTicket<
T extends
| "ticket_ticket:simple"
| "ticket_ticket:extended" = "ticket_ticket:simple",
T extends "ticket_ticket:simple" | "ticket_ticket:extended" =
"ticket_ticket:simple",
> {
type_extended: T;
type: "ticket_ticket";