mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-02 05:57:45 +00:00
Merge branch '1243-display-ticket-list' into 'ticket-app-master'
Créer un composant pour afficher une liste des tickets See merge request Chill-Projet/chill-bundles!849
This commit is contained in:
commit
bbf387d96f
@ -10,9 +10,11 @@
|
|||||||
>
|
>
|
||||||
<div class="modal-dialog" :class="modalDialogClass || {}">
|
<div class="modal-dialog" :class="modalDialogClass || {}">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div
|
||||||
|
class="modal-header d-flex justify-content-between align-items-center"
|
||||||
|
>
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
<button class="close btn" @click="emits('close')">
|
<button class="close btn ms-auto" @click="emits('close')">
|
||||||
<i class="fa fa-times" aria-hidden="true"></i>
|
<i class="fa fa-times" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -132,6 +132,7 @@ interface BaseTicket<
|
|||||||
type: "ticket_ticket";
|
type: "ticket_ticket";
|
||||||
id: number;
|
id: number;
|
||||||
externalRef: string;
|
externalRef: string;
|
||||||
|
createdAt: DateTime | null;
|
||||||
currentAddressees: UserGroupOrUser[];
|
currentAddressees: UserGroupOrUser[];
|
||||||
currentPersons: Person[];
|
currentPersons: Person[];
|
||||||
currentMotive: null | Motive;
|
currentMotive: null | Motive;
|
||||||
@ -146,7 +147,6 @@ export interface TicketSimple extends BaseTicket<"ticket_ticket:simple"> {
|
|||||||
|
|
||||||
export interface Ticket extends BaseTicket<"ticket_ticket:extended"> {
|
export interface Ticket extends BaseTicket<"ticket_ticket:extended"> {
|
||||||
type_extended: "ticket_ticket:extended";
|
type_extended: "ticket_ticket:extended";
|
||||||
createdAt: DateTime | null;
|
|
||||||
createdBy: User | null;
|
createdBy: User | null;
|
||||||
updatedAt: DateTime | null;
|
updatedAt: DateTime | null;
|
||||||
updatedBy: User | null;
|
updatedBy: User | null;
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<banner-component :ticket="ticket" />
|
<banner-component :ticket="ticket" />
|
||||||
<div class="container-xxl pt-1" style="padding-bottom: 55px">
|
<div class="container-xxl pt-1" style="padding-bottom: 55px">
|
||||||
<ticket-selector-component :tickets="[]" />
|
<previous-tickets-component />
|
||||||
<ticket-history-list-component :history="ticketHistory" />
|
<ticket-history-list-component :history="ticketHistory" />
|
||||||
</div>
|
</div>
|
||||||
<action-toolbar-component />
|
<action-toolbar-component />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useToast } from "vue-toast-notification";
|
import { useToast } from "vue-toast-notification";
|
||||||
import { computed, onMounted } from "vue";
|
import { computed, onMounted } from "vue";
|
||||||
@ -15,7 +16,7 @@ import { useStore } from "vuex";
|
|||||||
import { Ticket } from "../../types";
|
import { Ticket } from "../../types";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import TicketSelectorComponent from "./components/TicketSelectorComponent.vue";
|
import PreviousTicketsComponent from "./components/PreviousTicketsComponent.vue";
|
||||||
import TicketHistoryListComponent from "./components/TicketHistoryListComponent.vue";
|
import TicketHistoryListComponent from "./components/TicketHistoryListComponent.vue";
|
||||||
import ActionToolbarComponent from "./components/ActionToolbarComponent.vue";
|
import ActionToolbarComponent from "./components/ActionToolbarComponent.vue";
|
||||||
import BannerComponent from "./components/BannerComponent.vue";
|
import BannerComponent from "./components/BannerComponent.vue";
|
||||||
@ -27,7 +28,7 @@ store.commit("setTicket", JSON.parse(window.initialTicket) as Ticket);
|
|||||||
|
|
||||||
const ticket = computed(() => store.getters.getTicket as Ticket);
|
const ticket = computed(() => store.getters.getTicket as Ticket);
|
||||||
|
|
||||||
const ticketHistory = computed(() => store.getters.getDistinctAddressesHistory);
|
const ticketHistory = computed(() => store.getters.getTicketHistory);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form @submit.prevent="submitAction">
|
<form @submit.prevent="submitAction">
|
||||||
<add-comment-component
|
<comment-editor-component
|
||||||
v-model="content"
|
v-model="content"
|
||||||
v-if="activeTab === 'add_comment'"
|
v-if="activeTab === 'add_comment'"
|
||||||
/>
|
/>
|
||||||
@ -53,12 +53,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<caller-selector-component v-model="caller" :suggested="[]" />
|
<persons-selector-component
|
||||||
|
v-model="caller"
|
||||||
|
:suggested="[]"
|
||||||
|
:multiple="false"
|
||||||
|
:types="['person', 'thirdparty']"
|
||||||
|
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<persons-selector-component
|
<persons-selector-component
|
||||||
v-model="persons"
|
v-model="persons"
|
||||||
:suggested="suggestedPersons"
|
:suggested="suggestedPersons"
|
||||||
|
:multiple="true"
|
||||||
|
:types="['person']"
|
||||||
|
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -82,15 +91,14 @@
|
|||||||
<li v-else class="nav-item p-2 go-back">
|
<li v-else class="nav-item p-2 go-back">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-link p-0"
|
class="btn btn-light"
|
||||||
style="font-size: 1.5rem; line-height: 1; color: #888"
|
|
||||||
@click="closeAllActions"
|
@click="closeAllActions"
|
||||||
aria-label="Fermer"
|
aria-label="Fermer"
|
||||||
title="Fermer"
|
title="Fermer"
|
||||||
>
|
>
|
||||||
<span aria-hidden="true">×</span>
|
<i class="fa fa-arrow-left"></i>
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CANCEL) }}
|
||||||
</button>
|
</button>
|
||||||
>
|
|
||||||
</li>
|
</li>
|
||||||
<li v-for="btn in actionButtons" :key="btn.key" class="nav-item p-2">
|
<li v-for="btn in actionButtons" :key="btn.key" class="nav-item p-2">
|
||||||
<button
|
<button
|
||||||
@ -130,11 +138,10 @@ import { useStore } from "vuex";
|
|||||||
import { useToast } from "vue-toast-notification";
|
import { useToast } from "vue-toast-notification";
|
||||||
|
|
||||||
// Component
|
// Component
|
||||||
import MotiveSelectorComponent from "./MotiveSelectorComponent.vue";
|
import MotiveSelectorComponent from "./Motive/MotiveSelectorComponent.vue";
|
||||||
import AddresseeSelectorComponent from "./AddresseeSelectorComponent.vue";
|
import AddresseeSelectorComponent from "./Addressee/AddresseeSelectorComponent.vue";
|
||||||
import AddCommentComponent from "./AddCommentComponent.vue";
|
import CommentEditorComponent from "./Comment/CommentEditorComponent.vue";
|
||||||
import PersonsSelectorComponent from "./PersonsSelectorComponent.vue";
|
import PersonsSelectorComponent from "./Person/PersonsSelectorComponent.vue";
|
||||||
import CallerSelectorComponent from "./CallerSelectorComponent.vue";
|
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
import {
|
import {
|
||||||
@ -152,6 +159,8 @@ import {
|
|||||||
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_CALLER,
|
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_CALLER,
|
||||||
CHILL_TICKET_TICKET_SET_PERSONS_TITLE,
|
CHILL_TICKET_TICKET_SET_PERSONS_TITLE,
|
||||||
CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS,
|
CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
|
||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_SAVE,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_SAVE,
|
||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE,
|
||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS,
|
||||||
@ -159,6 +168,7 @@ import {
|
|||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN,
|
||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS,
|
||||||
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_ERROR,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_ERROR,
|
||||||
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CANCEL,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@ -220,7 +230,7 @@ const isOpen = computed(() => store.getters.isOpen);
|
|||||||
const motives = computed(() => store.getters.getMotives as Motive[]);
|
const motives = computed(() => store.getters.getMotives as Motive[]);
|
||||||
const userGroups = computed(() => store.getters.getUserGroups as UserGroup[]);
|
const userGroups = computed(() => store.getters.getUserGroups as UserGroup[]);
|
||||||
const suggestedPersons = computed(() => store.getters.getPersons as Person[]);
|
const suggestedPersons = computed(() => store.getters.getPersons as Person[]);
|
||||||
console.log("suggestedPersons", suggestedPersons.value);
|
|
||||||
const hasReturnPath = computed((): boolean => {
|
const hasReturnPath = computed((): boolean => {
|
||||||
const params = new URL(document.location.toString()).searchParams;
|
const params = new URL(document.location.toString()).searchParams;
|
||||||
return params.has("returnPath");
|
return params.has("returnPath");
|
||||||
@ -291,6 +301,7 @@ async function submitAction() {
|
|||||||
await store.dispatch("setPersons", {
|
await store.dispatch("setPersons", {
|
||||||
persons: persons.value,
|
persons: persons.value,
|
||||||
});
|
});
|
||||||
|
await store.dispatch("fetchTicketsByPerson");
|
||||||
activeTab.value = "";
|
activeTab.value = "";
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS));
|
toast.success(trans(CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS));
|
||||||
break;
|
break;
|
||||||
@ -351,6 +362,6 @@ div.footer-ticket-details {
|
|||||||
.fixed-bottom {
|
.fixed-bottom {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -37,7 +37,7 @@ import {
|
|||||||
User,
|
User,
|
||||||
UserGroup,
|
UserGroup,
|
||||||
UserGroupOrUser,
|
UserGroupOrUser,
|
||||||
} from "../../../../../../../ChillMainBundle/Resources/public/types";
|
} from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
const props = defineProps<{ addressees: UserGroupOrUser[] }>();
|
const props = defineProps<{ addressees: UserGroupOrUser[] }>();
|
||||||
|
|
@ -3,12 +3,10 @@
|
|||||||
<div class="container-xxl text-primary">
|
<div class="container-xxl text-primary">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
<div class="col-md-6 col-sm-12 ps-md-5 ps-xxl-0">
|
||||||
<h1 v-if="ticket.currentMotive">
|
<h1>
|
||||||
#{{ ticket.id }} {{ ticket.currentMotive.label.fr }}
|
{{ getTicketTitle(ticket) }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="chill-no-data-statement" v-else>
|
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_NO_MOTIVE) }}
|
|
||||||
</p>
|
|
||||||
<h2 v-if="ticket.currentPersons.length">
|
<h2 v-if="ticket.currentPersons.length">
|
||||||
{{ ticket.currentPersons.map((person) => person.text).join(", ") }}
|
{{ ticket.currentPersons.map((person) => person.text).join(", ") }}
|
||||||
</h2>
|
</h2>
|
||||||
@ -16,7 +14,7 @@
|
|||||||
|
|
||||||
<div class="col-md-6 col-sm-12">
|
<div class="col-md-6 col-sm-12">
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<toggle-flags />
|
<emergency-toggle-component />
|
||||||
|
|
||||||
<span
|
<span
|
||||||
class="badge text-bg-chill-green text-white"
|
class="badge text-bg-chill-green text-white"
|
||||||
@ -49,37 +47,42 @@
|
|||||||
<Teleport to="#header-ticket-details">
|
<Teleport to="#header-ticket-details">
|
||||||
<div class="container-xxl">
|
<div class="container-xxl">
|
||||||
<div class="row justify-content-between">
|
<div class="row justify-content-between">
|
||||||
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
|
<div
|
||||||
|
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
|
||||||
|
v-if="ticket.caller"
|
||||||
|
>
|
||||||
<h3 class="text-primary">
|
<h3 class="text-primary">
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_CALLER) }}
|
{{
|
||||||
|
trans(CHILL_TICKET_TICKET_BANNER_CALLER, {
|
||||||
|
count: ticket.caller ? 1 : 0,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</h3>
|
</h3>
|
||||||
<on-the-fly
|
<person-component :entities="[ticket.caller] as Person[]" />
|
||||||
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>
|
||||||
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
|
<div
|
||||||
|
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
|
||||||
|
v-if="ticket.currentPersons.length"
|
||||||
|
>
|
||||||
<h3 class="text-primary">
|
<h3 class="text-primary">
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER) }}
|
{{
|
||||||
|
trans(CHILL_TICKET_TICKET_BANNER_PERSON, {
|
||||||
|
count: ticket.currentPersons.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</h3>
|
</h3>
|
||||||
<on-the-fly
|
<person-component :entities="ticket.currentPersons" />
|
||||||
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>
|
||||||
<div class="col-md-4 col-sm-12 d-flex flex-column align-items-start">
|
<div
|
||||||
|
class="col-md-4 col-sm-12 d-flex flex-column align-items-start"
|
||||||
|
v-if="ticket.currentAddressees.length"
|
||||||
|
>
|
||||||
<h3 class="text-primary">
|
<h3 class="text-primary">
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_SPEAKER) }}
|
{{
|
||||||
|
trans(CHILL_TICKET_TICKET_BANNER_SPEAKER, {
|
||||||
|
count: ticket.currentAddressees.length,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</h3>
|
</h3>
|
||||||
<addressee-component :addressees="ticket.currentAddressees" />
|
<addressee-component :addressees="ticket.currentAddressees" />
|
||||||
</div>
|
</div>
|
||||||
@ -99,92 +102,41 @@
|
|||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import AddresseeComponent from "./AddresseeComponent.vue";
|
import AddresseeComponent from "./Addressee/AddresseeComponent.vue";
|
||||||
import ToggleFlags from "./ToggleFlags.vue";
|
import EmergencyToggleComponent from "./Emergency/EmergencyToggleComponent.vue";
|
||||||
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";
|
import PersonComponent from "./Person/PersonComponent.vue";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Ticket } from "../../../types";
|
import { Ticket } from "../../../types";
|
||||||
import { ISOToDatetime } from "../../../../../../../ChillMainBundle/Resources/public/chill/js/date";
|
|
||||||
|
|
||||||
// Translations
|
|
||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
CHILL_TICKET_TICKET_BANNER_NO_MOTIVE,
|
|
||||||
CHILL_TICKET_TICKET_BANNER_OPEN,
|
CHILL_TICKET_TICKET_BANNER_OPEN,
|
||||||
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
||||||
CHILL_TICKET_TICKET_BANNER_SINCE,
|
CHILL_TICKET_TICKET_BANNER_SINCE,
|
||||||
CHILL_TICKET_TICKET_BANNER_CONCERNED_USAGER,
|
|
||||||
CHILL_TICKET_TICKET_BANNER_SPEAKER,
|
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,
|
CHILL_TICKET_TICKET_BANNER_CALLER,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_PERSON,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
import { getTicketTitle } from "../utils/utils";
|
||||||
|
|
||||||
const props = defineProps<{
|
defineProps<{
|
||||||
ticket: Ticket;
|
ticket: Ticket;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const today = ref(new Date());
|
const today = ref(new Date());
|
||||||
const createdAt = ref(props.ticket.createdAt);
|
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
today.value = new Date();
|
today.value = new Date();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
const isOpen = computed(() => store.getters.isOpen);
|
const isOpen = computed(() => store.getters.isOpen);
|
||||||
|
|
||||||
const since = computed(() => {
|
const since = computed(() => {
|
||||||
if (createdAt.value == null) {
|
return store.getters.getSinceCreated(today.value);
|
||||||
return "";
|
|
||||||
}
|
|
||||||
const date = ISOToDatetime(createdAt.value.datetime);
|
|
||||||
|
|
||||||
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 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];
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
<template>
|
|
||||||
<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">
|
|
||||||
import { ref, watch, defineProps, defineEmits } from "vue";
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Entities } from "ChillPersonAssets/types";
|
|
||||||
|
|
||||||
// Translations
|
|
||||||
import {
|
|
||||||
trans,
|
|
||||||
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
|
|
||||||
} from "translator";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
modelValue: Entities | null;
|
|
||||||
suggested: Entities[];
|
|
||||||
}>();
|
|
||||||
const emit =
|
|
||||||
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 },
|
|
||||||
);
|
|
||||||
|
|
||||||
function addNewEntity({ entity }: { entity: Entities }) {
|
|
||||||
selectedEntity.value = entity;
|
|
||||||
emit("update:modelValue", selectedEntity.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeEntity() {
|
|
||||||
selectedEntity.value = null;
|
|
||||||
emit("update:modelValue", null);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
ul.person-list {
|
|
||||||
list-style-type: none;
|
|
||||||
|
|
||||||
& > li {
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
button.remove-person {
|
|
||||||
opacity: 10%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > li:hover {
|
|
||||||
border: 1px solid white;
|
|
||||||
|
|
||||||
button.remove-person {
|
|
||||||
opacity: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -11,7 +11,7 @@ import { marked } from "marked";
|
|||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Comment } from "../../../types";
|
import { Comment } from "../../../../types";
|
||||||
|
|
||||||
defineProps<{ commentHistory: Comment }>();
|
defineProps<{ commentHistory: Comment }>();
|
||||||
|
|
@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="col-12 fw-bolder">
|
<div class="col-12 fw-bolder">
|
||||||
{{ motiveHistory.motive.label.fr }}
|
{{ localizeTranslatableString(motiveHistory.motive.label) }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
// Types
|
// Types
|
||||||
import { MotiveHistory } from "../../../types";
|
import { MotiveHistory } from "../../../../types";
|
||||||
|
|
||||||
|
//Utils
|
||||||
|
import { localizeTranslatableString } from "../../utils/utils";
|
||||||
|
|
||||||
defineProps<{ motiveHistory: MotiveHistory }>();
|
defineProps<{ motiveHistory: MotiveHistory }>();
|
||||||
</script>
|
</script>
|
@ -27,7 +27,7 @@ import { ref, watch } from "vue";
|
|||||||
import VueMultiselect from "vue-multiselect";
|
import VueMultiselect from "vue-multiselect";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Motive } from "../../../types";
|
import { Motive } from "../../../../types";
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
import {
|
import {
|
@ -28,10 +28,10 @@ defineProps<{ entities: Person[] | Thirdparty[] }>();
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
ul.persons-list {
|
ul.persons-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
& > li {
|
& > li {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 0.15rem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -0,0 +1,139 @@
|
|||||||
|
<template>
|
||||||
|
<pick-entity
|
||||||
|
uniqid="ticket-person-selector"
|
||||||
|
:types="types"
|
||||||
|
:picked="pickedEntities"
|
||||||
|
:suggested="suggestedValues"
|
||||||
|
:multiple="multiple"
|
||||||
|
:removable-if-set="true"
|
||||||
|
:display-picked="true"
|
||||||
|
:label="label"
|
||||||
|
@add-new-entity="addNewEntity"
|
||||||
|
@remove-entity="removeEntity"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch, defineProps, defineEmits, computed } from "vue";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { Entities, EntityType } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: Entities[] | Entities | null;
|
||||||
|
suggested: Entities[];
|
||||||
|
multiple: boolean;
|
||||||
|
types: EntityType[];
|
||||||
|
label: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:modelValue": [value: Entities[] | Entities | null];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Valeurs par défaut
|
||||||
|
const multiple = props.multiple;
|
||||||
|
const types = props.types;
|
||||||
|
const label = props.label;
|
||||||
|
|
||||||
|
// État local
|
||||||
|
const selectedEntities = ref<Entities[]>(
|
||||||
|
multiple
|
||||||
|
? [...((props.modelValue as Entities[]) || [])]
|
||||||
|
: props.modelValue
|
||||||
|
? [props.modelValue as Entities]
|
||||||
|
: [],
|
||||||
|
);
|
||||||
|
const suggestedValues = ref<Entities[]>([...props.suggested]);
|
||||||
|
|
||||||
|
// Entités sélectionnées pour le composant PickEntity
|
||||||
|
const pickedEntities = computed(() =>
|
||||||
|
multiple ? selectedEntities.value : selectedEntities.value.slice(0, 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [props.suggested, props.modelValue],
|
||||||
|
() => {
|
||||||
|
// Mise à jour des entités sélectionnées
|
||||||
|
selectedEntities.value = multiple
|
||||||
|
? [...((props.modelValue as Entities[]) || [])]
|
||||||
|
: props.modelValue
|
||||||
|
? [props.modelValue as Entities]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Filtrage des suggestions
|
||||||
|
if (multiple) {
|
||||||
|
suggestedValues.value = props.suggested.filter(
|
||||||
|
(suggested: Entities) =>
|
||||||
|
!(props.modelValue as Entities[])?.some(
|
||||||
|
(selected: Entities) =>
|
||||||
|
suggested.id === selected.id && suggested.type === selected.type,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const currentEntity = props.modelValue as Entities | null;
|
||||||
|
suggestedValues.value = props.suggested.filter(
|
||||||
|
(suggested: Entities) =>
|
||||||
|
!(
|
||||||
|
currentEntity &&
|
||||||
|
suggested.id === currentEntity.id &&
|
||||||
|
suggested.type === currentEntity.type
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
function addNewEntity({ entity }: { entity: Entities }) {
|
||||||
|
if (multiple) {
|
||||||
|
selectedEntities.value.push(entity);
|
||||||
|
emit("update:modelValue", selectedEntities.value);
|
||||||
|
} else {
|
||||||
|
selectedEntities.value = [entity];
|
||||||
|
emit("update:modelValue", entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeEntity({ entity }: { entity: Entities }) {
|
||||||
|
if (multiple) {
|
||||||
|
const index = selectedEntities.value.findIndex(
|
||||||
|
(selectedEntity) => selectedEntity === entity,
|
||||||
|
);
|
||||||
|
if (index !== -1) {
|
||||||
|
selectedEntities.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
emit("update:modelValue", selectedEntities.value);
|
||||||
|
} else {
|
||||||
|
selectedEntities.value = [];
|
||||||
|
emit("update:modelValue", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
ul.person-list {
|
||||||
|
list-style-type: none;
|
||||||
|
|
||||||
|
& > li {
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
button.remove-person {
|
||||||
|
opacity: 10%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > li:hover {
|
||||||
|
border: 1px solid white;
|
||||||
|
|
||||||
|
button.remove-person {
|
||||||
|
opacity: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,90 +0,0 @@
|
|||||||
<template>
|
|
||||||
<pick-entity
|
|
||||||
uniqid="ticket-person-selector"
|
|
||||||
:types="['person']"
|
|
||||||
:picked="selectedEntities"
|
|
||||||
:suggested="suggestedValues"
|
|
||||||
:multiple="false"
|
|
||||||
:removable-if-set="true"
|
|
||||||
:display-picked="true"
|
|
||||||
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL)"
|
|
||||||
@add-new-entity="addNewEntity"
|
|
||||||
@remove-entity="removeEntity"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, watch, defineProps, defineEmits } from "vue";
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import PickEntity from "ChillMainAssets/vuejs/PickEntity/PickEntity.vue";
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Entities } from "ChillPersonAssets/types";
|
|
||||||
|
|
||||||
// Translations
|
|
||||||
import { trans, CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL } from "translator";
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
modelValue: Entities[];
|
|
||||||
suggested: Entities[];
|
|
||||||
}>();
|
|
||||||
const emit = defineEmits<{
|
|
||||||
"update:modelValue": [value: Entities[]];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const selectedEntities = ref<Entities[]>([...props.modelValue]);
|
|
||||||
const suggestedValues = ref<Entities[]>([...props.suggested]);
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => [props.suggested, props.modelValue],
|
|
||||||
() => {
|
|
||||||
suggestedValues.value = props.suggested.filter(
|
|
||||||
(suggested: Entities) =>
|
|
||||||
!props.modelValue.some(
|
|
||||||
(selected: Entities) =>
|
|
||||||
suggested.id === selected.id && suggested.type === selected.type,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
function addNewEntity({ entity }: { entity: Entities }) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<style scoped lang="scss">
|
|
||||||
ul.person-list {
|
|
||||||
list-style-type: none;
|
|
||||||
|
|
||||||
& > li {
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 6px;
|
|
||||||
|
|
||||||
button.remove-person {
|
|
||||||
opacity: 10%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > li:hover {
|
|
||||||
border: 1px solid white;
|
|
||||||
|
|
||||||
button.remove-person {
|
|
||||||
opacity: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -0,0 +1,166 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<div @click="handleClick">
|
||||||
|
<button type="button" class="btn btn-light position-relative">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_PREVIOUS_TICKETS) }}
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-chill-green"
|
||||||
|
>
|
||||||
|
{{ previousTickets.length }}
|
||||||
|
<span class="visually-hidden">Tickets</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal for ticket list -->
|
||||||
|
<Modal
|
||||||
|
v-if="showPreviousTicketModal"
|
||||||
|
:show="showPreviousTicketModal"
|
||||||
|
modal-dialog-class="modal-lg"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h3 class="modal-title">
|
||||||
|
{{
|
||||||
|
trans(CHILL_TICKET_LIST_TITLE_PREVIOUS_TICKETS, {
|
||||||
|
name:
|
||||||
|
currentPersons.length > 0
|
||||||
|
? currentPersons
|
||||||
|
.map((person: Person) => person.text)
|
||||||
|
.join(", ")
|
||||||
|
: undefined,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<TicketListComponent
|
||||||
|
:tickets="previousTickets"
|
||||||
|
@view-ticket="handleViewTicket"
|
||||||
|
@edit-ticket="handleEditTicket"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<!-- Modal for ticket history -->
|
||||||
|
<Modal
|
||||||
|
v-if="
|
||||||
|
showTicketHistoryModal &&
|
||||||
|
selectedTicketId !== null &&
|
||||||
|
previousTicketHistory
|
||||||
|
"
|
||||||
|
:show="showTicketHistoryModal"
|
||||||
|
modal-dialog-class="modal-xl"
|
||||||
|
@close="closeHistoryModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h3 class="modal-title">
|
||||||
|
{{ getTicketTitle(previousTicketDetails) }}
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<TicketHistoryListComponent
|
||||||
|
v-if="previousTicketHistory.length > 0"
|
||||||
|
:history="previousTicketHistory"
|
||||||
|
/>
|
||||||
|
<div v-else class="text-center p-4">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">Chargement...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<button
|
||||||
|
class="btn btn-edit"
|
||||||
|
@click="handleEditTicket(selectedTicketId)"
|
||||||
|
>
|
||||||
|
{{ trans(EDIT) }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, computed } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import Modal from "../../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
|
||||||
|
import TicketListComponent from "./TicketListComponent.vue";
|
||||||
|
import TicketHistoryListComponent from "./TicketHistoryListComponent.vue";
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
CHILL_TICKET_TICKET_PREVIOUS_TICKETS,
|
||||||
|
CHILL_TICKET_LIST_TITLE_PREVIOUS_TICKETS,
|
||||||
|
EDIT,
|
||||||
|
} from "translator";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
import { TicketHistoryLine, TicketSimple } from "../../../types";
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import { getTicketTitle } from "../utils/utils";
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
const showPreviousTicketModal = ref(false);
|
||||||
|
const showTicketHistoryModal = ref(false);
|
||||||
|
const selectedTicketId = ref<number | null>(null);
|
||||||
|
|
||||||
|
const currentPersons = computed(
|
||||||
|
() => store.getters.getCurrentPersons as Person[],
|
||||||
|
);
|
||||||
|
const previousTickets = computed(
|
||||||
|
() => store.getters.getPreviousTickets as TicketSimple[],
|
||||||
|
);
|
||||||
|
const previousTicketHistory = computed(
|
||||||
|
() => store.getters.getPreviousTicketHistory as TicketHistoryLine[],
|
||||||
|
);
|
||||||
|
const previousTicketDetails = computed(() => {
|
||||||
|
return store.getters.getPreviousTicketDetails as TicketSimple;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
await store.dispatch("fetchTicketsByPerson");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors du chargement des tickets:", error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
showPreviousTicketModal.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
showPreviousTicketModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleViewTicket(ticketId: number) {
|
||||||
|
selectedTicketId.value = ticketId;
|
||||||
|
showTicketHistoryModal.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await store.dispatch("fetchPreviousTicketDetails", ticketId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur lors du chargement de l'historique:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEditTicket(ticketId: number) {
|
||||||
|
window.location.href = `/fr/ticket/ticket/${ticketId}/edit`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeHistoryModal() {
|
||||||
|
showTicketHistoryModal.value = false;
|
||||||
|
selectedTicketId.value = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
@ -13,36 +13,11 @@
|
|||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_CLOSED) }}
|
{{ trans(CHILL_TICKET_TICKET_BANNER_CLOSED) }}
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
<!--
|
|
||||||
<span
|
|
||||||
class="text-chill-green mx-2"
|
|
||||||
style="
|
|
||||||
font-size: 1rem;
|
|
||||||
max-width: 80px;
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-word;
|
|
||||||
"
|
|
||||||
v-if="props.new_state == 'open'"
|
|
||||||
>
|
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_OPEN) }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
class="text-chill-red mx-2"
|
|
||||||
style="
|
|
||||||
font-size: 1rem;
|
|
||||||
max-width: 80px;
|
|
||||||
white-space: normal;
|
|
||||||
word-break: break-word;
|
|
||||||
"
|
|
||||||
v-else-if="props.new_state == 'closed'"
|
|
||||||
>
|
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_CLOSED) }}
|
|
||||||
</span> -->
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
// Types
|
// Types
|
||||||
import { StateChange } from "../../../types";
|
import { StateChange } from "../../../../types";
|
||||||
|
|
||||||
// Translations
|
// Translations
|
||||||
import {
|
import {
|
||||||
@ -50,6 +25,7 @@ import {
|
|||||||
CHILL_TICKET_TICKET_BANNER_OPEN,
|
CHILL_TICKET_TICKET_BANNER_OPEN,
|
||||||
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
const props = defineProps<StateChange>();
|
const props = defineProps<StateChange>();
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -1,18 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="col-12">
|
|
||||||
<addressee-component :addressees="addresseeState.addressees" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Types
|
|
||||||
import { AddresseeState } from "../../../types";
|
|
||||||
|
|
||||||
// Components
|
|
||||||
import AddresseeComponent from "./AddresseeComponent.vue";
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
addresseeState: AddresseeState;
|
|
||||||
}>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
@ -1,16 +0,0 @@
|
|||||||
<template>
|
|
||||||
<p>Ticket créé par {{ props.by.text }}</p>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Types
|
|
||||||
import { User } from "../../../../../../../ChillMainBundle/Resources/public/types";
|
|
||||||
|
|
||||||
interface TicketHistoryCreateComponentConfig {
|
|
||||||
by: User;
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<TicketHistoryCreateComponentConfig>();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
@ -13,12 +13,12 @@
|
|||||||
<div class="d-flex align-items-center fw-bold">
|
<div class="d-flex align-items-center fw-bold">
|
||||||
<i :class="`${actionIcons[history_line.event_type]} me-1`"></i>
|
<i :class="`${actionIcons[history_line.event_type]} me-1`"></i>
|
||||||
<span>{{ explainSentence(history_line) }}</span>
|
<span>{{ explainSentence(history_line) }}</span>
|
||||||
<TicketHistoryStateComponent
|
<state-component
|
||||||
:new_state="history_line.data.new_state"
|
:new_state="history_line.data.new_state"
|
||||||
v-if="history_line.event_type == 'state_change'"
|
v-if="history_line.event_type == 'state_change'"
|
||||||
/>
|
/>
|
||||||
<TicketHistoryEmergencyComponent
|
<emergency-component
|
||||||
v-if="history_line.event_type == 'emergency_change'"
|
v-else-if="history_line.event_type == 'emergency_change'"
|
||||||
:new_emergency="history_line.data.new_emergency"
|
:new_emergency="history_line.data.new_emergency"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -37,37 +37,35 @@
|
|||||||
<div
|
<div
|
||||||
class="card-body row"
|
class="card-body row"
|
||||||
v-if="
|
v-if="
|
||||||
!['state_change', 'emergency_change'].includes(history_line.event_type)
|
!['state_change', 'emergency_change', 'create_ticket'].includes(
|
||||||
|
history_line.event_type,
|
||||||
|
)
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<ticket-history-person-component
|
<person-component
|
||||||
:entities="history_line.data.persons"
|
:entities="history_line.data.persons"
|
||||||
v-if="history_line.event_type == 'persons_state'"
|
v-if="history_line.event_type == 'persons_state'"
|
||||||
/>
|
/>
|
||||||
<ticket-history-person-component
|
<person-component
|
||||||
:entities="
|
:entities="
|
||||||
history_line.data.new_caller
|
history_line.data.new_caller
|
||||||
? ([history_line.data.new_caller] as Person[] | Thirdparty[])
|
? ([history_line.data.new_caller] as Person[] | Thirdparty[])
|
||||||
: []
|
: []
|
||||||
"
|
"
|
||||||
v-if="history_line.event_type == 'set_caller'"
|
v-else-if="history_line.event_type == 'set_caller'"
|
||||||
/>
|
/>
|
||||||
<ticket-history-motive-component
|
<motive-component
|
||||||
:motiveHistory="history_line.data"
|
:motiveHistory="history_line.data"
|
||||||
v-else-if="history_line.event_type == 'set_motive'"
|
v-else-if="history_line.event_type == 'set_motive'"
|
||||||
/>
|
/>
|
||||||
<ticket-history-comment-component
|
<comment-component
|
||||||
:commentHistory="history_line.data"
|
:commentHistory="history_line.data"
|
||||||
v-else-if="history_line.event_type == 'add_comment'"
|
v-else-if="history_line.event_type == 'add_comment'"
|
||||||
/>
|
/>
|
||||||
<ticket-history-addressee-component
|
<addressee-component
|
||||||
:addresseeState="history_line.data"
|
:addressees="history_line.data.addressees"
|
||||||
v-else-if="history_line.event_type == 'addressees_state'"
|
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>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -82,13 +80,12 @@ import { Person } from "ChillPersonAssets/types";
|
|||||||
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
|
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import TicketHistoryPersonComponent from "./TicketHistoryPersonComponent.vue";
|
import PersonComponent from "./Person/PersonComponent.vue";
|
||||||
import TicketHistoryMotiveComponent from "./TicketHistoryMotiveComponent.vue";
|
import MotiveComponent from "./Motive/MotiveComponent.vue";
|
||||||
import TicketHistoryCommentComponent from "./TicketHistoryCommentComponent.vue";
|
import CommentComponent from "./Comment/CommentComponent.vue";
|
||||||
import TicketHistoryAddresseeComponent from "./TicketHistoryAddresseeComponent.vue";
|
import AddresseeComponent from "./Addressee/AddresseeComponent.vue";
|
||||||
import TicketHistoryCreateComponent from "./TicketHistoryCreateComponent.vue";
|
import StateComponent from "./State/StateComponent.vue";
|
||||||
import TicketHistoryStateComponent from "./TicketHistoryStateComponent.vue";
|
import EmergencyComponent from "./Emergency/EmergencyComponent.vue";
|
||||||
import TicketHistoryEmergencyComponent from "./TicketHistoryEmergencyComponent.vue";
|
|
||||||
|
|
||||||
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
|
import UserRenderBoxBadge from "ChillMainAssets/vuejs/_components/Entity/UserRenderBoxBadge.vue";
|
||||||
|
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ticket-list-container">
|
||||||
|
<div v-if="tickets.length === 0" class="chill-no-data-statement">
|
||||||
|
{{ trans(CHILL_TICKET_LIST_NO_TICKETS) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex-table">
|
||||||
|
<TicketListItemComponent
|
||||||
|
v-for="ticket in tickets"
|
||||||
|
:key="ticket.id"
|
||||||
|
:ticket="ticket"
|
||||||
|
@view-ticket="$emit('view-ticket', $event)"
|
||||||
|
@edit-ticket="$emit('edit-ticket', $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { TicketSimple } from "../../../types";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import TicketListItemComponent from "./TicketListItemComponent.vue";
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
import { trans, CHILL_TICKET_LIST_NO_TICKETS } from "translator";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
tickets: TicketSimple[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
"view-ticket": [ticketId: number];
|
||||||
|
"edit-ticket": [ticketId: number];
|
||||||
|
}>();
|
||||||
|
</script>
|
@ -0,0 +1,128 @@
|
|||||||
|
<template>
|
||||||
|
<div class="card rounded-4 mb-3">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="wrap-header">
|
||||||
|
<div class="wh-row d-flex justify-content-between align-items-center">
|
||||||
|
<div class="wh-col">
|
||||||
|
<span
|
||||||
|
v-if="ticket.currentMotive"
|
||||||
|
class="h2"
|
||||||
|
style="color: var(--bs-chill-blue); font-variant: all-small-caps"
|
||||||
|
>
|
||||||
|
{{ getTicketTitle(ticket) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="wh-col">
|
||||||
|
<emergency-component
|
||||||
|
:new_emergency="ticket.emergency"
|
||||||
|
v-if="ticket.emergency"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="wh-col">
|
||||||
|
<state-component
|
||||||
|
v-if="ticket.currentState"
|
||||||
|
:new_state="ticket.currentState"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wh-row">
|
||||||
|
<div class="wh-col">#{{ ticket.id }}</div>
|
||||||
|
<div class="wh-col" v-if="ticket.createdAt">
|
||||||
|
<span
|
||||||
|
v-if="ticket"
|
||||||
|
:title="formatDateTime(ticket.createdAt.datetime, 'long', 'long')"
|
||||||
|
style="font-style: italic"
|
||||||
|
>
|
||||||
|
{{ getSinceCreated(ticket.createdAt.datetime, new Date()) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body pt-0">
|
||||||
|
<div class="wrap-list">
|
||||||
|
<div class="wl-row">
|
||||||
|
<div class="wl-col title text-end">
|
||||||
|
<h3>Attribué à</h3>
|
||||||
|
</div>
|
||||||
|
<div class="wl-col list">
|
||||||
|
<addressee-component :addressees="ticket.currentAddressees" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wl-row">
|
||||||
|
<div class="wl-col title text-end">
|
||||||
|
<h3>Patients concernés</h3>
|
||||||
|
</div>
|
||||||
|
<div class="wl-col list">
|
||||||
|
<person-component :entities="ticket.currentPersons" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="wl-row">
|
||||||
|
<div class="wl-col title text-end">
|
||||||
|
<h3>Appelants</h3>
|
||||||
|
</div>
|
||||||
|
<div class="wl-col list">
|
||||||
|
<person-component
|
||||||
|
:entities="
|
||||||
|
ticket.caller
|
||||||
|
? ([ticket.caller] as Person[] | Thirdparty[])
|
||||||
|
: []
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-0" />
|
||||||
|
<div class="card-footer bg-transparent border-0">
|
||||||
|
<ul class="record_actions mb-0">
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="btn btn-view btn-outline-secondary"
|
||||||
|
type="button"
|
||||||
|
@click="$emit('view-ticket', ticket.id)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
class="btn btn-update btn-outline-primary"
|
||||||
|
type="button"
|
||||||
|
@click="emit('edit-ticket', ticket.id)"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, defineEmits } from "vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { TicketSimple } from "../../../types";
|
||||||
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
import { Thirdparty } from "src/Bundle/ChillThirdPartyBundle/Resources/public/types";
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {
|
||||||
|
getSinceCreated,
|
||||||
|
formatDateTime,
|
||||||
|
getTicketTitle,
|
||||||
|
} from "../utils/utils";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import EmergencyComponent from "./Emergency/EmergencyComponent.vue";
|
||||||
|
import StateComponent from "./State/StateComponent.vue";
|
||||||
|
import AddresseeComponent from "./Addressee/AddresseeComponent.vue";
|
||||||
|
import PersonComponent from "./Person/PersonComponent.vue";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
ticket: TicketSimple;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"edit-ticket": [ticketId: number];
|
||||||
|
"view-ticket": [ticketId: number];
|
||||||
|
}>();
|
||||||
|
</script>
|
@ -1,36 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="d-flex justify-content-end">
|
|
||||||
<div class="btn-group" @click="handleClick">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-light dropdown-toggle"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
{{ trans(CHILL_TICKET_TICKET_PREVIOUS_TICKETS) }}
|
|
||||||
<span
|
|
||||||
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-chill-green"
|
|
||||||
>
|
|
||||||
{{ tickets.length }}
|
|
||||||
<span class="visually-hidden">Tickets</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
// Translations
|
|
||||||
import { trans, CHILL_TICKET_TICKET_PREVIOUS_TICKETS } from "translator";
|
|
||||||
|
|
||||||
// Types
|
|
||||||
import { Ticket } from "../../../types";
|
|
||||||
|
|
||||||
defineProps<{ tickets: Ticket[] }>();
|
|
||||||
|
|
||||||
function handleClick() {
|
|
||||||
alert("Sera disponible plus tard");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
@ -1,18 +1,23 @@
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
|
|
||||||
import { Ticket, TicketEmergencyState } from "../../../../types";
|
import { Ticket, TicketEmergencyState, TicketSimple } from "../../../../types";
|
||||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||||
import { ApiException } from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
import { ApiException } from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
import { getSinceCreated } from "../../utils/utils";
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
ticket: Ticket;
|
ticket: Ticket;
|
||||||
|
previous_tickets: TicketSimple[];
|
||||||
|
previous_ticket_details: Ticket;
|
||||||
action_icons: object;
|
action_icons: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const moduleTicket: Module<State, RootState> = {
|
export const moduleTicket: Module<State, RootState> = {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
ticket: {} as Ticket,
|
ticket: {} as Ticket,
|
||||||
|
previous_tickets: [] as TicketSimple[],
|
||||||
|
previous_ticket_details: {} as Ticket,
|
||||||
action_icons: {
|
action_icons: {
|
||||||
add_person: "fa fa-user-plus",
|
add_person: "fa fa-user-plus",
|
||||||
add_comment: "fa fa-comment",
|
add_comment: "fa fa-comment",
|
||||||
@ -37,18 +42,42 @@ export const moduleTicket: Module<State, RootState> = {
|
|||||||
);
|
);
|
||||||
return state.ticket;
|
return state.ticket;
|
||||||
},
|
},
|
||||||
|
getTicketHistory(state) {
|
||||||
|
return state.ticket.history;
|
||||||
|
},
|
||||||
|
getPreviousTickets(state) {
|
||||||
|
return state.previous_tickets;
|
||||||
|
},
|
||||||
|
getPreviousTicketHistory(state) {
|
||||||
|
return state.previous_ticket_details.history;
|
||||||
|
},
|
||||||
|
getPreviousTicketDetails(state) {
|
||||||
|
return state.previous_ticket_details;
|
||||||
|
},
|
||||||
|
getCurrentPersons(state) {
|
||||||
|
return state.ticket.currentPersons ?? [];
|
||||||
|
},
|
||||||
getActionIcons(state) {
|
getActionIcons(state) {
|
||||||
return state.action_icons;
|
return state.action_icons;
|
||||||
},
|
},
|
||||||
getDistinctAddressesHistory(state) {
|
getSinceCreated: (state) => (currentTime: Date) => {
|
||||||
return state.ticket.history;
|
if (!state.ticket.createdAt) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return getSinceCreated(state.ticket.createdAt.datetime, currentTime);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
setTicket(state, ticket: Ticket) {
|
setTicket(state, ticket: Ticket) {
|
||||||
state.ticket = ticket;
|
state.ticket = ticket;
|
||||||
},
|
},
|
||||||
|
setPreviousTickets(state, previousTickets: TicketSimple[]) {
|
||||||
|
state.previous_tickets = previousTickets;
|
||||||
|
},
|
||||||
|
setPreviousTicketDetails(state, ticket: Ticket) {
|
||||||
|
state.previous_ticket_details = ticket;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async closeTicket({ commit, state }) {
|
async closeTicket({ commit, state }) {
|
||||||
@ -87,5 +116,39 @@ export const moduleTicket: Module<State, RootState> = {
|
|||||||
throw error.name;
|
throw error.name;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async fetchTicketsByPerson({ commit, state }) {
|
||||||
|
try {
|
||||||
|
if (state.ticket.currentPersons.length === 0) {
|
||||||
|
commit("setPreviousTickets", []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { results }: { results: TicketSimple[] } = await makeFetch(
|
||||||
|
"GET",
|
||||||
|
`/api/1.0/ticket/ticket/list?byPerson=${state.ticket.currentPersons.map((person) => person.id).join(",")}`,
|
||||||
|
);
|
||||||
|
const excludeCurrentTicket = results.filter(
|
||||||
|
(ticket) => ticket.id !== state.ticket.id,
|
||||||
|
);
|
||||||
|
commit("setPreviousTickets", excludeCurrentTicket);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchPreviousTicketDetails({ commit }, ticketId: number) {
|
||||||
|
try {
|
||||||
|
const ticket: Ticket = await makeFetch(
|
||||||
|
"GET",
|
||||||
|
`/api/1.0/ticket/ticket/${ticketId}`,
|
||||||
|
);
|
||||||
|
commit("setPreviousTicketDetails", ticket);
|
||||||
|
return ticket;
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,94 @@
|
|||||||
|
import { ISOToDatetime } from "../../../../../../../../Bundle/ChillMainBundle/Resources/public/chill/js/date";
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
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_NO_MOTIVE,
|
||||||
|
} from "translator";
|
||||||
|
import { Ticket, TicketSimple } from "../../../types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcule et formate le temps écoulé depuis une date de création
|
||||||
|
* @param createdAt La date de création au format ISO
|
||||||
|
* @param currentTime La date actuelle
|
||||||
|
* @returns Une chaîne formatée représentant le temps écoulé
|
||||||
|
*/
|
||||||
|
export function getSinceCreated(createdAt: string, currentTime: Date): string {
|
||||||
|
const date = ISOToDatetime(createdAt);
|
||||||
|
if (date == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeDiff = Math.abs(currentTime.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,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function localizeTranslatableString(
|
||||||
|
translatableString: Record<string, string> | string,
|
||||||
|
): string {
|
||||||
|
// This would be implemented based on your localization logic
|
||||||
|
if (typeof translatableString === "string") {
|
||||||
|
return translatableString;
|
||||||
|
}
|
||||||
|
// Assuming it's an object with locale keys
|
||||||
|
return translatableString?.fr || translatableString?.en || "Unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDateTime(
|
||||||
|
dateTime: string,
|
||||||
|
dateStyle: string,
|
||||||
|
timeStyle: string,
|
||||||
|
): string {
|
||||||
|
return new Date(dateTime).toLocaleString("fr-FR", {
|
||||||
|
dateStyle: dateStyle as "short" | "medium" | "long" | "full",
|
||||||
|
timeStyle: timeStyle as "short" | "medium" | "long" | "full",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function getTicketTitle(ticket: Ticket | TicketSimple): string {
|
||||||
|
if (ticket.currentMotive) {
|
||||||
|
return `#${ticket.id} ${localizeTranslatableString(ticket.currentMotive.label)}`;
|
||||||
|
}
|
||||||
|
return `#${ticket.id} ${trans(CHILL_TICKET_TICKET_BANNER_NO_MOTIVE)}`;
|
||||||
|
}
|
@ -45,6 +45,7 @@ final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInte
|
|||||||
'type' => 'ticket_ticket',
|
'type' => 'ticket_ticket',
|
||||||
'id' => $object->getId(),
|
'id' => $object->getId(),
|
||||||
'externalRef' => $object->getExternalRef(),
|
'externalRef' => $object->getExternalRef(),
|
||||||
|
'createdAt' => $this->normalizer->normalize($object->getCreatedAt(), $format, $context),
|
||||||
'currentPersons' => $this->normalizer->normalize($object->getPersons(), $format, [
|
'currentPersons' => $this->normalizer->normalize($object->getPersons(), $format, [
|
||||||
'groups' => 'read',
|
'groups' => 'read',
|
||||||
]),
|
]),
|
||||||
@ -65,7 +66,6 @@ final class TicketNormalizer implements NormalizerInterface, NormalizerAwareInte
|
|||||||
$data += [
|
$data += [
|
||||||
'type_extended' => 'ticket_ticket:extended',
|
'type_extended' => 'ticket_ticket:extended',
|
||||||
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
'history' => array_values($this->serializeHistory($object, $format, ['groups' => 'read'])),
|
||||||
'createdAt' => $this->normalizer->normalize($object->getCreatedAt(), $format, $context),
|
|
||||||
'updatedAt' => $this->normalizer->normalize($object->getUpdatedAt(), $format, $context),
|
'updatedAt' => $this->normalizer->normalize($object->getUpdatedAt(), $format, $context),
|
||||||
'updatedBy' => $this->normalizer->normalize($object->getUpdatedBy(), $format, $context),
|
'updatedBy' => $this->normalizer->normalize($object->getUpdatedBy(), $format, $context),
|
||||||
'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, $context),
|
'createdBy' => $this->normalizer->normalize($object->getCreatedBy(), $format, $context),
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
chill_ticket:
|
chill_ticket:
|
||||||
list:
|
list:
|
||||||
title: Tickets
|
title: Tickets
|
||||||
|
title_previous_tickets: "{name, select, other {Précédent ticket de {name}} undefined {Précédent ticket}}"
|
||||||
|
no_tickets: "Aucun ticket"
|
||||||
filter:
|
filter:
|
||||||
to_me: Tickets qui me sont attribués
|
to_me: Tickets qui me sont attribués
|
||||||
in_alert: Tickets en alerte (délai de résolution dépassé)
|
in_alert: Tickets en alerte (délai de résolution dépassé)
|
||||||
@ -51,9 +53,9 @@ chill_ticket:
|
|||||||
success: "Appelants et usagers mis à jour"
|
success: "Appelants et usagers mis à jour"
|
||||||
error: "Aucun usager sélectionné"
|
error: "Aucun usager sélectionné"
|
||||||
banner:
|
banner:
|
||||||
concerned_usager: "Usagers concernés"
|
person: "{count, plural, =0 {Aucun usager concerné} =1 {Usager concerné} other {Usagers concernés}}"
|
||||||
speaker: "Attribué à"
|
speaker: "{count, plural, =0 {Aucun intervenant} =1 {Attribué à} other {Attribués à}}"
|
||||||
caller: "Appelant"
|
caller: "{count, plural, =0 {Aucun appelant} =1 {Appelant} other {Appelants}}"
|
||||||
open: "Ouvert"
|
open: "Ouvert"
|
||||||
closed: "Fermé"
|
closed: "Fermé"
|
||||||
since: "Depuis {time}"
|
since: "Depuis {time}"
|
||||||
|
@ -282,12 +282,12 @@ class TicketNormalizerTest extends KernelTestCase
|
|||||||
'currentState',
|
'currentState',
|
||||||
'emergency',
|
'emergency',
|
||||||
'caller',
|
'caller',
|
||||||
|
'createdAt',
|
||||||
];
|
];
|
||||||
|
|
||||||
// Keys that should not be present in read:simple normalization
|
// Keys that should not be present in read:simple normalization
|
||||||
$unexpectedKeys = [
|
$unexpectedKeys = [
|
||||||
'history',
|
'history',
|
||||||
'createdAt',
|
|
||||||
'updatedAt',
|
'updatedAt',
|
||||||
'updatedBy',
|
'updatedBy',
|
||||||
'createdBy',
|
'createdBy',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user