Merge branch '1405-refactor-to-get-thirdparty' into 'ticket-app-master'

Refactor third-party type imports and update related components

See merge request Chill-Projet/chill-bundles!851
This commit is contained in:
Julien Fastré 2025-07-07 14:56:35 +00:00
commit 2a54d1b909
15 changed files with 1337 additions and 1397 deletions

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"tabWidth": 2,
"useTabs": false
}

View File

@ -1,287 +1,269 @@
import { GenericDoc } from "ChillDocStoreAssets/types/generic_doc";
export interface DateTime {
datetime: string;
datetime8601: string;
datetime: string;
datetime8601: string;
}
export interface Civility {
id: number;
// TODO
id: number;
// TODO
}
export interface Household {
type: "household";
id: number;
type: "household";
id: number;
}
export interface Job {
id: number;
type: "user_job";
label: {
fr: string; // could have other key. How to do that in ts ?
};
id: number;
type: "user_job";
label: {
fr: string; // could have other key. How to do that in ts ?
};
}
export interface Center {
id: number;
type: "center";
name: string;
id: number;
type: "center";
name: string;
}
export interface Scope {
id: number;
type: "scope";
name: {
fr: string;
};
id: number;
type: "scope";
name: {
fr: string;
};
}
export interface ResultItem<T> {
result: T;
relevance: number;
result: T;
relevance: number;
}
export interface User {
type: "user";
id: number;
username: string;
text: string;
text_without_absence: string;
email: string;
user_job: Job;
label: string;
// todo: mainCenter; mainJob; etc..
}
export interface ThirdParty {
type: "thirdparty";
id: number;
text: string;
firstname: string;
name: string;
email: string;
telephone: string;
telephone2: string;
address: {
address_id: number;
text: string;
postcode: {
name: string;
};
id: number;
};
type: "user";
id: number;
username: string;
text: string;
text_without_absence: string;
email: string;
user_job: Job;
label: string;
// todo: mainCenter; mainJob; etc..
}
export interface UserGroup {
type: "user_group";
id: number;
label: TranslatableString;
backgroundColor: string;
foregroundColor: string;
excludeKey: string;
text: string;
type: "user_group";
id: number;
label: TranslatableString;
backgroundColor: string;
foregroundColor: string;
excludeKey: string;
text: string;
}
export type UserGroupOrUser = User | UserGroup;
export interface UserAssociatedInterface {
type: "user";
id: number;
type: "user";
id: number;
}
export type TranslatableString = Record<string, string>;
export interface Postcode {
id: number;
name: string;
code: string;
center: Point;
id: number;
name: string;
code: string;
center: Point;
}
export interface Point {
type: "Point";
coordinates: [lat: number, lon: number];
type: "Point";
coordinates: [lat: number, lon: number];
}
export interface Country {
id: number;
name: TranslatableString;
code: string;
id: number;
name: TranslatableString;
code: string;
}
export type AddressRefStatus = "match" | "to_review" | "reviewed";
export interface Address {
type: "address";
address_id: number;
text: string;
street: string;
streetNumber: string;
postcode: Postcode;
country: Country;
floor: string | null;
corridor: string | null;
steps: string | null;
flat: string | null;
buildingName: string | null;
distribution: string | null;
extra: string | null;
confidential: boolean;
lines: string[];
addressReference: AddressReference | null;
validFrom: DateTime;
validTo: DateTime | null;
point: Point | null;
refStatus: AddressRefStatus;
isNoAddress: boolean;
type: "address";
address_id: number;
text: string;
street: string;
streetNumber: string;
postcode: Postcode;
country: Country;
floor: string | null;
corridor: string | null;
steps: string | null;
flat: string | null;
buildingName: string | null;
distribution: string | null;
extra: string | null;
confidential: boolean;
lines: string[];
addressReference: AddressReference | null;
validFrom: DateTime;
validTo: DateTime | null;
point: Point | null;
refStatus: AddressRefStatus;
isNoAddress: boolean;
}
export interface AddressWithPoint extends Address {
point: Point;
point: Point;
}
export interface AddressReference {
id: number;
createdAt: DateTime | null;
deletedAt: DateTime | null;
municipalityCode: string;
point: Point;
postcode: Postcode;
refId: string;
source: string;
street: string;
streetNumber: string;
updatedAt: DateTime | null;
id: number;
createdAt: DateTime | null;
deletedAt: DateTime | null;
municipalityCode: string;
point: Point;
postcode: Postcode;
refId: string;
source: string;
street: string;
streetNumber: string;
updatedAt: DateTime | null;
}
export interface SimpleGeographicalUnit {
id: number;
layerId: number;
unitName: string;
unitRefId: string;
id: number;
layerId: number;
unitName: string;
unitRefId: string;
}
export interface GeographicalUnitLayer {
id: number;
name: TranslatableString;
refId: string;
id: number;
name: TranslatableString;
refId: string;
}
export interface Location {
type: "location";
id: number;
active: boolean;
address: Address | null;
availableForUsers: boolean;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
email: string | null;
name: string;
phonenumber1: string | null;
phonenumber2: string | null;
locationType: LocationType;
type: "location";
id: number;
active: boolean;
address: Address | null;
availableForUsers: boolean;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
email: string | null;
name: string;
phonenumber1: string | null;
phonenumber2: string | null;
locationType: LocationType;
}
export interface LocationAssociated {
type: "location";
id: number;
type: "location";
id: number;
}
export interface LocationType {
type: "location-type";
id: number;
active: boolean;
addressRequired: "optional" | "required";
availableForUsers: boolean;
editableByUsers: boolean;
contactData: "optional" | "required";
title: TranslatableString;
type: "location-type";
id: number;
active: boolean;
addressRequired: "optional" | "required";
availableForUsers: boolean;
editableByUsers: boolean;
contactData: "optional" | "required";
title: TranslatableString;
}
export interface NewsItemType {
id: number;
title: string;
content: string;
startDate: DateTime;
endDate: DateTime | null;
id: number;
title: string;
content: string;
startDate: DateTime;
endDate: DateTime | null;
}
export interface WorkflowAvailable {
name: string;
text: string;
name: string;
text: string;
}
export interface WorkflowAttachment {
id: number;
relatedGenericDocKey: string;
relatedGenericDocIdentifiers: object;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
genericDoc: null | GenericDoc;
id: number;
relatedGenericDocKey: string;
relatedGenericDocIdentifiers: object;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
genericDoc: null | GenericDoc;
}
export interface PrivateCommentEmbeddable {
comments: Record<number, string>;
comments: Record<number, string>;
}
// API Exception types
export interface TransportExceptionInterface {
name: string;
name: string;
}
export interface ValidationExceptionInterface
extends TransportExceptionInterface {
name: "ValidationException";
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
extends TransportExceptionInterface {
name: "ValidationException";
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
}
export interface AccessExceptionInterface extends TransportExceptionInterface {
name: "AccessException";
violations: string[];
name: "AccessException";
violations: string[];
}
export interface NotFoundExceptionInterface
extends TransportExceptionInterface {
name: "NotFoundException";
extends TransportExceptionInterface {
name: "NotFoundException";
}
export interface ServerExceptionInterface extends TransportExceptionInterface {
name: "ServerException";
message: string;
code: number;
body: string;
name: "ServerException";
message: string;
code: number;
body: string;
}
export interface ConflictHttpExceptionInterface
extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
extends TransportExceptionInterface {
name: "ConflictHttpException";
violations: string[];
}
export type ApiException =
| ValidationExceptionInterface
| AccessExceptionInterface
| NotFoundExceptionInterface
| ServerExceptionInterface
| ConflictHttpExceptionInterface;
| ValidationExceptionInterface
| AccessExceptionInterface
| NotFoundExceptionInterface
| ServerExceptionInterface
| ConflictHttpExceptionInterface;
export interface Modal {
showModal: boolean;
modalDialogClass: string;
showModal: boolean;
modalDialogClass: string;
}
export interface Selected {
result: UserGroupOrUser;
result: UserGroupOrUser;
}
export interface addNewEntities {
selected: Selected[];
modal: Modal;
selected: Selected[];
modal: Modal;
}

View File

@ -1,97 +1,97 @@
<template>
<a v-if="isDisplayBadge" @click="openModal">
<span class="chill-entity" :class="badgeType">
{{ buttonText }}<span v-if="isDead"> ()</span>
</span>
</a>
<a
v-else
class="btn btn-sm"
target="_blank"
:class="classAction"
:title="trans(titleAction)"
@click="openModal"
<a v-if="isDisplayBadge" @click="openModal">
<span class="chill-entity" :class="badgeType">
{{ buttonText }}<span v-if="isDead"> ()</span>
</span>
</a>
<a
v-else
class="btn btn-sm"
target="_blank"
:class="classAction"
:title="trans(titleAction)"
@click="openModal"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
</a>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
{{ buttonText }}<span v-if="isDead"> ()</span>
</a>
<template #header>
<h3 v-if="parent" class="modal-title">
{{ trans(titleModal, { q: parent.text }) }}
</h3>
<h3 v-else class="modal-title">
{{ trans(titleModal) }}
</h3>
</template>
<teleport to="body">
<modal
v-if="modal.showModal"
:modal-dialog-class="modal.modalDialogClass"
@close="modal.showModal = false"
>
<template #header>
<h3 v-if="parent" class="modal-title">
{{ trans(titleModal, { q: parent.text }) }}
</h3>
<h3 v-else class="modal-title">
{{ trans(titleModal) }}
</h3>
</template>
<template #body v-if="type === 'person'">
<on-the-fly-person
:id="id"
:type="type"
:action="action"
ref="castPerson"
/>
<div v-if="hasResourceComment">
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-if="type === 'person'">
<on-the-fly-person
:id="id"
:type="type"
:action="action"
ref="castPerson"
/>
<div v-if="hasResourceComment">
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-else-if="type === 'thirdparty'">
<on-the-fly-thirdparty
:id="id"
:type="type"
:action="action"
ref="castThirdparty"
/>
<div v-if="hasResourceComment">
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-else-if="type === 'thirdparty'">
<on-the-fly-thirdparty
:id="id"
:type="type"
:action="action"
ref="castThirdparty"
/>
<div v-if="hasResourceComment">
<h3>{{ trans(ONTHEFLY_RESOURCE_COMMENT_TITLE) }}</h3>
<blockquote class="chill-user-quote">
{{ parent.comment }}
</blockquote>
</div>
</template>
<template #body v-else-if="parent">
<on-the-fly-thirdparty
:parent="parent"
:action="action"
type="thirdparty"
ref="castThirdparty"
/>
</template>
<template #body v-else-if="parent">
<on-the-fly-thirdparty
:parent="parent"
:action="action"
type="thirdparty"
ref="castThirdparty"
/>
</template>
<template #body v-else>
<on-the-fly-create
:action="action"
:allowed-types="allowedTypes"
:query="query"
ref="castNew"
/>
</template>
<template #body v-else>
<on-the-fly-create
:action="action"
:allowed-types="allowedTypes"
:query="query"
ref="castNew"
/>
</template>
<template #footer>
<a
v-if="action === 'show'"
:href="buildLocation(id, type)"
:title="trans(titleMessage)"
class="btn btn-show"
>{{ trans(buttonMessage) }}
</a>
<a v-else class="btn btn-save" @click="saveAction">
{{ trans(ACTION_SAVE) }}
</a>
</template>
</modal>
</teleport>
<template #footer>
<a
v-if="action === 'show'"
:href="buildLocation(id, type)"
:title="trans(titleMessage)"
class="btn btn-show"
>{{ trans(buttonMessage) }}
</a>
<a v-else class="btn btn-save" @click="saveAction">
{{ trans(ACTION_SAVE) }}
</a>
</template>
</modal>
</teleport>
</template>
<script setup>
import { ref, computed, defineEmits, defineProps } from "vue";
@ -100,45 +100,45 @@ import OnTheFlyCreate from "./Create.vue";
import OnTheFlyPerson from "ChillPersonAssets/vuejs/_components/OnTheFly/Person.vue";
import OnTheFlyThirdparty from "ChillThirdPartyAssets/vuejs/_components/OnTheFly/ThirdParty.vue";
import {
trans,
ACTION_SHOW,
ACTION_EDIT,
ACTION_CREATE,
ACTION_ADDCONTACT,
ONTHEFLY_CREATE_TITLE_DEFAULT,
ONTHEFLY_CREATE_TITLE_PERSON,
ONTHEFLY_CREATE_TITLE_THIRDPARTY,
ONTHEFLY_SHOW_PERSON,
ONTHEFLY_SHOW_THIRDPARTY,
ONTHEFLY_EDIT_PERSON,
ONTHEFLY_EDIT_THIRDPARTY,
ONTHEFLY_ADDCONTACT_TITLE,
ACTION_REDIRECT_PERSON,
ACTION_REDIRECT_THIRDPARTY,
ONTHEFLY_SHOW_FILE_PERSON,
ONTHEFLY_SHOW_FILE_THIRDPARTY,
ONTHEFLY_SHOW_FILE_DEFAULT,
ONTHEFLY_RESOURCE_COMMENT_TITLE,
ACTION_SAVE,
trans,
ACTION_SHOW,
ACTION_EDIT,
ACTION_CREATE,
ACTION_ADDCONTACT,
ONTHEFLY_CREATE_TITLE_DEFAULT,
ONTHEFLY_CREATE_TITLE_PERSON,
ONTHEFLY_CREATE_TITLE_THIRDPARTY,
ONTHEFLY_SHOW_PERSON,
ONTHEFLY_SHOW_THIRDPARTY,
ONTHEFLY_EDIT_PERSON,
ONTHEFLY_EDIT_THIRDPARTY,
ONTHEFLY_ADDCONTACT_TITLE,
ACTION_REDIRECT_PERSON,
ACTION_REDIRECT_THIRDPARTY,
ONTHEFLY_SHOW_FILE_PERSON,
ONTHEFLY_SHOW_FILE_THIRDPARTY,
ONTHEFLY_SHOW_FILE_DEFAULT,
ONTHEFLY_RESOURCE_COMMENT_TITLE,
ACTION_SAVE,
} from "translator";
const props = defineProps({
type: String,
id: [String, Number],
action: String,
buttonText: String,
displayBadge: Boolean,
isDead: Boolean,
parent: Object,
allowedTypes: Array,
query: String,
type: String,
id: [String, Number],
action: String,
buttonText: String,
displayBadge: Boolean,
isDead: Boolean,
parent: Object,
allowedTypes: Array,
query: String,
});
const emit = defineEmits(["saveFormOnTheFly"]);
const modal = ref({
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl",
});
const castPerson = ref();
@ -146,214 +146,209 @@ const castThirdparty = ref();
const castNew = ref();
const hasResourceComment = computed(() => {
return (
typeof props.parent !== "undefined" &&
props.parent !== null &&
props.action === "show" &&
props.parent.type === "accompanying_period_resource" &&
props.parent.comment !== null &&
props.parent.comment !== ""
);
return (
typeof props.parent !== "undefined" &&
props.parent !== null &&
props.action === "show" &&
props.parent.type === "accompanying_period_resource" &&
props.parent.comment !== null &&
props.parent.comment !== ""
);
});
const classAction = computed(() => {
switch (props.action) {
case "show":
return "btn-show";
case "edit":
return "btn-update";
case "create":
return "btn-create";
case "addContact":
return "btn-tpchild";
default:
return "";
}
switch (props.action) {
case "show":
return "btn-show";
case "edit":
return "btn-update";
case "create":
return "btn-create";
case "addContact":
return "btn-tpchild";
default:
return "";
}
});
const titleAction = computed(() => {
switch (props.action) {
case "show":
return ACTION_SHOW;
case "edit":
return ACTION_EDIT;
case "create":
return ACTION_CREATE;
case "addContact":
return ACTION_ADDCONTACT;
default:
return "";
}
switch (props.action) {
case "show":
return ACTION_SHOW;
case "edit":
return ACTION_EDIT;
case "create":
return ACTION_CREATE;
case "addContact":
return ACTION_ADDCONTACT;
default:
return "";
}
});
const titleCreate = computed(() => {
if (typeof props.allowedTypes === "undefined") {
return ONTHEFLY_CREATE_TITLE_DEFAULT;
}
return props.allowedTypes.every((t) => t === "person")
? ONTHEFLY_CREATE_TITLE_PERSON
: props.allowedTypes.every((t) => t === "thirdparty")
? ONTHEFLY_CREATE_TITLE_THIRDPARTY
: ONTHEFLY_CREATE_TITLE_DEFAULT;
if (typeof props.allowedTypes === "undefined") {
return ONTHEFLY_CREATE_TITLE_DEFAULT;
}
return props.allowedTypes.every((t) => t === "person")
? ONTHEFLY_CREATE_TITLE_PERSON
: props.allowedTypes.every((t) => t === "thirdparty")
? ONTHEFLY_CREATE_TITLE_THIRDPARTY
: ONTHEFLY_CREATE_TITLE_DEFAULT;
});
const titleModal = computed(() => {
switch (props.action) {
case "show":
if (props.type == "person") {
return ONTHEFLY_SHOW_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_SHOW_THIRDPARTY;
}
break;
case "edit":
if (props.type == "person") {
return ONTHEFLY_EDIT_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_EDIT_THIRDPARTY;
}
break;
case "create":
return titleCreate.value;
case "addContact":
return ONTHEFLY_ADDCONTACT_TITLE;
default:
break;
}
return "";
switch (props.action) {
case "show":
if (props.type == "person") {
return ONTHEFLY_SHOW_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_SHOW_THIRDPARTY;
}
break;
case "edit":
if (props.type == "person") {
return ONTHEFLY_EDIT_PERSON;
} else if (props.type == "thirdparty") {
return ONTHEFLY_EDIT_THIRDPARTY;
}
break;
case "create":
return titleCreate.value;
case "addContact":
return ONTHEFLY_ADDCONTACT_TITLE;
default:
break;
}
return "";
});
const titleMessage = computed(() => {
switch (props.type) {
case "person":
return ACTION_REDIRECT_PERSON;
case "thirdparty":
return ACTION_REDIRECT_THIRDPARTY;
default:
return "";
}
switch (props.type) {
case "person":
return ACTION_REDIRECT_PERSON;
case "thirdparty":
return ACTION_REDIRECT_THIRDPARTY;
default:
return "";
}
});
const buttonMessage = computed(() => {
switch (props.type) {
case "person":
return ONTHEFLY_SHOW_FILE_PERSON;
case "thirdparty":
return ONTHEFLY_SHOW_FILE_THIRDPARTY;
default:
return ONTHEFLY_SHOW_FILE_DEFAULT;
}
switch (props.type) {
case "person":
return ONTHEFLY_SHOW_FILE_PERSON;
case "thirdparty":
return ONTHEFLY_SHOW_FILE_THIRDPARTY;
default:
return ONTHEFLY_SHOW_FILE_DEFAULT;
}
});
const isDisplayBadge = computed(() => {
return props.displayBadge === true && props.buttonText !== null;
return props.displayBadge === true && props.buttonText !== null;
});
const badgeType = computed(() => {
return "entity-" + props.type + " badge-" + props.type;
return "entity-" + props.type + " badge-" + props.type;
});
const getReturnPath = computed(() => {
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
return `?returnPath=${window.location.pathname}${window.location.search}${window.location.hash}`;
});
function closeModal() {
modal.value.showModal = false;
modal.value.showModal = false;
}
function openModal() {
modal.value.showModal = true;
modal.value.showModal = true;
}
function changeActionTo(action) {
console.log(action);
// Not reactive in setup, but you can emit or use a ref if needed
console.log(action);
// Not reactive in setup, but you can emit or use a ref if needed
}
function saveAction() {
let type = props.type,
data = {};
switch (type) {
case "person":
data = castPerson.value?.$data.person;
break;
case "thirdparty":
data = castThirdparty.value?.$data.thirdparty;
break;
default:
if (typeof props.type === "undefined") {
if (props.action === "addContact") {
type = "thirdparty";
data = castThirdparty.value?.$data.thirdparty;
data.parent = {
type: "thirdparty",
id: props.parent.id,
};
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
: null;
data.profession =
data.profession !== "" ? data.profession : "";
} else {
type = castNew.value.radioType;
data = castNew.value.castDataByType();
if (
typeof data.civility !== "undefined" &&
null !== data.civility
) {
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
: null;
}
if (
typeof data.profession !== "undefined" &&
"" !== data.profession
) {
data.profession =
data.profession !== "" ? data.profession : "";
}
let type = props.type,
data = {};
switch (type) {
case "person":
data = castPerson.value?.$data.person;
break;
case "thirdparty":
data = castThirdparty.value?.$data.thirdparty;
break;
default:
if (typeof props.type === "undefined") {
if (props.action === "addContact") {
type = "thirdparty";
data = castThirdparty.value?.$data.thirdparty;
data.parent = {
type: "thirdparty",
id: props.parent.id,
};
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
} else {
throw "error with object type";
}
}
emit("saveFormOnTheFly", { type: type, data: data });
: null;
data.profession = data.profession !== "" ? data.profession : "";
} else {
type = castNew.value.radioType;
data = castNew.value.castDataByType();
if (typeof data.civility !== "undefined" && null !== data.civility) {
data.civility =
data.civility !== null
? {
type: "chill_main_civility",
id: data.civility.id,
}
: null;
}
if (
typeof data.profession !== "undefined" &&
"" !== data.profession
) {
data.profession = data.profession !== "" ? data.profession : "";
}
}
} else {
throw "error with object type";
}
}
emit("saveFormOnTheFly", { type: type, data: data });
}
function buildLocation(id, type) {
if (type === "person") {
return encodeURI(`/fr/person/${id}/general${getReturnPath.value}`);
} else if (type === "thirdparty") {
return encodeURI(`/fr/3party/3party/${id}/view${getReturnPath.value}`);
}
if (type === "person") {
return encodeURI(`/fr/person/${id}/general${getReturnPath.value}`);
} else if (type === "thirdparty") {
return encodeURI(`/fr/3party/3party/${id}/view${getReturnPath.value}`);
}
}
defineExpose({
openModal,
closeModal,
changeActionTo,
saveAction,
castPerson,
castThirdparty,
castNew,
hasResourceComment,
modal,
isDisplayBadge,
Modal,
openModal,
closeModal,
changeActionTo,
saveAction,
castPerson,
castThirdparty,
castNew,
hasResourceComment,
modal,
isDisplayBadge,
Modal,
});
</script>
<style lang="css" scoped>
a {
cursor: pointer;
cursor: pointer;
}
/* .btn-add-contact {

View File

@ -1,41 +1,37 @@
<template>
<div class="grey-card">
<ul :class="listClasses" v-if="picked.length && displayPicked">
<li
v-for="p in picked"
@click="removeEntity(p)"
:key="p.type + p.id"
>
<span
:class="getBadgeClass(p)"
class="chill_denomination"
:style="getBadgeStyle(p)"
>
{{ p.text }}
</span>
</li>
</ul>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:options="addPersonsOptions"
:key="uniqid"
:buttonTitle="translatedListOfTypes"
:modalTitle="translatedListOfTypes"
@addNewPersons="addNewEntity"
>
</add-persons>
</li>
</ul>
<div class="grey-card">
<ul :class="listClasses" v-if="picked.length && displayPicked">
<li v-for="p in picked" @click="removeEntity(p)" :key="p.type + p.id">
<span
:class="getBadgeClass(p)"
class="chill_denomination"
:style="getBadgeStyle(p)"
>
{{ p.text }}
</span>
</li>
</ul>
<ul class="record_actions">
<li class="add-persons">
<add-persons
:options="addPersonsOptions"
:key="uniqid"
:buttonTitle="translatedListOfTypes"
:modalTitle="translatedListOfTypes"
@addNewPersons="addNewEntity"
>
</add-persons>
</li>
</ul>
<ul class="badge-suggest add-items inline" style="float: right">
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)">
<span :class="getBadgeClass(s)" :style="getBadgeStyle(s)">
{{ s.text }}
</span>
</li>
</ul>
</div>
<ul class="badge-suggest add-items inline" style="float: right">
<li v-for="s in suggested" :key="s.id" @click="addNewSuggested(s)">
<span :class="getBadgeClass(s)" :style="getBadgeStyle(s)">
{{ s.text }}
</span>
</li>
</ul>
</div>
</template>
<script lang="ts" setup>
@ -43,135 +39,135 @@ import { ref, computed, defineProps, defineEmits, defineComponent } from "vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
import { Entities, EntityType, SearchOptions } from "ChillPersonAssets/types";
import {
PICK_ENTITY_MODAL_TITLE,
PICK_ENTITY_USER,
PICK_ENTITY_USER_GROUP,
PICK_ENTITY_PERSON,
PICK_ENTITY_THIRDPARTY,
trans,
PICK_ENTITY_MODAL_TITLE,
PICK_ENTITY_USER,
PICK_ENTITY_USER_GROUP,
PICK_ENTITY_PERSON,
PICK_ENTITY_THIRDPARTY,
trans,
} from "translator";
import { addNewEntities } from "ChillMainAssets/types";
defineComponent({
components: {
AddPersons,
},
components: {
AddPersons,
},
});
const props = defineProps<{
multiple: boolean;
types: EntityType[];
picked: Entities[];
uniqid: string;
removableIfSet?: boolean;
displayPicked?: boolean;
suggested?: Entities[];
label?: string;
multiple: boolean;
types: EntityType[];
picked: Entities[];
uniqid: string;
removableIfSet?: boolean;
displayPicked?: boolean;
suggested?: Entities[];
label?: string;
}>();
const emits = defineEmits<{
(e: "addNewEntity", payload: { entity: Entities }): void;
(e: "removeEntity", payload: { entity: Entities }): void;
(e: "addNewEntityProcessEnded"): void;
(e: "addNewEntity", payload: { entity: Entities }): void;
(e: "removeEntity", payload: { entity: Entities }): void;
(e: "addNewEntityProcessEnded"): void;
}>();
const addPersons = ref();
const addPersonsOptions = computed(
() =>
({
uniq: !props.multiple,
type: props.types,
priority: null,
button: {
size: "btn-sm",
class: "btn-submit",
},
}) as SearchOptions,
() =>
({
uniq: !props.multiple,
type: props.types,
priority: null,
button: {
size: "btn-sm",
class: "btn-submit",
},
}) as SearchOptions,
);
const translatedListOfTypes = computed(() => {
if (props.label !== undefined && props.label !== "") {
return props.label;
if (props.label !== undefined && props.label !== "") {
return props.label;
}
const translatedTypes = props.types.map((type: EntityType) => {
switch (type) {
case "user":
return trans(PICK_ENTITY_USER, {
count: props.multiple ? 2 : 1,
});
case "person":
return trans(PICK_ENTITY_PERSON, {
count: props.multiple ? 2 : 1,
});
case "thirdparty":
return trans(PICK_ENTITY_THIRDPARTY, {
count: props.multiple ? 2 : 1,
});
case "user_group":
return trans(PICK_ENTITY_USER_GROUP, {
count: props.multiple ? 2 : 1,
});
}
});
const translatedTypes = props.types.map((type: EntityType) => {
switch (type) {
case "user":
return trans(PICK_ENTITY_USER, {
count: props.multiple ? 2 : 1,
});
case "person":
return trans(PICK_ENTITY_PERSON, {
count: props.multiple ? 2 : 1,
});
case "third_party":
return trans(PICK_ENTITY_THIRDPARTY, {
count: props.multiple ? 2 : 1,
});
case "user_group":
return trans(PICK_ENTITY_USER_GROUP, {
count: props.multiple ? 2 : 1,
});
}
});
return `${trans(PICK_ENTITY_MODAL_TITLE, {
count: props.multiple ? 2 : 1,
})} ${translatedTypes.join(", ")}`;
return `${trans(PICK_ENTITY_MODAL_TITLE, {
count: props.multiple ? 2 : 1,
})} ${translatedTypes.join(", ")}`;
});
const listClasses = computed(() => ({
"badge-suggest": true,
"remove-items": props.removableIfSet !== false,
inline: true,
"badge-suggest": true,
"remove-items": props.removableIfSet !== false,
inline: true,
}));
function addNewSuggested(entity: Entities) {
emits("addNewEntity", { entity });
emits("addNewEntity", { entity });
}
function addNewEntity({ selected }: addNewEntities) {
Object.values(selected).forEach((item) => {
emits("addNewEntity", { entity: item.result });
});
addPersons.value?.resetSearch();
Object.values(selected).forEach((item) => {
emits("addNewEntity", { entity: item.result });
});
addPersons.value?.resetSearch();
emits("addNewEntityProcessEnded");
emits("addNewEntityProcessEnded");
}
function removeEntity(entity: Entities) {
if (props.removableIfSet === false) {
return;
}
emits("removeEntity", { entity });
if (props.removableIfSet === false) {
return;
}
emits("removeEntity", { entity });
}
function getBadgeClass(entities: Entities) {
if (entities.type !== "user_group") {
return entities.type;
}
return "";
if (entities.type !== "user_group") {
return entities.type;
}
return "";
}
function getBadgeStyle(entities: Entities) {
if (entities.type === "user_group") {
return [
`ul.badge-suggest li > span {
if (entities.type === "user_group") {
return [
`ul.badge-suggest li > span {
color: ${entities.foregroundColor}!important;
border-bottom-color: ${entities.backgroundColor};
}`,
];
}
return [];
];
}
return [];
}
</script>
<style lang="scss" scoped>
.grey-card {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
min-height: 160px;
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
min-height: 160px;
}
.btn-check:checked + .btn,
@ -179,94 +175,94 @@ function getBadgeStyle(entities: Entities) {
.btn:first-child:active,
.btn.active,
.btn.show {
color: white;
box-shadow: 0 0 0 0.2rem var(--bs-chill-green);
outline: 0;
color: white;
box-shadow: 0 0 0 0.2rem var(--bs-chill-green);
outline: 0;
}
.as-user-group {
display: inline-block;
display: inline-block;
}
ul.badge-suggest {
list-style-type: none;
padding-left: 0;
margin-bottom: 0px;
list-style-type: none;
padding-left: 0;
margin-bottom: 0px;
}
ul.badge-suggest li > span {
white-space: normal;
text-align: start;
margin-bottom: 3px;
white-space: normal;
text-align: start;
margin-bottom: 3px;
}
ul.badge-suggest.inline li {
display: inline-block;
margin-right: 0.2em;
display: inline-block;
margin-right: 0.2em;
}
ul.badge-suggest.add-items li {
position: relative;
position: relative;
}
ul.badge-suggest.add-items li span {
cursor: pointer;
padding-left: 2rem;
cursor: pointer;
padding-left: 2rem;
}
ul.badge-suggest.add-items li span:hover {
color: #ced4da;
color: #ced4da;
}
ul.badge-suggest.add-items li > span:before {
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f067";
color: var(--bs-success);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f067";
color: var(--bs-success);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
ul.badge-suggest.remove-items li {
position: relative;
position: relative;
}
ul.badge-suggest.remove-items li span {
cursor: pointer;
padding-left: 2rem;
cursor: pointer;
padding-left: 2rem;
}
ul.badge-suggest.remove-items li span:hover {
color: #ced4da;
color: #ced4da;
}
ul.badge-suggest.remove-items li > span:before {
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f1f8";
color: var(--bs-danger);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
font: normal normal normal 13px ForkAwesome;
margin-right: 1.8em;
content: "\f1f8";
color: var(--bs-danger);
position: absolute;
display: block;
top: 50%;
left: 0.75rem;
-webkit-transform: translateY(-50%);
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
ul.badge-suggest li > span {
margin: 0.2rem 0.1rem;
display: inline-block;
padding: 0 1em 0 2.2em !important;
background-color: #fff;
border: 1px solid #dee2e6;
border-bottom-width: 3px;
border-bottom-style: solid;
border-radius: 6px;
font-size: 0.75em;
font-weight: 700;
margin: 0.2rem 0.1rem;
display: inline-block;
padding: 0 1em 0 2.2em !important;
background-color: #fff;
border: 1px solid #dee2e6;
border-bottom-width: 3px;
border-bottom-style: solid;
border-radius: 6px;
font-size: 0.75em;
font-weight: 700;
}
ul.badge-suggest li > span.person {
border-bottom-color: #43b29d;
border-bottom-color: #43b29d;
}
ul.badge-suggest li > span.thirdparty {
border-bottom-color: rgb(198.9, 72, 98.1);
border-bottom-color: rgb(198.9, 72, 98.1);
}
</style>

View File

@ -1,309 +1,315 @@
import { StoredObject } from "ChillDocStoreAssets/types";
import {
Address,
Center,
Civility,
DateTime,
User,
UserGroup,
Household,
ThirdParty,
WorkflowAvailable,
Scope,
Job,
PrivateCommentEmbeddable,
Address,
Center,
Civility,
DateTime,
User,
UserGroup,
Household,
WorkflowAvailable,
Scope,
Job,
PrivateCommentEmbeddable,
} from "../../../ChillMainBundle/Resources/public/types";
import { Thirdparty } from "../../../ChillThirdPartyBundle/Resources/public/types";
import { Calendar } from "../../../ChillCalendarBundle/Resources/public/types";
export interface AltName {
label: string;
key: string;
}
export interface Person {
id: number;
type: "person";
text: string;
textAge: string;
firstName: string;
lastName: string;
current_household_address: Address | null;
birthdate: DateTime | null;
deathdate: DateTime | null;
age: number;
phonenumber: string;
mobilenumber: string;
email: string;
gender: "woman" | "man" | "other";
centers: Center[];
civility: Civility | null;
current_household_id: number;
current_residential_addresses: Address[];
id: number;
type: "person";
text: string;
textAge: string;
firstName: string;
lastName: string;
altNames: AltName[];
suffixText: string;
current_household_address: Address | null;
birthdate: DateTime | null;
deathdate: DateTime | null;
age: number;
phonenumber: string;
mobilenumber: string;
email: string;
gender: "woman" | "man" | "other";
centers: Center[];
civility: Civility | null;
current_household_id: number;
current_residential_addresses: Address[];
}
export interface AccompanyingPeriod {
id: number;
addressLocation?: Address | null;
administrativeLocation?: Location | null;
calendars: Calendar[];
closingDate?: Date | null;
closingMotive?: ClosingMotive | null;
comments: Comment[];
confidential: boolean;
createdAt?: Date | null;
createdBy?: User | null;
emergency: boolean;
intensity?: "occasional" | "regular";
job?: Job | null;
locationHistories: AccompanyingPeriodLocationHistory[];
openingDate?: Date | null;
origin?: Origin | null;
participations: AccompanyingPeriodParticipation[];
personLocation?: Person | null;
pinnedComment?: Comment | null;
preventUserIsChangedNotification: boolean;
remark: string;
requestorAnonymous: boolean;
requestorPerson?: Person | null;
requestorThirdParty?: Thirdparty | null;
resources: AccompanyingPeriodResource[];
scopes: Scope[];
socialIssues: SocialIssue[];
step?:
| "CLOSED"
| "CONFIRMED"
| "CONFIRMED_INACTIVE_SHORT"
| "CONFIRMED_INACTIVE_LONG"
| "DRAFT";
id: number;
addressLocation?: Address | null;
administrativeLocation?: Location | null;
calendars: Calendar[];
closingDate?: Date | null;
closingMotive?: ClosingMotive | null;
comments: Comment[];
confidential: boolean;
createdAt?: Date | null;
createdBy?: User | null;
emergency: boolean;
intensity?: "occasional" | "regular";
job?: Job | null;
locationHistories: AccompanyingPeriodLocationHistory[];
openingDate?: Date | null;
origin?: Origin | null;
participations: AccompanyingPeriodParticipation[];
personLocation?: Person | null;
pinnedComment?: Comment | null;
preventUserIsChangedNotification: boolean;
remark: string;
requestorAnonymous: boolean;
requestorPerson?: Person | null;
requestorThirdParty?: Thirdparty | null;
resources: AccompanyingPeriodResource[];
scopes: Scope[];
socialIssues: SocialIssue[];
step?:
| "CLOSED"
| "CONFIRMED"
| "CONFIRMED_INACTIVE_SHORT"
| "CONFIRMED_INACTIVE_LONG"
| "DRAFT";
}
export interface AccompanyingPeriodWork {
id: number;
accompanyingPeriod?: AccompanyingPeriod;
accompanyingPeriodWorkEvaluations: AccompanyingPeriodWorkEvaluation[];
createdAt?: string;
createdAutomatically: boolean;
createdAutomaticallyReason: string;
createdBy: User;
endDate?: string;
goals: AccompanyingPeriodWorkGoal[];
handlingThierParty?: Thirdparty;
note: string;
persons: Person[];
privateComment: PrivateCommentEmbeddable;
referrersHistory: AccompanyingPeriodWorkReferrerHistory[];
results: Result[];
socialAction?: SocialAction;
startDate?: string;
thirdParties: Thirdparty[];
updatedAt?: string;
updatedBy: User;
version: number;
id: number;
accompanyingPeriod?: AccompanyingPeriod;
accompanyingPeriodWorkEvaluations: AccompanyingPeriodWorkEvaluation[];
createdAt?: string;
createdAutomatically: boolean;
createdAutomaticallyReason: string;
createdBy: User;
endDate?: string;
goals: AccompanyingPeriodWorkGoal[];
handlingThierParty?: Thirdparty;
note: string;
persons: Person[];
privateComment: PrivateCommentEmbeddable;
referrersHistory: AccompanyingPeriodWorkReferrerHistory[];
results: Result[];
socialAction?: SocialAction;
startDate?: string;
thirdParties: Thirdparty[];
updatedAt?: string;
updatedBy: User;
version: number;
}
export interface SocialAction {
id: number;
parent?: SocialAction | null;
children: SocialAction[];
issue?: SocialIssue | null;
ordering: number;
title: {
fr: string;
};
defaultNotificationDelay?: string | null;
desactivationDate?: string | null;
evaluations: Evaluation[];
goals: Goal[];
results: Result[];
id: number;
parent?: SocialAction | null;
children: SocialAction[];
issue?: SocialIssue | null;
ordering: number;
title: {
fr: string;
};
defaultNotificationDelay?: string | null;
desactivationDate?: string | null;
evaluations: Evaluation[];
goals: Goal[];
results: Result[];
}
export interface AccompanyingPeriodResource {
id: number;
accompanyingPeriod: AccompanyingPeriod;
comment?: string | null;
person?: Person | null;
thirdParty?: Thirdparty | null;
id: number;
accompanyingPeriod: AccompanyingPeriod;
comment?: string | null;
person?: Person | null;
thirdParty?: Thirdparty | null;
}
export interface Origin {
id: number;
label: {
fr: string;
};
noActiveAfter: DateTime;
id: number;
label: {
fr: string;
};
noActiveAfter: DateTime;
}
export interface ClosingMotive {
id: number;
active: boolean;
name: {
fr: string;
};
ordering: number;
isCanceledAccompanyingPeriod: boolean;
parent?: ClosingMotive | null;
children: ClosingMotive[];
id: number;
active: boolean;
name: {
fr: string;
};
ordering: number;
isCanceledAccompanyingPeriod: boolean;
parent?: ClosingMotive | null;
children: ClosingMotive[];
}
export interface AccompanyingPeriodParticipation {
id: number;
startDate: DateTime;
endDate?: DateTime | null;
accompanyingPeriod: AccompanyingPeriod;
person: Person;
id: number;
startDate: DateTime;
endDate?: DateTime | null;
accompanyingPeriod: AccompanyingPeriod;
person: Person;
}
export interface AccompanyingPeriodLocationHistory {
id: number;
startDate: DateTime;
endDate?: DateTime | null;
addressLocation?: Address | null;
period: AccompanyingPeriod;
personLocation?: Person | null;
id: number;
startDate: DateTime;
endDate?: DateTime | null;
addressLocation?: Address | null;
period: AccompanyingPeriod;
personLocation?: Person | null;
}
export interface SocialIssue {
id: number;
parent?: SocialIssue | null;
children: SocialIssue[];
socialActions?: SocialAction[] | null;
ordering: number;
title: {
fr: string;
};
desactivationDate?: string | null;
id: number;
parent?: SocialIssue | null;
children: SocialIssue[];
socialActions?: SocialAction[] | null;
ordering: number;
title: {
fr: string;
};
desactivationDate?: string | null;
}
export interface Goal {
id: number;
results: Result[];
socialActions?: SocialAction[] | null;
title: {
fr: string;
};
id: number;
results: Result[];
socialActions?: SocialAction[] | null;
title: {
fr: string;
};
}
export interface AccompanyingPeriodWorkGoal {
id: number;
accompanyingPeriodWork: AccompanyingPeriodWork;
goal: Goal;
note: string;
results: Result[];
id: number;
accompanyingPeriodWork: AccompanyingPeriodWork;
goal: Goal;
note: string;
results: Result[];
}
export interface AccompanyingPeriodWorkEvaluation {
accompanyingPeriodWork: AccompanyingPeriodWork | null;
comment: string;
createdAt: DateTime | null;
createdBy: User | null;
documents: AccompanyingPeriodWorkEvaluationDocument[];
endDate: DateTime | null;
evaluation: Evaluation | null;
id: number | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
key: any;
maxDate: DateTime | null;
startDate: DateTime | null;
updatedAt: DateTime | null;
updatedBy: User | null;
warningInterval: string | null;
timeSpent: number | null;
accompanyingPeriodWork: AccompanyingPeriodWork | null;
comment: string;
createdAt: DateTime | null;
createdBy: User | null;
documents: AccompanyingPeriodWorkEvaluationDocument[];
endDate: DateTime | null;
evaluation: Evaluation | null;
id: number | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
key: any;
maxDate: DateTime | null;
startDate: DateTime | null;
updatedAt: DateTime | null;
updatedBy: User | null;
warningInterval: string | null;
timeSpent: number | null;
}
export interface Evaluation {
id: number;
url: string;
socialActions: SocialAction[];
title: {
fr: string;
};
active: boolean;
delay: string;
notificationDelay: string;
id: number;
url: string;
socialActions: SocialAction[];
title: {
fr: string;
};
active: boolean;
delay: string;
notificationDelay: string;
}
export interface AccompanyingPeriodWorkReferrerHistory {
id: number;
accompanyingPeriodWork: AccompanyingPeriodWork;
user: User;
startDate: DateTime;
endDate: DateTime | null;
createdAt: DateTime;
updatedAt: DateTime | null;
createdBy: User;
updatedBy: User | null;
id: number;
accompanyingPeriodWork: AccompanyingPeriodWork;
user: User;
startDate: DateTime;
endDate: DateTime | null;
createdAt: DateTime;
updatedAt: DateTime | null;
createdBy: User;
updatedBy: User | null;
}
export interface AccompanyingPeriodWorkEvaluationDocument {
id: number;
type: "accompanying_period_work_evaluation_document";
storedObject: StoredObject;
title: string;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
workflows_availables: WorkflowAvailable[];
workflows: object[];
id: number;
type: "accompanying_period_work_evaluation_document";
storedObject: StoredObject;
title: string;
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
workflows_availables: WorkflowAvailable[];
workflows: object[];
}
export type EntityType =
| "user_group"
| "user"
| "person"
| "third_party"
| "household";
| "user_group"
| "user"
| "person"
| "thirdparty"
| "household";
export type Entities = (UserGroup | User | Person | ThirdParty | Household) & {
address?: Address | null;
kind?: string;
text?: string;
profession?: string;
export type Entities = (UserGroup | User | Person | Thirdparty | Household) & {
address?: Address | null;
kind?: string;
text?: string;
profession?: string;
};
export type Result = Entities & {
parent?: Entities | null;
parent?: Entities | null;
};
export interface Suggestion {
key: string;
relevance: number;
result: Result;
key: string;
relevance: number;
result: Result;
}
export interface SearchPagination {
first: number;
items_per_page: number;
next: number | null;
previous: number | null;
more: boolean;
first: number;
items_per_page: number;
next: number | null;
previous: number | null;
more: boolean;
}
export interface Search {
count: number;
pagination: SearchPagination;
results: Suggestion[];
count: number;
pagination: SearchPagination;
results: Suggestion[];
}
export interface SearchOptions {
uniq: boolean;
type: string[];
priority: number | null;
button: {
size: string;
class: string;
type: string;
display: string;
};
uniq: boolean;
type: string[];
priority: number | null;
button: {
size: string;
class: string;
type: string;
display: string;
};
}
export class MakeFetchException extends Error {
sta: number;
txt: string;
violations: unknown | null;
sta: number;
txt: string;
violations: unknown | null;
constructor(txt: string, sta: number, violations: unknown | null = null) {
super(txt);
this.name = "ValidationException";
this.sta = sta;
this.txt = txt;
this.violations = violations;
Object.setPrototypeOf(this, MakeFetchException.prototype);
}
constructor(txt: string, sta: number, violations: unknown | null = null) {
super(txt);
this.name = "ValidationException";
this.sta = sta;
this.txt = txt;
this.violations = violations;
Object.setPrototypeOf(this, MakeFetchException.prototype);
}
}

View File

@ -23,6 +23,7 @@ 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";
function formatDate(dateString: string | undefined, format: string) {
if (!dateString) return "";
@ -36,15 +37,7 @@ function formatDate(dateString: string | undefined, format: string) {
const props = defineProps<{
item: {
result: {
id: number | string;
birthdate: { datetime: string } | null;
current_household_address: {
text: string;
postcode: { name: string };
} | null;
// add other fields as needed
};
result: Person; // add other fields as needed
};
}>();

View File

@ -36,8 +36,8 @@ 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 { ThirdParty } from "ChillMainAssets/types";
import { Result, Suggestion } from "ChillPersonAssets/types";
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
interface TypeThirdPartyProps {
item: Suggestion;
@ -76,7 +76,7 @@ const getAddress = computed(() => {
return null;
});
function saveFormOnTheFly({ data }: { data: ThirdParty }) {
function saveFormOnTheFly({ data }: { data: Thirdparty }) {
makeFetch("POST", "/api/1.0/thirdparty/thirdparty.json", data)
.then((response: unknown) => {
const result = response as Result;

View File

@ -23,22 +23,7 @@
<script lang="ts" setup>
import { computed, toRefs } from "vue";
import { trans, RENDERBOX_YEARS_OLD } from "translator";
interface AltName {
label: string;
key: string;
}
interface Person {
firstName: string;
lastName: string;
altNames: AltName[];
suffixText?: string;
birthdate: string | null;
deathdate: string | null;
age: number;
text: string;
}
import { AltName, Person } from "ChillPersonAssets/types";
const props = defineProps<{
person: Person;

View File

@ -1,47 +1,49 @@
import {
Address,
Center,
Civility,
DateTime,
User,
Address,
Center,
Civility,
DateTime,
User,
} from "ChillMainAssets/types";
export interface Thirdparty {
acronym: string | null;
active: boolean;
address: Address | null;
canonicalized: string | null;
categories: ThirdpartyCategory[];
centers: Center[];
children: Thirdparty[];
civility: Civility | null;
comment: string | null;
contactDataAnonymous: boolean;
createdAt: DateTime;
createdBy: User | null;
email: string | null;
firstname: string | null;
id: number | null;
kind: string;
name: string;
nameCompany: string | null;
parent: Thirdparty | null;
profession: string;
telephone: string | null;
thirdPartyTypes: ThirdpartyType[] | null;
updatedAt: DateTime | null;
updatedBy: User | null;
type: "thirdparty";
text: string;
acronym: string | null;
active: boolean;
address: Address | null;
canonicalized: string | null;
categories: ThirdpartyCategory[];
centers: Center[];
children: Thirdparty[];
civility: Civility | null;
comment: string | null;
contactDataAnonymous: boolean;
createdAt: DateTime;
createdBy: User | null;
email: string | null;
firstname: string | null;
id: number | null;
kind: string;
name: string;
nameCompany: string | null;
parent: Thirdparty | null;
profession: string;
telephone: string | null;
thirdPartyTypes: ThirdpartyType[] | null;
updatedAt: DateTime | null;
updatedBy: User | null;
}
interface ThirdpartyType {
key: string;
value: string;
key: string;
value: string;
}
export interface ThirdpartyCategory {
id: number;
active: boolean;
name: {
fr: string;
};
id: number;
active: boolean;
name: {
fr: string;
};
}

View File

@ -1,17 +1,17 @@
import {
DateTime,
TranslatableString,
User,
UserGroupOrUser,
DateTime,
TranslatableString,
User,
UserGroupOrUser,
} from "ChillMainAssets/types";
import { Person } from "ChillPersonAssets/types";
import {Thirdparty} from "../../../../ChillThirdPartyBundle/Resources/public/types";
import { Thirdparty } from "../../../../ChillThirdPartyBundle/Resources/public/types";
export interface Motive {
type: "ticket_motive";
id: number;
active: boolean;
label: TranslatableString;
type: "ticket_motive";
id: number;
active: boolean;
label: TranslatableString;
}
export type TicketState = "open" | "closed";
@ -19,134 +19,135 @@ export type TicketState = "open" | "closed";
export type TicketEmergencyState = "yes" | "no";
interface TicketHistory<T extends string, D extends object> {
event_type: T;
at: DateTime;
by: User;
data: D;
event_type: T;
at: DateTime;
by: User;
data: D;
}
export interface PersonHistory {
type: "ticket_person_history";
id: number;
startDate: DateTime;
endDate: null | DateTime;
person: Person;
removedBy: null;
createdBy: User | null;
createdAt: DateTime | null;
type: "ticket_person_history";
id: number;
startDate: DateTime;
endDate: null | DateTime;
person: Person;
removedBy: null;
createdBy: User | null;
createdAt: DateTime | null;
}
export interface MotiveHistory {
type: "ticket_motive_history";
id: number;
startDate: null;
endDate: null | DateTime;
motive: Motive;
createdBy: User | null;
createdAt: DateTime | null;
type: "ticket_motive_history";
id: number;
startDate: null;
endDate: null | DateTime;
motive: Motive;
createdBy: User | null;
createdAt: DateTime | null;
}
export interface Comment {
type: "ticket_comment";
id: number;
content: string;
createdBy: User | null;
createdAt: DateTime | null;
updatedBy: User | null;
updatedAt: DateTime | null;
type: "ticket_comment";
id: number;
content: string;
createdBy: User | null;
createdAt: DateTime | null;
updatedBy: User | null;
updatedAt: DateTime | null;
}
export interface AddresseeHistory {
type: "ticket_addressee_history";
id: number;
startDate: DateTime | null;
addressee: UserGroupOrUser;
endDate: DateTime | null;
removedBy: User | null;
createdBy: User | null;
createdAt: DateTime | null;
updatedBy: User | null;
updatedAt: DateTime | null;
type: "ticket_addressee_history";
id: number;
startDate: DateTime | null;
addressee: UserGroupOrUser;
endDate: DateTime | null;
removedBy: User | null;
createdBy: User | null;
createdAt: DateTime | null;
updatedBy: User | null;
updatedAt: DateTime | null;
}
export interface AddresseeState {
addressees: UserGroupOrUser[];
addressees: UserGroupOrUser[];
}
export interface PersonsState {
persons: Person[];
persons: Person[];
}
export interface CallerState {
new_caller: Person | null;
new_caller: Person | Thirdparty | null;
}
export interface StateChange {
new_state: TicketState;
new_state: TicketState;
}
export interface EmergencyChange {
new_emergency: TicketEmergencyState;
new_emergency: TicketEmergencyState;
}
export interface CreateTicketState {
by: User;
by: User;
}
export type AddCommentEvent = TicketHistory<"add_comment", Comment>;
export type SetMotiveEvent = TicketHistory<"set_motive", MotiveHistory>;
export type AddPersonEvent = TicketHistory<"add_person", PersonHistory>;
export type AddresseesStateEvent = TicketHistory<
"addressees_state",
AddresseeState
"addressees_state",
AddresseeState
>;
export type CreateTicketEvent = TicketHistory<
"create_ticket",
CreateTicketState
"create_ticket",
CreateTicketState
>;
export type PersonStateEvent = TicketHistory<"persons_state", PersonsState>;
export type ChangeStateEvent = TicketHistory<"state_change", StateChange>;
export type EmergencyStateEvent = TicketHistory<
"emergency_change",
EmergencyChange
"emergency_change",
EmergencyChange
>;
export type CallerStateEvent = TicketHistory<"set_caller", CallerState>;
export type TicketHistoryLine =
| AddPersonEvent
| CreateTicketEvent
| AddCommentEvent
| SetMotiveEvent
| AddresseesStateEvent
| PersonStateEvent
| ChangeStateEvent
| EmergencyStateEvent
| CallerStateEvent;
| AddPersonEvent
| CreateTicketEvent
| AddCommentEvent
| SetMotiveEvent
| AddresseesStateEvent
| PersonStateEvent
| ChangeStateEvent
| EmergencyStateEvent
| CallerStateEvent;
interface BaseTicket<T extends "ticket_ticket:simple"|"ticket_ticket:extended" = "ticket_ticket:simple"> {
type_extended: T;
type: "ticket_ticket";
id: number;
externalRef: string;
currentAddressees: UserGroupOrUser[];
currentPersons: Person[];
currentMotive: null | Motive;
currentState: TicketState | null;
emergency: TicketEmergencyState | null;
caller: Person | Thirdparty | null;
}
export interface SimpleTicket extends BaseTicket<"ticket_ticket:simple"> {
interface BaseTicket<
T extends
| "ticket_ticket:simple"
| "ticket_ticket:extended" = "ticket_ticket:simple",
> {
type_extended: T;
type: "ticket_ticket";
id: number;
externalRef: string;
currentAddressees: UserGroupOrUser[];
currentPersons: Person[];
currentMotive: null | Motive;
currentState: TicketState | null;
emergency: TicketEmergencyState | null;
caller: Person | Thirdparty | null;
}
export interface TicketSimple extends BaseTicket<"ticket_ticket:simple"> {
type_extended: "ticket_ticket:simple";
type_extended: "ticket_ticket:simple";
}
export interface Ticket extends BaseTicket<"ticket_ticket:extended"> {
type_extended: "ticket_ticket:extended";
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
history: TicketHistoryLine[];
type_extended: "ticket_ticket:extended";
createdAt: DateTime | null;
createdBy: User | null;
updatedAt: DateTime | null;
updatedBy: User | null;
history: TicketHistoryLine[];
}

View File

@ -1,20 +1,20 @@
<template>
<div class="row">
<div class="col-12">
<pick-entity
uniqid="ticket-addressee-selector"
:types="['user', 'user_group', 'third_party']"
:picked="selectedEntities"
:suggested="suggestedValues"
:multiple="true"
:removable-if-set="true"
:display-picked="true"
:label="trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_USER_LABEL)"
@add-new-entity="addNewEntity"
@remove-entity="removeEntity"
/>
</div>
<div class="row">
<div class="col-12">
<pick-entity
uniqid="ticket-addressee-selector"
:types="['user', 'user_group', 'thirdparty']"
:picked="selectedEntities"
:suggested="suggestedValues"
:multiple="true"
:removable-if-set="true"
:display-picked="true"
:label="trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_USER_LABEL)"
@add-new-entity="addNewEntity"
@remove-entity="removeEntity"
/>
</div>
</div>
</template>
<script lang="ts" setup>
@ -28,13 +28,13 @@ import { Entities } from "ChillPersonAssets/types";
// Translations
import {
CHILL_TICKET_TICKET_ADD_ADDRESSEE_USER_LABEL,
trans,
CHILL_TICKET_TICKET_ADD_ADDRESSEE_USER_LABEL,
trans,
} from "translator";
const props = defineProps<{
modelValue: Entities[];
suggested: Entities[];
modelValue: Entities[];
suggested: Entities[];
}>();
const emit = defineEmits<(e: "update:modelValue", value: Entities[]) => void>();
@ -42,53 +42,46 @@ const emit = defineEmits<(e: "update:modelValue", value: Entities[]) => void>();
const selectedEntities = ref<Entities[]>([...props.modelValue]);
const suggestedValues = ref<Entities[]>([...props.suggested]);
watch(
() => [props.suggested, props.modelValue],
() => {
const modelValue = props.modelValue ?? [];
() => [props.suggested, props.modelValue],
() => {
const modelValue = props.modelValue ?? [];
suggestedValues.value = props.suggested.filter(
(suggested: Entities) => {
return !modelValue.some((selected: Entities) => {
if (
suggested.type == "user_group" &&
selected.type == "user_group"
) {
switch (selected.excludeKey) {
case "level":
return suggested.excludeKey === "level";
case "":
return (
suggested.excludeKey === "" &&
suggested.id === selected.id
);
default:
return false;
}
} else {
return (
suggested.type === selected.type &&
suggested.id === selected.id
);
}
});
},
);
},
{ immediate: true, deep: true },
suggestedValues.value = props.suggested.filter((suggested: Entities) => {
return !modelValue.some((selected: Entities) => {
if (suggested.type == "user_group" && selected.type == "user_group") {
switch (selected.excludeKey) {
case "level":
return suggested.excludeKey === "level";
case "":
return (
suggested.excludeKey === "" && suggested.id === selected.id
);
default:
return false;
}
} else {
return (
suggested.type === selected.type && suggested.id === selected.id
);
}
});
});
},
{ immediate: true, deep: true },
);
function addNewEntity({ entity }: { entity: Entities }) {
selectedEntities.value.push(entity);
emit("update:modelValue", selectedEntities.value);
selectedEntities.value.push(entity);
emit("update:modelValue", selectedEntities.value);
}
function removeEntity({ entity }: { entity: Entities }) {
const index = selectedEntities.value.findIndex(
(selectedEntity) => selectedEntity === entity,
);
if (index !== -1) {
selectedEntities.value.splice(index, 1);
}
emit("update:modelValue", selectedEntities.value);
const index = selectedEntities.value.findIndex(
(selectedEntity) => selectedEntity === entity,
);
if (index !== -1) {
selectedEntities.value.splice(index, 1);
}
emit("update:modelValue", selectedEntities.value);
}
</script>

View File

@ -1,109 +1,97 @@
<template>
<Teleport to="#header-ticket-main">
<div class="container-xxl text-primary">
<div class="row">
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
<h1 v-if="ticket.currentMotive">
#{{ ticket.id }} {{ ticket.currentMotive.label.fr }}
</h1>
<p class="chill-no-data-statement" v-else>
{{ trans(CHILL_TICKET_TICKET_BANNER_NO_MOTIVE) }}
</p>
<h2 v-if="ticket.currentPersons.length">
{{
ticket.currentPersons
.map((person) => person.text)
.join(", ")
}}
</h2>
</div>
<div class="col-md-6 col-sm-12">
<div class="d-flex justify-content-end">
<toggle-flags />
<span
class="badge text-bg-chill-green text-white"
style="font-size: 1rem"
v-if="isOpen"
>
{{ trans(CHILL_TICKET_TICKET_BANNER_OPEN) }}
</span>
<span
class="badge text-bg-chill-red text-white"
style="font-size: 1rem"
v-else
>
{{ trans(CHILL_TICKET_TICKET_BANNER_CLOSED) }}
</span>
</div>
<div class="d-flex justify-content-end">
<p class="created-at-timespan" v-if="ticket.createdAt">
{{
trans(CHILL_TICKET_TICKET_BANNER_SINCE, {
time: since,
})
}}
</p>
</div>
</div>
</div>
<Teleport to="#header-ticket-main">
<div class="container-xxl text-primary">
<div class="row">
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
<h1 v-if="ticket.currentMotive">
#{{ ticket.id }} {{ ticket.currentMotive.label.fr }}
</h1>
<p class="chill-no-data-statement" v-else>
{{ trans(CHILL_TICKET_TICKET_BANNER_NO_MOTIVE) }}
</p>
<h2 v-if="ticket.currentPersons.length">
{{ ticket.currentPersons.map((person) => person.text).join(", ") }}
</h2>
</div>
</Teleport>
<Teleport to="#header-ticket-details">
<div class="container-xxl">
<div class="row justify-content-between">
<div
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
>
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_CALLER) }}
</h3>
<on-the-fly
v-if="ticket.caller"
:key="ticket.caller.id"
:type="ticket.caller.type"
:id="ticket.caller.id"
:buttonText="ticket.caller.textAge"
:displayBadge="'true' === 'true'"
action="show"
></on-the-fly>
</div>
<div
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
>
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER) }}
</h3>
<on-the-fly
v-for="person in ticket.currentPersons"
:key="person.id"
:type="person.type"
:id="person.id"
:buttonText="person.textAge"
:displayBadge="'true' === 'true'"
action="show"
></on-the-fly>
</div>
<div
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
>
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_SPEAKER) }}
</h3>
<addressee-component
:addressees="ticket.currentAddressees"
/>
</div>
</div>
<div class="col-md-6 col-sm-12">
<div class="d-flex justify-content-end">
<toggle-flags />
<span
class="badge text-bg-chill-green text-white"
style="font-size: 1rem"
v-if="isOpen"
>
{{ trans(CHILL_TICKET_TICKET_BANNER_OPEN) }}
</span>
<span
class="badge text-bg-chill-red text-white"
style="font-size: 1rem"
v-else
>
{{ trans(CHILL_TICKET_TICKET_BANNER_CLOSED) }}
</span>
</div>
<div class="d-flex justify-content-end">
<p class="created-at-timespan" v-if="ticket.createdAt">
{{
trans(CHILL_TICKET_TICKET_BANNER_SINCE, {
time: since,
})
}}
</p>
</div>
</div>
</Teleport>
</div>
</div>
</Teleport>
<Teleport to="#header-ticket-details">
<div class="container-xxl">
<div class="row justify-content-between">
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_CALLER) }}
</h3>
<on-the-fly
v-if="ticket.caller"
:key="ticket.caller.id"
:type="ticket.caller.type"
:id="ticket.caller.id"
:buttonText="ticket.caller.text"
:displayBadge="'true' === 'true'"
action="show"
></on-the-fly>
</div>
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER) }}
</h3>
<on-the-fly
v-for="person in ticket.currentPersons"
:key="person.id"
:type="person.type"
:id="person.id"
:buttonText="person.textAge"
:displayBadge="'true' === 'true'"
action="show"
></on-the-fly>
</div>
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
<h3 class="text-primary">
{{ trans(CHILL_TICKET_TICKET_BANNER_SPEAKER) }}
</h3>
<addressee-component :addressees="ticket.currentAddressees" />
</div>
</div>
</div>
</Teleport>
</template>
<style lang="scss">
.created-at-timespan {
font-weight: 600;
font-variant: all-petite-caps;
font-weight: 600;
font-variant: all-petite-caps;
}
</style>
@ -121,26 +109,26 @@ import { ISOToDatetime } from "../../../../../../../ChillMainBundle/Resources/pu
// Translations
import {
trans,
CHILL_TICKET_TICKET_BANNER_NO_MOTIVE,
CHILL_TICKET_TICKET_BANNER_OPEN,
CHILL_TICKET_TICKET_BANNER_CLOSED,
CHILL_TICKET_TICKET_BANNER_SINCE,
CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER,
CHILL_TICKET_TICKET_BANNER_SPEAKER,
CHILL_TICKET_TICKET_BANNER_DAYS,
CHILL_TICKET_TICKET_BANNER_HOURS,
CHILL_TICKET_TICKET_BANNER_MINUTES,
CHILL_TICKET_TICKET_BANNER_SECONDS,
CHILL_TICKET_TICKET_BANNER_AND,
CHILL_TICKET_TICKET_BANNER_CALLER,
trans,
CHILL_TICKET_TICKET_BANNER_NO_MOTIVE,
CHILL_TICKET_TICKET_BANNER_OPEN,
CHILL_TICKET_TICKET_BANNER_CLOSED,
CHILL_TICKET_TICKET_BANNER_SINCE,
CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER,
CHILL_TICKET_TICKET_BANNER_SPEAKER,
CHILL_TICKET_TICKET_BANNER_DAYS,
CHILL_TICKET_TICKET_BANNER_HOURS,
CHILL_TICKET_TICKET_BANNER_MINUTES,
CHILL_TICKET_TICKET_BANNER_SECONDS,
CHILL_TICKET_TICKET_BANNER_AND,
CHILL_TICKET_TICKET_BANNER_CALLER,
} from "translator";
// Store
import { useStore } from "vuex";
const props = defineProps<{
ticket: Ticket;
ticket: Ticket;
}>();
const store = useStore();
@ -148,59 +136,55 @@ const today = ref(new Date());
const createdAt = ref(props.ticket.createdAt);
setInterval(() => {
today.value = new Date();
today.value = new Date();
}, 5000);
const isOpen = computed(() => store.getters.isOpen);
const since = computed(() => {
if (createdAt.value == null) {
return "";
}
const date = ISOToDatetime(createdAt.value.datetime);
if (createdAt.value == null) {
return "";
}
const date = ISOToDatetime(createdAt.value.datetime);
if (date == null) {
return "";
}
if (date == null) {
return "";
}
const timeDiff = Math.abs(today.value.getTime() - date.getTime());
const daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
const hoursDiff = Math.floor(
(timeDiff % (1000 * 3600 * 24)) / (1000 * 3600),
const timeDiff = Math.abs(today.value.getTime() - date.getTime());
const daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
const hoursDiff = Math.floor((timeDiff % (1000 * 3600 * 24)) / (1000 * 3600));
const minutesDiff = Math.floor((timeDiff % (1000 * 3600)) / (1000 * 60));
const secondsDiff = Math.floor((timeDiff % (1000 * 60)) / 1000);
// On construit la liste des parties à afficher
const parts: string[] = [];
if (daysDiff > 0) {
parts.push(trans(CHILL_TICKET_TICKET_BANNER_DAYS, { count: daysDiff }));
}
if (hoursDiff > 0 || daysDiff > 0) {
parts.push(trans(CHILL_TICKET_TICKET_BANNER_HOURS, { count: hoursDiff }));
}
if (minutesDiff > 0 || hoursDiff > 0 || daysDiff > 0) {
parts.push(
trans(CHILL_TICKET_TICKET_BANNER_MINUTES, { count: minutesDiff }),
);
const minutesDiff = Math.floor((timeDiff % (1000 * 3600)) / (1000 * 60));
const secondsDiff = Math.floor((timeDiff % (1000 * 60)) / 1000);
// On construit la liste des parties à afficher
const parts: string[] = [];
if (daysDiff > 0) {
parts.push(trans(CHILL_TICKET_TICKET_BANNER_DAYS, { count: daysDiff }));
}
if (hoursDiff > 0 || daysDiff > 0) {
parts.push(
trans(CHILL_TICKET_TICKET_BANNER_HOURS, { count: hoursDiff }),
);
}
if (minutesDiff > 0 || hoursDiff > 0 || daysDiff > 0) {
parts.push(
trans(CHILL_TICKET_TICKET_BANNER_MINUTES, { count: minutesDiff }),
);
}
if (parts.length === 0) {
return trans(CHILL_TICKET_TICKET_BANNER_SECONDS, {
count: secondsDiff,
});
}
if (parts.length > 1) {
const last = parts.pop();
return (
parts.join(", ") +
" " +
trans(CHILL_TICKET_TICKET_BANNER_AND) +
" " +
last
);
}
return parts[0];
}
if (parts.length === 0) {
return trans(CHILL_TICKET_TICKET_BANNER_SECONDS, {
count: secondsDiff,
});
}
if (parts.length > 1) {
const last = parts.pop();
return (
parts.join(", ") +
" " +
trans(CHILL_TICKET_TICKET_BANNER_AND) +
" " +
last
);
}
return parts[0];
});
</script>

View File

@ -1,16 +1,16 @@
<template>
<pick-entity
uniqid="ticket-person-selector"
:types="['person', 'third_party']"
:picked="selectedEntity ? [selectedEntity] : []"
:suggested="suggestedValues"
:multiple="false"
:removable-if-set="true"
:display-picked="true"
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL)"
@add-new-entity="addNewEntity"
@remove-entity="removeEntity"
/>
<pick-entity
uniqid="ticket-person-selector"
:types="['person', 'thirdparty']"
:picked="selectedEntity ? [selectedEntity] : []"
:suggested="suggestedValues"
:multiple="false"
:removable-if-set="true"
:display-picked="true"
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL)"
@add-new-entity="addNewEntity"
@remove-entity="removeEntity"
/>
</template>
<script setup lang="ts">
@ -24,62 +24,62 @@ import { Entities } from "ChillPersonAssets/types";
// Translations
import {
trans,
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
trans,
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
} from "translator";
const props = defineProps<{
modelValue: Entities | null;
suggested: Entities[];
modelValue: Entities | null;
suggested: Entities[];
}>();
const emit =
defineEmits<(event: "update:modelValue", value: Entities | null) => void>();
defineEmits<(event: "update:modelValue", value: Entities | null) => void>();
const selectedEntity = ref<Entities | null>(props.modelValue);
const suggestedValues = ref<Entities[]>([...props.suggested]);
watch(
() => [props.suggested, props.modelValue],
() => {
suggestedValues.value = props.suggested.filter(
(suggested: Entities) =>
suggested.id === selectedEntity.value?.id &&
suggested.type === selectedEntity.value?.type,
);
},
{ immediate: true, deep: true },
() => [props.suggested, props.modelValue],
() => {
suggestedValues.value = props.suggested.filter(
(suggested: Entities) =>
suggested.id === selectedEntity.value?.id &&
suggested.type === selectedEntity.value?.type,
);
},
{ immediate: true, deep: true },
);
function addNewEntity({ entity }: { entity: Entities }) {
selectedEntity.value = entity;
emit("update:modelValue", selectedEntity.value);
selectedEntity.value = entity;
emit("update:modelValue", selectedEntity.value);
}
function removeEntity() {
selectedEntity.value = null;
emit("update:modelValue", null);
selectedEntity.value = null;
emit("update:modelValue", null);
}
</script>
<style scoped lang="scss">
ul.person-list {
list-style-type: none;
list-style-type: none;
& > li {
display: inline-block;
border: 1px solid transparent;
border-radius: 6px;
& > li {
display: inline-block;
border: 1px solid transparent;
border-radius: 6px;
button.remove-person {
opacity: 10%;
}
button.remove-person {
opacity: 10%;
}
}
& > li:hover {
border: 1px solid white;
& > li:hover {
border: 1px solid white;
button.remove-person {
opacity: 100%;
}
button.remove-person {
opacity: 100%;
}
}
}
</style>

View File

@ -1,79 +1,75 @@
<template>
<div
class="card my-2 bg-light"
v-for="history_line in history.filter(
(line) => line.event_type != 'add_person',
)"
:key="history.indexOf(history_line)"
>
<div class="card-header">
<div
class="history-header d-flex align-items-center justify-content-between"
>
<div class="d-flex align-items-center fw-bold">
<i
:class="`${actionIcons[history_line.event_type]} me-1`"
></i>
<span>{{ explainSentence(history_line) }}</span>
<TicketHistoryStateComponent
:new_state="history_line.data.new_state"
v-if="history_line.event_type == 'state_change'"
/>
<TicketHistoryEmergencyComponent
v-if="history_line.event_type == 'emergency_change'"
:new_emergency="history_line.data.new_emergency"
/>
</div>
<div>
<span class="badge-user">
<user-render-box-badge
:user="history_line.by"
></user-render-box-badge>
</span>
<span class="fst-italic mx-2">
{{ formatDate(history_line.at) }}
</span>
</div>
</div>
<div
class="card my-2 bg-light"
v-for="history_line in history.filter(
(line) => line.event_type != 'add_person',
)"
:key="history.indexOf(history_line)"
>
<div class="card-header">
<div
class="history-header d-flex align-items-center justify-content-between"
>
<div class="d-flex align-items-center fw-bold">
<i :class="`${actionIcons[history_line.event_type]} me-1`"></i>
<span>{{ explainSentence(history_line) }}</span>
<TicketHistoryStateComponent
:new_state="history_line.data.new_state"
v-if="history_line.event_type == 'state_change'"
/>
<TicketHistoryEmergencyComponent
v-if="history_line.event_type == 'emergency_change'"
:new_emergency="history_line.data.new_emergency"
/>
</div>
<div
class="card-body row"
v-if="
!['state_change', 'emergency_change'].includes(
history_line.event_type,
)
"
>
<ticket-history-person-component
:persons="history_line.data.persons"
v-if="history_line.event_type == 'persons_state'"
/>
<ticket-history-person-component
:persons="
history_line.data.new_caller
? [history_line.data.new_caller]
: []
"
v-if="history_line.event_type == 'set_caller'"
/>
<ticket-history-motive-component
:motiveHistory="history_line.data"
v-else-if="history_line.event_type == 'set_motive'"
/>
<ticket-history-comment-component
:commentHistory="history_line.data"
v-else-if="history_line.event_type == 'add_comment'"
/>
<ticket-history-addressee-component
:addresseeState="history_line.data"
v-else-if="history_line.event_type == 'addressees_state'"
/>
<ticket-history-create-component
:by="history_line.by"
v-else-if="history_line.event_type == 'create_ticket'"
/>
<div>
<span class="badge-user">
<user-render-box-badge
:user="history_line.by"
></user-render-box-badge>
</span>
<span class="fst-italic mx-2">
{{ formatDate(history_line.at) }}
</span>
</div>
</div>
</div>
<div
class="card-body row"
v-if="
!['state_change', 'emergency_change'].includes(history_line.event_type)
"
>
<ticket-history-person-component
:entities="history_line.data.persons"
v-if="history_line.event_type == 'persons_state'"
/>
<ticket-history-person-component
:entities="
history_line.data.new_caller
? ([history_line.data.new_caller] as Person[] | Thirdparty[])
: []
"
v-if="history_line.event_type == 'set_caller'"
/>
<ticket-history-motive-component
:motiveHistory="history_line.data"
v-else-if="history_line.event_type == 'set_motive'"
/>
<ticket-history-comment-component
:commentHistory="history_line.data"
v-else-if="history_line.event_type == 'add_comment'"
/>
<ticket-history-addressee-component
:addresseeState="history_line.data"
v-else-if="history_line.event_type == 'addressees_state'"
/>
<ticket-history-create-component
:by="history_line.by"
v-else-if="history_line.event_type == 'create_ticket'"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
@ -82,6 +78,8 @@ import { useStore } from "vuex";
// Types
import { DateTime } from "../../../../../../../ChillMainBundle/Resources/public/types";
import { TicketHistoryLine } from "../../../types";
import { Person } from "ChillPersonAssets/types";
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
// Components
import TicketHistoryPersonComponent from "./TicketHistoryPersonComponent.vue";
@ -99,15 +97,15 @@ import { ISOToDatetime } from "../../../../../../../ChillMainBundle/Resources/pu
// Translations
import {
trans,
CHILL_TICKET_TICKET_HISTORY_ADD_COMMENT,
CHILL_TICKET_TICKET_HISTORY_ADDRESSEES_STATE,
CHILL_TICKET_TICKET_HISTORY_PERSONS_STATE,
CHILL_TICKET_TICKET_HISTORY_SET_MOTIVE,
CHILL_TICKET_TICKET_HISTORY_CREATE_TICKET,
CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE,
CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE,
CHILL_TICKET_TICKET_HISTORY_SET_CALLER,
trans,
CHILL_TICKET_TICKET_HISTORY_ADD_COMMENT,
CHILL_TICKET_TICKET_HISTORY_ADDRESSEES_STATE,
CHILL_TICKET_TICKET_HISTORY_PERSONS_STATE,
CHILL_TICKET_TICKET_HISTORY_SET_MOTIVE,
CHILL_TICKET_TICKET_HISTORY_CREATE_TICKET,
CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE,
CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE,
CHILL_TICKET_TICKET_HISTORY_SET_CALLER,
} from "translator";
defineProps<{ history: TicketHistoryLine[] }>();
@ -116,36 +114,36 @@ const store = useStore();
const actionIcons = ref(store.getters.getActionIcons);
function explainSentence(history: TicketHistoryLine): string {
switch (history.event_type) {
case "add_comment":
return trans(CHILL_TICKET_TICKET_HISTORY_ADD_COMMENT);
case "addressees_state":
return trans(CHILL_TICKET_TICKET_HISTORY_ADDRESSEES_STATE);
case "persons_state":
return trans(CHILL_TICKET_TICKET_HISTORY_PERSONS_STATE);
case "set_motive":
return trans(CHILL_TICKET_TICKET_HISTORY_SET_MOTIVE);
case "create_ticket":
return trans(CHILL_TICKET_TICKET_HISTORY_CREATE_TICKET);
case "state_change":
return trans(CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE);
case "emergency_change":
return trans(CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE);
case "set_caller":
return trans(CHILL_TICKET_TICKET_HISTORY_SET_CALLER);
default:
return "";
}
switch (history.event_type) {
case "add_comment":
return trans(CHILL_TICKET_TICKET_HISTORY_ADD_COMMENT);
case "addressees_state":
return trans(CHILL_TICKET_TICKET_HISTORY_ADDRESSEES_STATE);
case "persons_state":
return trans(CHILL_TICKET_TICKET_HISTORY_PERSONS_STATE);
case "set_motive":
return trans(CHILL_TICKET_TICKET_HISTORY_SET_MOTIVE);
case "create_ticket":
return trans(CHILL_TICKET_TICKET_HISTORY_CREATE_TICKET);
case "state_change":
return trans(CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE);
case "emergency_change":
return trans(CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE);
case "set_caller":
return trans(CHILL_TICKET_TICKET_HISTORY_SET_CALLER);
default:
return "";
}
}
function formatDate(d: DateTime): string {
const date = ISOToDatetime(d.datetime);
const date = ISOToDatetime(d.datetime);
if (date === null) {
return "";
}
if (date === null) {
return "";
}
const month = date.toLocaleString("default", { month: "long" });
return `${date.getDate()} ${month} ${date.getFullYear()}, ${date.toLocaleTimeString()}`;
const month = date.toLocaleString("default", { month: "long" });
return `${date.getDate()} ${month} ${date.getFullYear()}, ${date.toLocaleTimeString()}`;
}
</script>

View File

@ -1,18 +1,18 @@
<template>
<div class="col-12">
<ul class="persons-list" v-if="persons.length > 0">
<li v-for="person in persons" :key="person.id">
<on-the-fly
:type="person.type"
:id="person.id"
:buttonText="person.textAge"
:displayBadge="true"
action="show"
></on-the-fly>
</li>
</ul>
<div v-else class="text-muted">Aucune personne concernée</div>
</div>
<div class="col-12">
<ul class="persons-list" v-if="entities.length > 0">
<li v-for="entity in entities" :key="entity.text">
<on-the-fly
:type="entity.type"
:id="entity.id"
:buttonText="entity.text"
:displayBadge="true"
action="show"
></on-the-fly>
</li>
</ul>
<div v-else class="text-muted">Aucune personne concernée</div>
</div>
</template>
<script setup lang="ts">
@ -20,17 +20,18 @@ import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
// Types
import { Person } from "ChillPersonAssets/types";
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
defineProps<{ persons: Person[] }>();
defineProps<{ entities: Person[] | Thirdparty[] }>();
</script>
<style lang="scss" scoped>
ul.persons-list {
list-style-type: none;
list-style-type: none;
& > li {
display: inline-block;
margin: 0 0.15rem;
}
& > li {
display: inline-block;
margin: 0 0.15rem;
}
}
</style>