mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-08-21 23:23:51 +00:00
Merge branch 'multiple-tasks-from-board-78' into 'ticket-app-master'
Merge request contenant différentes tâches provenant du board 78 See merge request Chill-Projet/chill-bundles!864
This commit is contained in:
@@ -6,15 +6,19 @@ import {
|
|||||||
} from "ChillMainAssets/types";
|
} from "ChillMainAssets/types";
|
||||||
import { Person } from "ChillPersonAssets/types";
|
import { Person } from "ChillPersonAssets/types";
|
||||||
import { Thirdparty } from "../../../../ChillThirdPartyBundle/Resources/public/types";
|
import { Thirdparty } from "../../../../ChillThirdPartyBundle/Resources/public/types";
|
||||||
|
import { StoredObject } from "ChillDocStoreAssets/types";
|
||||||
|
|
||||||
export interface Motive {
|
export interface Motive {
|
||||||
type: "ticket_motive";
|
type: "ticket_motive";
|
||||||
id: number;
|
id: number;
|
||||||
active: boolean;
|
active: boolean;
|
||||||
label: TranslatableString;
|
label: TranslatableString;
|
||||||
|
makeTicketEmergency: TicketEmergencyState;
|
||||||
|
storedObjects: StoredObject[];
|
||||||
|
supplementaryComments: { label: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TicketState = "open" | "closed";
|
export type TicketState = "open" | "closed" | "close";
|
||||||
|
|
||||||
export type TicketEmergencyState = "yes" | "no";
|
export type TicketEmergencyState = "yes" | "no";
|
||||||
|
|
||||||
@@ -55,9 +59,7 @@ export interface Comment {
|
|||||||
updatedBy: User | null;
|
updatedBy: User | null;
|
||||||
updatedAt: DateTime | null;
|
updatedAt: DateTime | null;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
supplementaryComments: {
|
supplementaryComments: { label: string }[];
|
||||||
label: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddresseeHistory {
|
export interface AddresseeHistory {
|
||||||
@@ -162,7 +164,7 @@ export interface TicketFilters {
|
|||||||
byCreatedAfter: string;
|
byCreatedAfter: string;
|
||||||
byCreatedBefore: string;
|
byCreatedBefore: string;
|
||||||
byResponseTimeExceeded: boolean;
|
byResponseTimeExceeded: boolean;
|
||||||
byMyTickets: boolean;
|
byAddresseeToMe: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TicketFilterParams {
|
export interface TicketFilterParams {
|
||||||
@@ -173,5 +175,12 @@ export interface TicketFilterParams {
|
|||||||
byCreatedAfter?: string;
|
byCreatedAfter?: string;
|
||||||
byCreatedBefore?: string;
|
byCreatedBefore?: string;
|
||||||
byResponseTimeExceeded?: string;
|
byResponseTimeExceeded?: string;
|
||||||
byMyTickets?: boolean;
|
byAddresseeToMe?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TicketInitForm {
|
||||||
|
content: string;
|
||||||
|
motive?: Motive;
|
||||||
|
persons: Person[];
|
||||||
|
caller: Person | null;
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +1,68 @@
|
|||||||
<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" v-if="!loading">
|
||||||
<previous-tickets-component />
|
<previous-tickets-component :key="refreshKey" />
|
||||||
<ticket-history-list-component :history="ticketHistory" />
|
<ticket-history-list-component
|
||||||
|
:history="ticketHistory"
|
||||||
|
:key="ticketHistory.length"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<action-toolbar-component />
|
<div v-else class="text-center p-4">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">
|
||||||
|
{{ trans(CHILL_TICKET_LIST_LOADING_TICKET_DETAILS) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>{{ trans(CHILL_TICKET_LIST_LOADING_TICKET_DETAILS) }}</div>
|
||||||
|
</div>
|
||||||
|
<action-toolbar-component :key="refreshKey" v-if="!loading" />
|
||||||
|
<Modal
|
||||||
|
v-if="showTicketInitFormModal"
|
||||||
|
:show="showTicketInitFormModal"
|
||||||
|
modal-dialog-class="modal-lg"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h3 class="modal-title">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_INIT_FORM_TITLE) }}
|
||||||
|
</h3>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body>
|
||||||
|
<ticket-init-form-component
|
||||||
|
:motives="motives"
|
||||||
|
:suggested-persons="suggestedPersons"
|
||||||
|
@submit="handleFormSubmit"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</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, ref } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Ticket } from "../../types";
|
import { Ticket, Motive, TicketInitForm } from "../../types";
|
||||||
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
import Modal from "../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
|
||||||
import PreviousTicketsComponent from "./components/PreviousTicketsComponent.vue";
|
import PreviousTicketsComponent from "./components/PreviousTicketsComponent.vue";
|
||||||
import TicketHistoryListComponent from "../TicketList/components/TicketHistoryListComponent.vue";
|
import TicketHistoryListComponent from "../TicketList/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";
|
||||||
|
import TicketInitFormComponent from "./components/TicketInitFormComponent.vue";
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
CHILL_TICKET_TICKET_INIT_FORM_TITLE,
|
||||||
|
CHILL_TICKET_TICKET_INIT_FORM_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_INIT_FORM_ERROR,
|
||||||
|
CHILL_TICKET_LIST_LOADING_TICKET_DETAILS,
|
||||||
|
} from "translator";
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const toast = useToast();
|
const toast = useToast();
|
||||||
@@ -27,19 +70,72 @@ const toast = useToast();
|
|||||||
store.commit("setTicket", JSON.parse(window.initialTicket) as Ticket);
|
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.getTicketHistory);
|
const ticketHistory = computed(() => store.getters.getTicketHistory);
|
||||||
|
const motives = computed(() => store.getters.getMotives as Motive[]);
|
||||||
|
const suggestedPersons = computed(() => store.getters.getPersons as Person[]);
|
||||||
|
const showTicketInitFormModal = ref(false);
|
||||||
|
const loading = ref(true);
|
||||||
|
const refreshKey = ref(0);
|
||||||
|
|
||||||
|
async function handleFormSubmit(ticketForm: TicketInitForm) {
|
||||||
|
try {
|
||||||
|
if (ticketForm.motive) {
|
||||||
|
await store.dispatch("createMotive", ticketForm.motive);
|
||||||
|
}
|
||||||
|
if (ticketForm.content && ticketForm.content.trim() !== "") {
|
||||||
|
await store.dispatch("createComment", ticketForm.content);
|
||||||
|
}
|
||||||
|
await store.dispatch("setPersons", ticketForm.persons);
|
||||||
|
|
||||||
|
// Rafraîchir les données nécessaires
|
||||||
|
await store.dispatch("fetchTicketHistory");
|
||||||
|
await store.dispatch("fetchPreviousTickets");
|
||||||
|
|
||||||
|
// Forcer le rafraîchissement des composants
|
||||||
|
refreshKey.value++;
|
||||||
|
|
||||||
|
toast.success(trans(CHILL_TICKET_TICKET_INIT_FORM_SUCCESS));
|
||||||
|
closeModal();
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(
|
||||||
|
(error as string) || trans(CHILL_TICKET_TICKET_INIT_FORM_ERROR),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
showTicketInitFormModal.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
|
await store.dispatch("getCurrentUser");
|
||||||
await store.dispatch("fetchMotives");
|
await store.dispatch("fetchMotives");
|
||||||
await store.dispatch("fetchUserGroups");
|
await store.dispatch("fetchUserGroups");
|
||||||
await store.dispatch("fetchUsers");
|
await store.dispatch("fetchUsers");
|
||||||
await store.dispatch("getSuggestedPersons");
|
await store.dispatch("getSuggestedPersons");
|
||||||
|
showTicketInitFormModal.value = store.getters.isNewTicket;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(error as string);
|
toast.error(error as string);
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.loading-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(255, 255, 255, 0.7);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 9999;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
<form @submit.prevent="submitAction">
|
<form @submit.prevent="submitAction">
|
||||||
<comment-editor-component
|
<comment-editor-component
|
||||||
v-model="content"
|
v-model="content"
|
||||||
|
:motive="motive"
|
||||||
v-if="activeTab === 'add_comment'"
|
v-if="activeTab === 'add_comment'"
|
||||||
/>
|
/>
|
||||||
<addressee-selector-component
|
<addressee-selector-component
|
||||||
@@ -169,6 +170,7 @@ import {
|
|||||||
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,
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CANCEL,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_ERROR,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -249,71 +251,75 @@ const returnPath = computed((): string => {
|
|||||||
return returnPath;
|
return returnPath;
|
||||||
});
|
});
|
||||||
|
|
||||||
const motive = ref(
|
const motive = ref(ticket.value.currentMotive as Motive);
|
||||||
ticket.value.currentMotive ? ticket.value.currentMotive : ({} as Motive),
|
|
||||||
);
|
|
||||||
const content = ref("" as Comment["content"]);
|
const content = ref("" as Comment["content"]);
|
||||||
const addressees = ref(ticket.value.currentAddressees as UserGroupOrUser[]);
|
const addressees = ref(ticket.value.currentAddressees as UserGroupOrUser[]);
|
||||||
const persons = ref(ticket.value.currentPersons as Person[]);
|
const persons = ref(ticket.value.currentPersons as Person[]);
|
||||||
const caller = ref(ticket.value.caller as Person);
|
const caller = ref(ticket.value.caller as Person);
|
||||||
|
|
||||||
async function submitAction() {
|
async function submitAction() {
|
||||||
try {
|
switch (activeTab.value) {
|
||||||
switch (activeTab.value) {
|
case "add_comment":
|
||||||
case "add_comment":
|
if (!content.value) {
|
||||||
if (!content.value) {
|
toast.error(trans(CHILL_TICKET_TICKET_ADD_COMMENT_ERROR));
|
||||||
toast.error(trans(CHILL_TICKET_TICKET_ADD_COMMENT_ERROR));
|
} else {
|
||||||
} else {
|
await store.dispatch("createComment", content.value);
|
||||||
await store.dispatch("createComment", {
|
content.value = "";
|
||||||
ticketId: ticket.value.id,
|
activeTab.value = "";
|
||||||
content: content.value,
|
toast.success(trans(CHILL_TICKET_TICKET_ADD_COMMENT_SUCCESS));
|
||||||
});
|
}
|
||||||
content.value = "";
|
break;
|
||||||
activeTab.value = "";
|
case "set_motive":
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_ADD_COMMENT_SUCCESS));
|
if (!motive.value) {
|
||||||
}
|
toast.error(trans(CHILL_TICKET_TICKET_SET_MOTIVE_ERROR));
|
||||||
break;
|
} else {
|
||||||
case "set_motive":
|
await store.dispatch("createMotive", motive.value);
|
||||||
if (!motive.value.id) {
|
activeTab.value = "";
|
||||||
toast.error(trans(CHILL_TICKET_TICKET_SET_MOTIVE_ERROR));
|
toast.success(trans(CHILL_TICKET_TICKET_SET_MOTIVE_SUCCESS));
|
||||||
} else {
|
}
|
||||||
await store.dispatch("createMotive", {
|
break;
|
||||||
ticketId: ticket.value.id,
|
case "addressees_state":
|
||||||
motive: motive.value,
|
if (addressees.value.length) {
|
||||||
});
|
try {
|
||||||
activeTab.value = "";
|
await store.dispatch(
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_SET_MOTIVE_SUCCESS));
|
"setAddressees",
|
||||||
}
|
addressees.value as UserGroupOrUser[],
|
||||||
break;
|
);
|
||||||
case "addressees_state":
|
|
||||||
if (!addressees.value.length) {
|
|
||||||
toast.error(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_ERROR));
|
|
||||||
} else {
|
|
||||||
await store.dispatch("setAdressees", {
|
|
||||||
ticketId: ticket.value.id,
|
|
||||||
addressees: addressees.value,
|
|
||||||
});
|
|
||||||
activeTab.value = "";
|
activeTab.value = "";
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_SUCCESS));
|
toast.success(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_SUCCESS));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_ERROR), error);
|
||||||
|
toast.error(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_ERROR));
|
||||||
}
|
}
|
||||||
break;
|
} else {
|
||||||
case "persons_state":
|
await store.dispatch("setAddressees", [] as UserGroupOrUser[]);
|
||||||
await store.dispatch("setPersons", {
|
toast.success(trans(CHILL_TICKET_TICKET_ADD_ADDRESSEE_SUCCESS));
|
||||||
persons: persons.value,
|
}
|
||||||
});
|
break;
|
||||||
await store.dispatch("fetchTicketsByPerson");
|
case "persons_state":
|
||||||
activeTab.value = "";
|
if (persons.value.length) {
|
||||||
|
await store.dispatch("setPersons", persons.value);
|
||||||
|
try {
|
||||||
|
await store.dispatch("fetchTicketList", {
|
||||||
|
byPerson: persons.value.map((person) => person.id),
|
||||||
|
});
|
||||||
|
activeTab.value = "";
|
||||||
|
toast.success(trans(CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS));
|
||||||
|
} catch (error) {
|
||||||
|
console.error(trans(CHILL_TICKET_TICKET_SET_PERSONS_ERROR), error);
|
||||||
|
toast.error(trans(CHILL_TICKET_TICKET_SET_PERSONS_ERROR));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
store.dispatch("setPersons", [] as Person[]);
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS));
|
toast.success(trans(CHILL_TICKET_TICKET_SET_PERSONS_SUCCESS));
|
||||||
break;
|
}
|
||||||
}
|
break;
|
||||||
} catch (error) {
|
|
||||||
toast.error(error as string);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeTicket() {
|
async function closeTicket() {
|
||||||
try {
|
try {
|
||||||
await store.dispatch("closeTicket");
|
await store.dispatch("setTicketState", "close");
|
||||||
closeAllActions();
|
closeAllActions();
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS));
|
toast.success(trans(CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -324,7 +330,7 @@ async function closeTicket() {
|
|||||||
|
|
||||||
async function reopenTicket() {
|
async function reopenTicket() {
|
||||||
try {
|
try {
|
||||||
await store.dispatch("reopenTicket");
|
await store.dispatch("setTicketState", "open");
|
||||||
toast.success(trans(CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS));
|
toast.success(trans(CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@@ -337,7 +343,7 @@ function closeAllActions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watch(caller, async (newCaller) => {
|
watch(caller, async (newCaller) => {
|
||||||
await store.dispatch("setCaller", { caller: newCaller });
|
await store.dispatch("setCaller", newCaller);
|
||||||
await store.dispatch("getSuggestedPersons");
|
await store.dispatch("getSuggestedPersons");
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -8,31 +8,21 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<h2 v-if="ticket.currentPersons.length">
|
<h2 v-if="ticket.currentPersons.length">
|
||||||
{{ ticket.currentPersons.map((person) => person.text).join(", ") }}
|
{{
|
||||||
|
ticket.currentPersons
|
||||||
|
.map((person: Person) => person.text)
|
||||||
|
.join(", ")
|
||||||
|
}}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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">
|
||||||
<emergency-toggle-component
|
<emergency-toggle-component
|
||||||
v-model="isEmergencyLocal"
|
v-model="isEmergency"
|
||||||
@toggle-emergency="handleEmergencyToggle"
|
:disabled="!isOpen"
|
||||||
/>
|
/>
|
||||||
|
<state-toggle-component v-model="isOpen" />
|
||||||
<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>
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
<p class="created-at-timespan" v-if="ticket.createdAt">
|
<p class="created-at-timespan" v-if="ticket.createdAt">
|
||||||
@@ -102,26 +92,33 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from "vue";
|
import { computed, ref } from "vue";
|
||||||
import { useToast } from "vue-toast-notification";
|
import { useToast } from "vue-toast-notification";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import AddresseeComponent from "./Addressee/AddresseeComponent.vue";
|
import AddresseeComponent from "./Addressee/AddresseeComponent.vue";
|
||||||
import EmergencyToggleComponent from "./Emergency/EmergencyToggleComponent.vue";
|
import EmergencyToggleComponent from "./Emergency/EmergencyToggleComponent.vue";
|
||||||
|
import StateToggleComponent from "./State/StateToggleComponent.vue";
|
||||||
import PersonComponent from "./Person/PersonComponent.vue";
|
import PersonComponent from "./Person/PersonComponent.vue";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { Ticket, TicketEmergencyState } from "../../../types";
|
import { Ticket } from "../../../types";
|
||||||
import { Person } from "ChillPersonAssets/types";
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
CHILL_TICKET_TICKET_BANNER_OPEN,
|
|
||||||
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
|
||||||
CHILL_TICKET_TICKET_BANNER_SINCE,
|
CHILL_TICKET_TICKET_BANNER_SINCE,
|
||||||
CHILL_TICKET_TICKET_BANNER_SPEAKER,
|
CHILL_TICKET_TICKET_BANNER_SPEAKER,
|
||||||
CHILL_TICKET_TICKET_BANNER_CALLER,
|
CHILL_TICKET_TICKET_BANNER_CALLER,
|
||||||
CHILL_TICKET_TICKET_BANNER_PERSON,
|
CHILL_TICKET_TICKET_BANNER_PERSON,
|
||||||
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_ERROR,
|
||||||
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_ERROR,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_EMERGENCY_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_EMERGENCY_ERROR,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_NO_EMERGENCY_ERROR,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_NO_EMERGENCY_SUCCESS,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// Store
|
// Store
|
||||||
@@ -142,22 +139,56 @@ setInterval(() => {
|
|||||||
today.value = new Date();
|
today.value = new Date();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
const isOpen = computed(() => store.getters.isOpen);
|
const isOpen = computed({
|
||||||
const isEmergencyLocal = computed(() => store.getters.isEmergency);
|
get: () => store.getters.isOpen as boolean,
|
||||||
|
set: async (value: boolean) => {
|
||||||
|
try {
|
||||||
|
await store.dispatch("setTicketState", value ? "open" : "close");
|
||||||
|
toast.success(
|
||||||
|
trans(
|
||||||
|
value
|
||||||
|
? CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_SUCCESS
|
||||||
|
: CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_SUCCESS,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(
|
||||||
|
trans(
|
||||||
|
value
|
||||||
|
? CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_REOPEN_ERROR
|
||||||
|
: CHILL_TICKET_TICKET_ACTIONS_TOOLBAR_CLOSE_ERROR,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const isEmergency = computed({
|
||||||
|
get: () => store.getters.isEmergency as boolean,
|
||||||
|
set: async (value: boolean) => {
|
||||||
|
try {
|
||||||
|
await store.dispatch("setEmergency", value ? "yes" : "no");
|
||||||
|
toast.success(
|
||||||
|
trans(
|
||||||
|
value
|
||||||
|
? CHILL_TICKET_TICKET_BANNER_EMERGENCY_SUCCESS
|
||||||
|
: CHILL_TICKET_TICKET_BANNER_NO_EMERGENCY_SUCCESS,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
toast.error(
|
||||||
|
trans(
|
||||||
|
value
|
||||||
|
? CHILL_TICKET_TICKET_BANNER_EMERGENCY_ERROR
|
||||||
|
: CHILL_TICKET_TICKET_BANNER_NO_EMERGENCY_ERROR,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const since = computed(() => {
|
const since = computed(() => {
|
||||||
return store.getters.getSinceCreated(today.value);
|
return store.getters.getSinceCreated(today.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Methods
|
|
||||||
function handleEmergencyToggle(emergency: TicketEmergencyState) {
|
|
||||||
store.dispatch("toggleEmergency", emergency).catch(({ name, violations }) => {
|
|
||||||
if (name === "ValidationException" || name === "AccessException") {
|
|
||||||
violations.forEach((violation: string) =>
|
|
||||||
toast.open({ message: violation }),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
toast.open({ message: "An error occurred" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,19 +1,103 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<blockquote class="chill-user-quote">
|
<blockquote class="chill-user-quote">
|
||||||
|
<button
|
||||||
|
class="btn btn-sm btn-edit float-end"
|
||||||
|
title="Edit"
|
||||||
|
@click="editCommentModal = true"
|
||||||
|
style="top: 0.5rem; right: 0.5rem"
|
||||||
|
v-if="canBeEdited && isOpen"
|
||||||
|
/>
|
||||||
<p v-html="convertMarkdownToHtml(commentHistory.content)"></p>
|
<p v-html="convertMarkdownToHtml(commentHistory.content)"></p>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
|
<Modal
|
||||||
|
v-if="editCommentModal"
|
||||||
|
:show="editCommentModal"
|
||||||
|
modal-dialog-class="modal-xl"
|
||||||
|
@close="editCommentModal = false"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h5 class="modal-title">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_EDIT_COMMENT_TITLE) }}
|
||||||
|
</h5>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<comment-editor v-model="editedComment" />
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
@click="restoreComment"
|
||||||
|
v-if="commentHistory.deleted"
|
||||||
|
>
|
||||||
|
{{ trans(RESTORE) }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-delete" @click="deleteComment" v-else>
|
||||||
|
{{ trans(DELETE) }}
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-save" @click="saveComment">
|
||||||
|
{{ trans(SAVE) }}
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed, ref } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
import { marked } from "marked";
|
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 }>();
|
// Components
|
||||||
|
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||||
|
import Modal from "../../../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
|
||||||
|
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
SAVE,
|
||||||
|
DELETE,
|
||||||
|
RESTORE,
|
||||||
|
CHILL_TICKET_TICKET_EDIT_COMMENT_TITLE,
|
||||||
|
CHILL_TICKET_TICKET_EDIT_COMMENT_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_DELETE_COMMENT_SUCCESS,
|
||||||
|
CHILL_TICKET_TICKET_RESTORE_COMMENT_SUCCESS,
|
||||||
|
} from "translator";
|
||||||
|
import { useToast } from "vue-toast-notification";
|
||||||
|
|
||||||
|
const props = defineProps<{ commentHistory: Comment }>();
|
||||||
|
const toast = useToast();
|
||||||
|
const store = useStore();
|
||||||
|
const isOpen = computed(() => store.getters.isOpen as boolean);
|
||||||
|
const canBeEdited = computed(() =>
|
||||||
|
store.getters["canBeEdited"](props.commentHistory),
|
||||||
|
);
|
||||||
|
const editCommentModal = ref<boolean>(false);
|
||||||
|
const editedComment = ref<string>(props.commentHistory.content);
|
||||||
|
|
||||||
|
const saveComment = () => {
|
||||||
|
store.dispatch("editComment", {
|
||||||
|
id: props.commentHistory.id,
|
||||||
|
content: editedComment.value,
|
||||||
|
});
|
||||||
|
editCommentModal.value = false;
|
||||||
|
toast.success(trans(CHILL_TICKET_TICKET_EDIT_COMMENT_SUCCESS));
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteComment = () => {
|
||||||
|
store.dispatch("deleteComment", props.commentHistory.id);
|
||||||
|
editCommentModal.value = false;
|
||||||
|
toast.success(trans(CHILL_TICKET_TICKET_DELETE_COMMENT_SUCCESS));
|
||||||
|
};
|
||||||
|
|
||||||
|
const restoreComment = () => {
|
||||||
|
store.dispatch("restoreComment", props.commentHistory.id);
|
||||||
|
editCommentModal.value = false;
|
||||||
|
toast.success(trans(CHILL_TICKET_TICKET_RESTORE_COMMENT_SUCCESS));
|
||||||
|
};
|
||||||
|
|
||||||
const preprocess = (markdown: string): string => {
|
const preprocess = (markdown: string): string => {
|
||||||
return markdown;
|
return markdown;
|
||||||
|
@@ -1,24 +1,92 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="col-12" v-if="motive">
|
||||||
|
<div class="input-group mb-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
:value="localizeTranslatableString(motive.label)"
|
||||||
|
readonly
|
||||||
|
class="form-control"
|
||||||
|
/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<peloton-component :stored-objects="motive.storedObjects" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<comment-editor v-model="content" />
|
<comment-editor v-model="content" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12" v-if="motive">
|
||||||
|
<div
|
||||||
|
class="input-group mb-2"
|
||||||
|
v-for="(supplementaryComments, index) in motive.supplementaryComments"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="input-group-prepend d-flex align-items-center px-1">
|
||||||
|
<span class="badge rounded-pill bg-chill-red">
|
||||||
|
{{ supplementaryComments.label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="supplementaryCommentsInput[index]"
|
||||||
|
class="form-control"
|
||||||
|
@keyup.enter="addSupplementaryComments(index)"
|
||||||
|
@keydown.enter.prevent
|
||||||
|
/>
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button
|
||||||
|
class="input-group-text btn btn-submit"
|
||||||
|
type="button"
|
||||||
|
@click="addSupplementaryComments(index)"
|
||||||
|
>
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from "vue";
|
import { reactive, ref, watch } from "vue";
|
||||||
|
|
||||||
|
// Components
|
||||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||||
|
import PelotonComponent from "../PelotonComponent.vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { Motive } from "../../../../types";
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import { localizeTranslatableString } from "../../utils/utils";
|
||||||
|
import { StoredObject } from "ChillDocStoreAssets/types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue?: string;
|
modelValue?: string;
|
||||||
|
motive?: Motive;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const emit =
|
const supplementaryCommentsInput = reactive<string[]>([]);
|
||||||
defineEmits<(e: "update:modelValue", value: string | undefined) => void>();
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:modelValue": [value: string | undefined];
|
||||||
|
"show-peloton-modal": [storedObjects: StoredObject[]];
|
||||||
|
}>();
|
||||||
|
|
||||||
const content = ref(props.modelValue);
|
const content = ref(props.modelValue);
|
||||||
|
|
||||||
|
function addSupplementaryComments(index: number) {
|
||||||
|
if (supplementaryCommentsInput[index]) {
|
||||||
|
const supplementaryText = `**${props.motive?.supplementaryComments[index].label}**: ${supplementaryCommentsInput[index]}`;
|
||||||
|
content.value = content.value
|
||||||
|
? content.value + "\n" + supplementaryText
|
||||||
|
: supplementaryText;
|
||||||
|
supplementaryCommentsInput[index] = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(content, (value) => {
|
watch(content, (value) => {
|
||||||
emit("update:modelValue", value);
|
emit("update:modelValue", value);
|
||||||
});
|
});
|
||||||
|
@@ -3,36 +3,41 @@
|
|||||||
<button
|
<button
|
||||||
class="badge rounded-pill me-1"
|
class="badge rounded-pill me-1"
|
||||||
:class="{
|
:class="{
|
||||||
'bg-danger': modelValue,
|
'bg-chill-red': modelValue,
|
||||||
'bg-secondary': !modelValue,
|
'bg-secondary': !modelValue,
|
||||||
|
'no-pointer': props.disabled,
|
||||||
}"
|
}"
|
||||||
@click="toggleEmergency"
|
@click="toggleEmergency"
|
||||||
|
:disabled="props.disabled"
|
||||||
>
|
>
|
||||||
{{ trans(CHILL_TICKET_TICKET_BANNER_EMERGENCY) }}
|
{{ trans(CHILL_TICKET_TICKET_BANNER_EMERGENCY) }}
|
||||||
</button>
|
</button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.no-pointer {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { trans, CHILL_TICKET_TICKET_BANNER_EMERGENCY } from "translator";
|
import { trans, CHILL_TICKET_TICKET_BANNER_EMERGENCY } from "translator";
|
||||||
import { TicketEmergencyState } from "../../../../types";
|
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue: boolean;
|
modelValue: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
"update:modelValue": [value: boolean];
|
"update:modelValue": [value: boolean];
|
||||||
"toggle-emergency": [emergency: TicketEmergencyState];
|
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
function toggleEmergency() {
|
function toggleEmergency() {
|
||||||
const newValue = !props.modelValue;
|
const newValue = !props.modelValue;
|
||||||
emit("update:modelValue", newValue);
|
emit("update:modelValue", newValue);
|
||||||
emit("toggle-emergency", newValue ? "yes" : "no");
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@@ -0,0 +1,262 @@
|
|||||||
|
<template>
|
||||||
|
<button
|
||||||
|
class="input-group-text btn btn-primary"
|
||||||
|
type="button"
|
||||||
|
@click="handleClick"
|
||||||
|
:disabled="!storedObjects?.length"
|
||||||
|
>
|
||||||
|
<i class="fa fa-sitemap"></i>
|
||||||
|
</button>
|
||||||
|
<Modal
|
||||||
|
v-if="showPelotonsModal"
|
||||||
|
:show="showPelotonsModal"
|
||||||
|
modal-dialog-class="modal-xl"
|
||||||
|
@close="closeModal"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<div class="dropdown-container">
|
||||||
|
<select
|
||||||
|
v-model="selectedStoredObject"
|
||||||
|
class="form-select"
|
||||||
|
@change="onSelectionChange"
|
||||||
|
style="max-width: 400px"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="storedObject in storedObjects"
|
||||||
|
:key="storedObject.id"
|
||||||
|
:value="storedObject"
|
||||||
|
>
|
||||||
|
{{ storedObject.title }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #body>
|
||||||
|
<div v-if="selectedStoredObject && documentUrl" class="document-viewer">
|
||||||
|
<div v-if="documentType === 'image'" class="image-container">
|
||||||
|
<img
|
||||||
|
:src="documentUrl"
|
||||||
|
class="img-fluid"
|
||||||
|
style="max-width: 100%; height: auto"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="documentType === 'pdf'" class="pdf-container">
|
||||||
|
<iframe
|
||||||
|
:src="documentUrl"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
style="border: 1px solid #ccc"
|
||||||
|
></iframe>
|
||||||
|
<noscript>
|
||||||
|
<p>
|
||||||
|
{{ trans(CHILL_TICKET_PELOTON_IFRAME_NOT_SUPPORTED) }}
|
||||||
|
<a :href="documentUrl" target="_blank">
|
||||||
|
{{ trans(CHILL_TICKET_PELOTON_CLICK_TO_OPEN_PDF) }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</noscript>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="unsupported-document">
|
||||||
|
<p>{{ trans(CHILL_TICKET_PELOTON_UNSUPPORTED_TYPE) }}</p>
|
||||||
|
<a :href="documentUrl" target="_blank" class="btn btn-primary">
|
||||||
|
{{ trans(CHILL_TICKET_PELOTON_OPEN_NEW_TAB) }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isLoading" class="loading-indicator">
|
||||||
|
<div class="spinner-border" role="status">
|
||||||
|
<span class="visually-hidden">{{
|
||||||
|
trans(CHILL_TICKET_PELOTON_LOADING)
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
|
<p>{{ trans(CHILL_TICKET_PELOTON_LOADING_DOCUMENT) }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="error" class="alert alert-danger">
|
||||||
|
{{ error }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from "vue";
|
||||||
|
|
||||||
|
// Component
|
||||||
|
import Modal from "../../../../../../../ChillMainBundle/Resources/public/vuejs/_components/Modal.vue";
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
import {
|
||||||
|
CHILL_TICKET_PELOTON_LOADING_DOCUMENT,
|
||||||
|
CHILL_TICKET_PELOTON_LOADING,
|
||||||
|
CHILL_TICKET_PELOTON_ERROR_LOADING,
|
||||||
|
CHILL_TICKET_PELOTON_ERROR_NOT_READY,
|
||||||
|
CHILL_TICKET_PELOTON_UNSUPPORTED_TYPE,
|
||||||
|
CHILL_TICKET_PELOTON_OPEN_NEW_TAB,
|
||||||
|
CHILL_TICKET_PELOTON_IFRAME_NOT_SUPPORTED,
|
||||||
|
CHILL_TICKET_PELOTON_CLICK_TO_OPEN_PDF,
|
||||||
|
trans,
|
||||||
|
} from "translator";
|
||||||
|
// Type
|
||||||
|
import { StoredObject } from "ChillDocStoreAssets/types";
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {
|
||||||
|
download_and_decrypt_doc,
|
||||||
|
is_object_ready,
|
||||||
|
} from "ChillDocStoreAssets/vuejs/StoredObjectButton/helpers";
|
||||||
|
|
||||||
|
const props = defineProps<{ storedObjects: StoredObject[] }>();
|
||||||
|
|
||||||
|
const selectedStoredObject = ref<StoredObject | null>(null);
|
||||||
|
const documentUrl = ref<string>("");
|
||||||
|
const documentType = ref<string>("");
|
||||||
|
const isLoading = ref<boolean>(false);
|
||||||
|
const error = ref<string>("");
|
||||||
|
const showPelotonsModal = ref<boolean>(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.storedObjects,
|
||||||
|
(newStoredObjects) => {
|
||||||
|
if (
|
||||||
|
newStoredObjects &&
|
||||||
|
newStoredObjects.length > 0 &&
|
||||||
|
!selectedStoredObject.value
|
||||||
|
) {
|
||||||
|
selectedStoredObject.value = newStoredObjects[0];
|
||||||
|
onSelectionChange();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
async function onSelectionChange() {
|
||||||
|
if (!selectedStoredObject.value) {
|
||||||
|
cleanupPrevious();
|
||||||
|
documentType.value = "";
|
||||||
|
error.value = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true;
|
||||||
|
error.value = "";
|
||||||
|
cleanupPrevious();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const document = await is_object_ready(selectedStoredObject.value);
|
||||||
|
|
||||||
|
if (!document) {
|
||||||
|
error.value = trans(CHILL_TICKET_PELOTON_ERROR_NOT_READY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = await download_and_decrypt_doc(
|
||||||
|
selectedStoredObject.value,
|
||||||
|
selectedStoredObject.value.currentVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
const blob = new Blob([doc], { type: document.type });
|
||||||
|
documentUrl.value = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
if (document.type.startsWith("image/")) {
|
||||||
|
documentType.value = "image";
|
||||||
|
} else if (document.type === "application/pdf") {
|
||||||
|
documentType.value = "pdf";
|
||||||
|
} else {
|
||||||
|
documentType.value = "other";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(trans(CHILL_TICKET_PELOTON_ERROR_LOADING), err);
|
||||||
|
error.value = trans(CHILL_TICKET_PELOTON_ERROR_LOADING);
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nettoyer les URLs blob précédentes
|
||||||
|
function cleanupPrevious() {
|
||||||
|
if (documentUrl.value) {
|
||||||
|
URL.revokeObjectURL(documentUrl.value);
|
||||||
|
documentUrl.value = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick() {
|
||||||
|
showPelotonsModal.value = true;
|
||||||
|
if (
|
||||||
|
!selectedStoredObject.value &&
|
||||||
|
props.storedObjects &&
|
||||||
|
props.storedObjects.length > 0
|
||||||
|
) {
|
||||||
|
selectedStoredObject.value = props.storedObjects[0];
|
||||||
|
onSelectionChange();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
showPelotonsModal.value = false;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dropdown-container {
|
||||||
|
.form-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-viewer {
|
||||||
|
height: 80vh;
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
img {
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pdf-container {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unsupported-document {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #6c757d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-indicator {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
|
||||||
|
.spinner-border {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: #6c757d;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -37,8 +37,10 @@
|
|||||||
|
|
||||||
<template #body>
|
<template #body>
|
||||||
<ticket-list-component
|
<ticket-list-component
|
||||||
|
:hasMoreTickets="pagination.next !== null"
|
||||||
:tickets="previousTickets"
|
:tickets="previousTickets"
|
||||||
:title="trans(CHILL_TICKET_LIST_NO_TICKETS)"
|
:title="trans(CHILL_TICKET_LIST_NO_TICKETS)"
|
||||||
|
@fetchNextPage="fetchNextPage"
|
||||||
@view-ticket="handleViewTicket"
|
@view-ticket="handleViewTicket"
|
||||||
@edit-ticket="handleEditTicket"
|
@edit-ticket="handleEditTicket"
|
||||||
/>
|
/>
|
||||||
@@ -59,6 +61,7 @@ import TicketListComponent from "../../TicketList/components/TicketListComponent
|
|||||||
import {
|
import {
|
||||||
trans,
|
trans,
|
||||||
CHILL_TICKET_LIST_NO_TICKETS,
|
CHILL_TICKET_LIST_NO_TICKETS,
|
||||||
|
CHILL_TICKET_LIST_ERROR_LOADING_TICKET,
|
||||||
CHILL_TICKET_TICKET_PREVIOUS_TICKETS,
|
CHILL_TICKET_TICKET_PREVIOUS_TICKETS,
|
||||||
CHILL_TICKET_LIST_TITLE_PREVIOUS_TICKETS,
|
CHILL_TICKET_LIST_TITLE_PREVIOUS_TICKETS,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
@@ -69,12 +72,14 @@ import { TicketSimple } from "../../../types";
|
|||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { localizedUrl } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
import { localizedUrl } from "ChillMainAssets/lib/localizationHelper/localizationHelper";
|
||||||
|
import { Pagination } from "ChillMainAssets/lib/api/apiMethods";
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const showPreviousTicketModal = ref(false);
|
const showPreviousTicketModal = ref(false);
|
||||||
const showTicketHistoryModal = ref(false);
|
const showTicketHistoryModal = ref(false);
|
||||||
const selectedTicketId = ref<number | null>(null);
|
const selectedTicketId = ref<number | null>(null);
|
||||||
|
|
||||||
|
const pagination = computed(() => store.getters.getPagination as Pagination);
|
||||||
const currentPersons = computed(
|
const currentPersons = computed(
|
||||||
() => store.getters.getCurrentPersons as Person[],
|
() => store.getters.getCurrentPersons as Person[],
|
||||||
);
|
);
|
||||||
@@ -84,11 +89,13 @@ const previousTickets = computed(
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
try {
|
try {
|
||||||
await store.dispatch("fetchTicketList", {
|
if (currentPersons.value.length) {
|
||||||
byPerson: currentPersons.value.map((person) => person.id),
|
await store.dispatch("fetchTicketList", {
|
||||||
});
|
byPerson: currentPersons.value.map((person) => person.id),
|
||||||
|
});
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors du chargement des tickets:", error);
|
console.error(trans(CHILL_TICKET_LIST_ERROR_LOADING_TICKET), error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,10 +114,16 @@ async function handleViewTicket(ticketId: number) {
|
|||||||
try {
|
try {
|
||||||
await store.dispatch("fetchTicket", ticketId);
|
await store.dispatch("fetchTicket", ticketId);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erreur lors du chargement du ticket:", error);
|
console.error(trans(CHILL_TICKET_LIST_ERROR_LOADING_TICKET), error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchNextPage = async () => {
|
||||||
|
if (pagination.value.next) {
|
||||||
|
await store.dispatch("fetchTicketListByUrl", pagination.value.next);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function handleEditTicket(ticketId: number) {
|
function handleEditTicket(ticketId: number) {
|
||||||
const returnPath = localizedUrl(`/ticket/ticket/list`);
|
const returnPath = localizedUrl(`/ticket/ticket/list`);
|
||||||
window.location.href = localizedUrl(
|
window.location.href = localizedUrl(
|
||||||
|
@@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<span class="d-block d-sm-inline-block ms-sm-3 ms-md-0">
|
||||||
|
<button
|
||||||
|
class="badge rounded-pill me-1"
|
||||||
|
:class="{
|
||||||
|
'bg-chill-green': modelValue,
|
||||||
|
'bg-chill-red': !modelValue,
|
||||||
|
}"
|
||||||
|
@click="toggleEmergency"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
modelValue
|
||||||
|
? trans(CHILL_TICKET_TICKET_BANNER_OPEN)
|
||||||
|
: trans(CHILL_TICKET_TICKET_BANNER_CLOSED)
|
||||||
|
}}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_OPEN,
|
||||||
|
CHILL_TICKET_TICKET_BANNER_CLOSED,
|
||||||
|
} from "translator";
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps<{
|
||||||
|
modelValue: boolean;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Emits
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:modelValue": [value: boolean];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
// Methods
|
||||||
|
function toggleEmergency() {
|
||||||
|
const newValue = !props.modelValue;
|
||||||
|
emit("update:modelValue", newValue);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
a.flag-toggle {
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
text-decoration: underline;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
margin: auto 0.4em;
|
||||||
|
}
|
||||||
|
span.on {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button.badge {
|
||||||
|
&.bg-secondary {
|
||||||
|
opacity: 0.5;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<form @submit.prevent="submitForm">
|
||||||
|
<!-- Sélection du motif -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{
|
||||||
|
trans(CHILL_TICKET_TICKET_SET_MOTIVE_TITLE)
|
||||||
|
}}</label>
|
||||||
|
<motive-selector-component
|
||||||
|
v-model="ticketForm.motive"
|
||||||
|
:motives="motives"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Sélection des personnes -->
|
||||||
|
<div class="row mb-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_SET_PERSONS_TITLE_CALLER) }}
|
||||||
|
</label>
|
||||||
|
<persons-selector-component
|
||||||
|
v-model="ticketForm.caller"
|
||||||
|
:suggested="[]"
|
||||||
|
:multiple="false"
|
||||||
|
:types="['person', 'thirdparty']"
|
||||||
|
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_SET_PERSONS_TITLE_PERSON) }}
|
||||||
|
</label>
|
||||||
|
<persons-selector-component
|
||||||
|
v-model="ticketForm.persons"
|
||||||
|
:suggested="suggestedPersons"
|
||||||
|
:multiple="true"
|
||||||
|
:types="['person']"
|
||||||
|
:label="trans(CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Éditeur de commentaire -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">{{
|
||||||
|
trans(CHILL_TICKET_TICKET_ADD_COMMENT_TITLE)
|
||||||
|
}}</label>
|
||||||
|
<comment-editor-component
|
||||||
|
v-model="ticketForm.content"
|
||||||
|
:motive="ticketForm.motive"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Boutons d'action -->
|
||||||
|
<div class="d-flex justify-content-end gap-2 mt-4">
|
||||||
|
<button type="button" class="btn btn-secondary" @click="resetForm">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_INIT_FORM_RESET) }}
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_INIT_FORM_SUBMIT) }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, watch } from "vue";
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import MotiveSelectorComponent from "./Motive/MotiveSelectorComponent.vue";
|
||||||
|
import CommentEditorComponent from "./Comment/CommentEditorComponent.vue";
|
||||||
|
import PersonsSelectorComponent from "./Person/PersonsSelectorComponent.vue";
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { Motive, TicketInitForm } from "../../../types";
|
||||||
|
import { Person } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
|
// Translations
|
||||||
|
import {
|
||||||
|
trans,
|
||||||
|
CHILL_TICKET_TICKET_INIT_FORM_SUBMIT,
|
||||||
|
CHILL_TICKET_TICKET_INIT_FORM_RESET,
|
||||||
|
CHILL_TICKET_TICKET_SET_MOTIVE_TITLE,
|
||||||
|
CHILL_TICKET_TICKET_ADD_COMMENT_TITLE,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_CALLER,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_PERSON,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
|
||||||
|
CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL,
|
||||||
|
} from "translator";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
motives: Motive[];
|
||||||
|
suggestedPersons: Person[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
"update:modelValue": [value: TicketInitForm];
|
||||||
|
submit: [formData: TicketInitForm];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
const ticketForm = reactive({
|
||||||
|
content: "",
|
||||||
|
motive: undefined as Motive | undefined,
|
||||||
|
persons: [] as Person[],
|
||||||
|
caller: null as Person | null,
|
||||||
|
} as TicketInitForm);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => ticketForm.caller,
|
||||||
|
async (newCaller) => {
|
||||||
|
await store.dispatch("setCaller", newCaller);
|
||||||
|
await store.dispatch("getSuggestedPersons");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
function submitForm() {
|
||||||
|
emit("submit", {
|
||||||
|
content: ticketForm.content,
|
||||||
|
motive: ticketForm.motive,
|
||||||
|
persons: [...ticketForm.persons],
|
||||||
|
caller: ticketForm.caller,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
ticketForm.content = "";
|
||||||
|
ticketForm.motive = undefined;
|
||||||
|
ticketForm.persons = [];
|
||||||
|
ticketForm.caller = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.card {
|
||||||
|
border: 1px solid #dee2e6;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
border-bottom: 1px solid #dee2e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-2 {
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -8,6 +8,7 @@ import {
|
|||||||
State as TicketListState,
|
State as TicketListState,
|
||||||
moduleTicketList,
|
moduleTicketList,
|
||||||
} from "./modules/ticket_list";
|
} from "./modules/ticket_list";
|
||||||
|
import { State as UserState, moduleUser } from "./modules/user";
|
||||||
|
|
||||||
export interface RootState {
|
export interface RootState {
|
||||||
motive: MotiveStates;
|
motive: MotiveStates;
|
||||||
@@ -16,6 +17,7 @@ export interface RootState {
|
|||||||
addressee: AddresseeStates;
|
addressee: AddresseeStates;
|
||||||
persons: PersonsState;
|
persons: PersonsState;
|
||||||
ticketList: TicketListState;
|
ticketList: TicketListState;
|
||||||
|
user: UserState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const store = createStore<RootState>({
|
export const store = createStore<RootState>({
|
||||||
@@ -26,5 +28,6 @@ export const store = createStore<RootState>({
|
|||||||
addressee: moduleAddressee,
|
addressee: moduleAddressee,
|
||||||
persons: modulePersons,
|
persons: modulePersons,
|
||||||
ticketList: moduleTicketList,
|
ticketList: moduleTicketList,
|
||||||
|
user: moduleUser,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@@ -61,15 +61,11 @@ export const moduleAddressee: Module<State, RootState> = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async setAdressees(
|
async setAddressees({ commit, rootState }, addressees: UserGroupOrUser[]) {
|
||||||
{ commit },
|
|
||||||
datas: { ticketId: number; addressees: UserGroupOrUser[] },
|
|
||||||
) {
|
|
||||||
const { ticketId, addressees } = datas;
|
|
||||||
try {
|
try {
|
||||||
const result = await makeFetch(
|
const result = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/${ticketId}/addressees/set`,
|
`/api/1.0/ticket/${rootState.ticket.ticket.id}/addressees/set`,
|
||||||
{
|
{
|
||||||
addressees: addressees.map((addressee) => {
|
addressees: addressees.map((addressee) => {
|
||||||
return { id: addressee.id, type: addressee.type };
|
return { id: addressee.id, type: addressee.type };
|
||||||
|
@@ -3,7 +3,7 @@ import { makeFetch } from "../../../../../../../../ChillMainBundle/Resources/pub
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
|
|
||||||
import { Comment } from "../../../../types";
|
import { Comment, Ticket } from "../../../../types";
|
||||||
import { ApiException } from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
import { ApiException } from "../../../../../../../../ChillMainBundle/Resources/public/types";
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
@@ -14,18 +14,32 @@ export const moduleComment: Module<State, RootState> = {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
comments: [] as Comment[],
|
comments: [] as Comment[],
|
||||||
}),
|
}),
|
||||||
getters: {},
|
getters: {
|
||||||
|
canBeDisplayed:
|
||||||
|
(state: State, getters: unknown, rootState: RootState) =>
|
||||||
|
(comment: Comment) => {
|
||||||
|
return (
|
||||||
|
(comment.deleted &&
|
||||||
|
comment.createdBy?.username ===
|
||||||
|
rootState.user.currentUser?.username) ||
|
||||||
|
!comment.deleted
|
||||||
|
);
|
||||||
|
},
|
||||||
|
canBeEdited:
|
||||||
|
(state: State, getters: unknown, rootState: RootState) =>
|
||||||
|
(comment: Comment) => {
|
||||||
|
return (
|
||||||
|
comment.createdBy?.username === rootState.user.currentUser?.username
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
mutations: {},
|
mutations: {},
|
||||||
actions: {
|
actions: {
|
||||||
async createComment(
|
async createComment({ commit, rootState }, content: Comment["content"]) {
|
||||||
{ commit },
|
|
||||||
datas: { ticketId: number; content: Comment["content"] },
|
|
||||||
) {
|
|
||||||
const { ticketId, content } = datas;
|
|
||||||
try {
|
try {
|
||||||
const result = await makeFetch(
|
const result: Ticket = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/${ticketId}/comment/add`,
|
`/api/1.0/ticket/${rootState.ticket.ticket.id}/comment/add`,
|
||||||
{ content },
|
{ content },
|
||||||
);
|
);
|
||||||
commit("setTicket", result);
|
commit("setTicket", result);
|
||||||
@@ -34,5 +48,46 @@ export const moduleComment: Module<State, RootState> = {
|
|||||||
throw error.name;
|
throw error.name;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
async editComment(
|
||||||
|
{ commit },
|
||||||
|
{ id, content }: { id: Comment["id"]; content: Comment["content"] },
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const result: Comment = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/comment/${id}/edit`,
|
||||||
|
{ content },
|
||||||
|
);
|
||||||
|
commit("setTicketHistoryComment", result);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteComment({ commit }, id: Comment["id"]) {
|
||||||
|
try {
|
||||||
|
const result: Comment = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/comment/${id}/delete`,
|
||||||
|
);
|
||||||
|
commit("setTicketHistoryComment", result);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async restoreComment({ commit }, id: Comment["id"]) {
|
||||||
|
try {
|
||||||
|
const result: Comment = await makeFetch(
|
||||||
|
"POST",
|
||||||
|
`/api/1.0/ticket/comment/${id}/restore`,
|
||||||
|
);
|
||||||
|
commit("setTicketHistoryComment", result);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -40,15 +40,11 @@ export const moduleMotive: Module<State, RootState> = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async createMotive(
|
async createMotive({ commit, rootState }, motive: Motive) {
|
||||||
{ commit },
|
|
||||||
datas: { ticketId: number; motive: Motive },
|
|
||||||
) {
|
|
||||||
const { ticketId, motive } = datas;
|
|
||||||
try {
|
try {
|
||||||
const result = await makeFetch(
|
const result = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/${ticketId}/motive/set`,
|
`/api/1.0/ticket/${rootState.ticket.ticket.id}/motive/set`,
|
||||||
{
|
{
|
||||||
motive: {
|
motive: {
|
||||||
id: motive.id,
|
id: motive.id,
|
||||||
|
@@ -24,19 +24,17 @@ export const modulePersons: Module<State, RootState> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async setPersons(
|
async setPersons({ commit, rootState }, persons: Person[]) {
|
||||||
{ commit, rootState: RootState },
|
const personData = persons.map((person: Person) => ({
|
||||||
payload: { persons: Person[] },
|
|
||||||
) {
|
|
||||||
const persons = payload.persons.map((person: Person) => ({
|
|
||||||
id: person.id,
|
id: person.id,
|
||||||
type: person.type,
|
type: person.type,
|
||||||
}));
|
}));
|
||||||
|
console.log("Setting persons:", personData);
|
||||||
try {
|
try {
|
||||||
const result: Ticket = await makeFetch(
|
const result: Ticket = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/${RootState.ticket.ticket.id}/persons/set`,
|
`/api/1.0/ticket/${rootState.ticket.ticket.id}/persons/set`,
|
||||||
{ persons },
|
{ persons: personData },
|
||||||
);
|
);
|
||||||
commit("setTicket", result);
|
commit("setTicket", result);
|
||||||
|
|
||||||
@@ -46,21 +44,18 @@ export const modulePersons: Module<State, RootState> = {
|
|||||||
throw error.name;
|
throw error.name;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async setCaller(
|
async setCaller({ commit, rootState }, caller: Person | null) {
|
||||||
{ commit, rootState: RootState },
|
|
||||||
payload: { caller: Person | null },
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
const caller = payload.caller
|
const callerData = caller
|
||||||
? {
|
? {
|
||||||
id: payload.caller.id,
|
id: caller.id,
|
||||||
type: payload.caller.type,
|
type: caller.type,
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
const result: Ticket = await makeFetch(
|
const result: Ticket = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/ticket/${RootState.ticket.ticket.id}/set-caller`,
|
`/api/1.0/ticket/ticket/${rootState.ticket.ticket.id}/set-caller`,
|
||||||
{ caller },
|
{ caller: callerData },
|
||||||
);
|
);
|
||||||
commit("setTicket", result as Ticket);
|
commit("setTicket", result as Ticket);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
@@ -69,9 +64,9 @@ export const modulePersons: Module<State, RootState> = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async getSuggestedPersons({ commit, rootState: RootState }) {
|
async getSuggestedPersons({ commit, rootState }) {
|
||||||
try {
|
try {
|
||||||
const ticketId = RootState.ticket.ticket.id;
|
const ticketId = rootState.ticket.ticket.id;
|
||||||
const result: Person[] = await makeFetch(
|
const result: Person[] = await makeFetch(
|
||||||
"GET",
|
"GET",
|
||||||
`/api/1.0/ticket/ticket/${ticketId}/suggest-person`,
|
`/api/1.0/ticket/ticket/${ticketId}/suggest-person`,
|
||||||
|
@@ -1,7 +1,13 @@
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
|
|
||||||
import { Ticket, TicketEmergencyState } from "../../../../types";
|
import {
|
||||||
|
Comment,
|
||||||
|
Ticket,
|
||||||
|
TicketEmergencyState,
|
||||||
|
TicketHistoryLine,
|
||||||
|
TicketState,
|
||||||
|
} 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";
|
import { getSinceCreated } from "../../utils/utils";
|
||||||
@@ -30,7 +36,10 @@ export const moduleTicket: Module<State, RootState> = {
|
|||||||
return state.ticket.currentState === "open";
|
return state.ticket.currentState === "open";
|
||||||
},
|
},
|
||||||
isEmergency(state) {
|
isEmergency(state) {
|
||||||
return state.ticket.emergency == "yes" ? true : false;
|
return state.ticket.emergency == "yes";
|
||||||
|
},
|
||||||
|
isNewTicket(state) {
|
||||||
|
return state.ticket.history.length == 3;
|
||||||
},
|
},
|
||||||
getTicket(state) {
|
getTicket(state) {
|
||||||
state.ticket.history = state.ticket.history.sort((a, b) =>
|
state.ticket.history = state.ticket.history.sort((a, b) =>
|
||||||
@@ -59,13 +68,24 @@ export const moduleTicket: Module<State, RootState> = {
|
|||||||
setTicket(state, ticket: Ticket) {
|
setTicket(state, ticket: Ticket) {
|
||||||
state.ticket = ticket;
|
state.ticket = ticket;
|
||||||
},
|
},
|
||||||
|
setTicketHistoryComment(state, ticketHistoryData: Comment) {
|
||||||
|
const index = state.ticket.history.findIndex(
|
||||||
|
(item: TicketHistoryLine) =>
|
||||||
|
item.event_type == "add_comment" &&
|
||||||
|
item.data.id === ticketHistoryData.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
state.ticket.history[index].data = ticketHistoryData;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async closeTicket({ commit, state }) {
|
async setTicketState({ commit, state }, ticketState: TicketState) {
|
||||||
try {
|
try {
|
||||||
const result: Ticket = await makeFetch(
|
const result: Ticket = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
`/api/1.0/ticket/ticket/${state.ticket.id}/close`,
|
`/api/1.0/ticket/ticket/${state.ticket.id}/${ticketState}`,
|
||||||
);
|
);
|
||||||
commit("setTicket", result as Ticket);
|
commit("setTicket", result as Ticket);
|
||||||
} catch (e: unknown) {
|
} catch (e: unknown) {
|
||||||
@@ -73,19 +93,23 @@ export const moduleTicket: Module<State, RootState> = {
|
|||||||
throw error.name;
|
throw error.name;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async reopenTicket({ commit, state }) {
|
|
||||||
|
async fetchTicket({ commit }, ticketId: number) {
|
||||||
try {
|
try {
|
||||||
const result: Ticket = await makeFetch(
|
const ticket: Ticket = await makeFetch(
|
||||||
"POST",
|
"GET",
|
||||||
`/api/1.0/ticket/ticket/${state.ticket.id}/open`,
|
`/api/1.0/ticket/ticket/${ticketId}`,
|
||||||
);
|
);
|
||||||
commit("setTicket", result as Ticket);
|
commit("setTicket", ticket);
|
||||||
} catch (e: unknown) {
|
} catch (error) {
|
||||||
const error = e as ApiException;
|
console.error(
|
||||||
throw error.name;
|
"Erreur lors du chargement du ticket:",
|
||||||
|
error as ApiException,
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async toggleEmergency({ commit, state }, emergency: TicketEmergencyState) {
|
async setEmergency({ commit, state }, emergency: TicketEmergencyState) {
|
||||||
try {
|
try {
|
||||||
const result: Ticket = await makeFetch(
|
const result: Ticket = await makeFetch(
|
||||||
"POST",
|
"POST",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { Module } from "vuex";
|
import { Module } from "vuex";
|
||||||
import { RootState } from "..";
|
import { RootState } from "..";
|
||||||
|
|
||||||
import { Ticket, TicketFilterParams, TicketSimple } from "../../../../types";
|
import { TicketFilterParams, TicketSimple } from "../../../../types";
|
||||||
import {
|
import {
|
||||||
makeFetch,
|
makeFetch,
|
||||||
Pagination,
|
Pagination,
|
||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
ticket_list: TicketSimple[];
|
ticket_list: TicketSimple[];
|
||||||
ticket_details: Ticket | null;
|
|
||||||
pagination: Pagination;
|
pagination: Pagination;
|
||||||
count: number;
|
count: number;
|
||||||
user: User;
|
user: User;
|
||||||
@@ -23,7 +22,6 @@ export interface State {
|
|||||||
export const moduleTicketList: Module<State, RootState> = {
|
export const moduleTicketList: Module<State, RootState> = {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
ticket_list: [],
|
ticket_list: [],
|
||||||
ticket_details: null,
|
|
||||||
pagination: {
|
pagination: {
|
||||||
first: 0,
|
first: 0,
|
||||||
items_per_page: 50,
|
items_per_page: 50,
|
||||||
@@ -38,9 +36,6 @@ export const moduleTicketList: Module<State, RootState> = {
|
|||||||
getTicketList(state): TicketSimple[] {
|
getTicketList(state): TicketSimple[] {
|
||||||
return state.ticket_list;
|
return state.ticket_list;
|
||||||
},
|
},
|
||||||
getTicketDetails(state): Ticket | null {
|
|
||||||
return state.ticket_details;
|
|
||||||
},
|
|
||||||
getPagination(state) {
|
getPagination(state) {
|
||||||
return state.pagination;
|
return state.pagination;
|
||||||
},
|
},
|
||||||
@@ -55,9 +50,6 @@ export const moduleTicketList: Module<State, RootState> = {
|
|||||||
setTicketList(state, ticketList: TicketSimple[]) {
|
setTicketList(state, ticketList: TicketSimple[]) {
|
||||||
state.ticket_list = ticketList;
|
state.ticket_list = ticketList;
|
||||||
},
|
},
|
||||||
setTicketDetails(state, ticket: Ticket | null) {
|
|
||||||
state.ticket_details = ticket;
|
|
||||||
},
|
|
||||||
setPagination(state, pagination: State["pagination"]) {
|
setPagination(state, pagination: State["pagination"]) {
|
||||||
state.pagination = pagination;
|
state.pagination = pagination;
|
||||||
},
|
},
|
||||||
@@ -69,11 +61,26 @@ export const moduleTicketList: Module<State, RootState> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async fetchTicketList({ commit }, ticketFilterParams: TicketFilterParams) {
|
async fetchTicketList(
|
||||||
|
{ commit },
|
||||||
|
ticketFilterParams: TicketFilterParams | null,
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const params = new URLSearchParams(
|
let params = "";
|
||||||
ticketFilterParams as Record<string, string>,
|
if (ticketFilterParams) {
|
||||||
).toString();
|
const filteredParams = Object.fromEntries(
|
||||||
|
Object.entries(ticketFilterParams).filter(
|
||||||
|
([, value]) =>
|
||||||
|
value !== undefined &&
|
||||||
|
value !== null &&
|
||||||
|
value !== "" &&
|
||||||
|
value.length > 0,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
params = new URLSearchParams(
|
||||||
|
filteredParams as Record<string, string>,
|
||||||
|
).toString();
|
||||||
|
}
|
||||||
const { results, pagination, count } = (await makeFetch(
|
const { results, pagination, count } = (await makeFetch(
|
||||||
"GET",
|
"GET",
|
||||||
`/api/1.0/ticket/ticket/list/?${params}`,
|
`/api/1.0/ticket/ticket/list/?${params}`,
|
||||||
@@ -94,7 +101,7 @@ export const moduleTicketList: Module<State, RootState> = {
|
|||||||
try {
|
try {
|
||||||
const user = await makeFetch(
|
const user = await makeFetch(
|
||||||
"GET",
|
"GET",
|
||||||
"http://localhost:8000/api/1.0/main/whoami.json",
|
"/api/1.0/main/whoami.json",
|
||||||
);
|
);
|
||||||
commit("setUser", user);
|
commit("setUser", user);
|
||||||
return user;
|
return user;
|
||||||
@@ -106,21 +113,6 @@ export const moduleTicketList: Module<State, RootState> = {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async fetchTicketDetails({ commit }, ticketId: number) {
|
|
||||||
try {
|
|
||||||
const ticket: Ticket = await makeFetch(
|
|
||||||
"GET",
|
|
||||||
`/api/1.0/ticket/ticket/${ticketId}`,
|
|
||||||
);
|
|
||||||
commit("setTicketDetails", ticket);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
"Erreur lors du chargement du ticket:",
|
|
||||||
error as ApiException,
|
|
||||||
);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async fetchTicketListByUrl({ commit, state }, url: string) {
|
async fetchTicketListByUrl({ commit, state }, url: string) {
|
||||||
try {
|
try {
|
||||||
const { results, pagination, count } = (await makeFetch(
|
const { results, pagination, count } = (await makeFetch(
|
||||||
|
@@ -0,0 +1,41 @@
|
|||||||
|
import { Module } from "vuex";
|
||||||
|
import { RootState } from "../index";
|
||||||
|
import { ApiException, User } from "ChillMainAssets/types";
|
||||||
|
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
currentUser: User | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const moduleUser: Module<State, RootState> = {
|
||||||
|
state: () => ({
|
||||||
|
currentUser: null,
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
currentUser(state) {
|
||||||
|
return state.currentUser;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mutations: {
|
||||||
|
setCurrentUser(state, user: User) {
|
||||||
|
state.currentUser = user;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async getCurrentUser({ commit }) {
|
||||||
|
try {
|
||||||
|
const userData: User = await makeFetch(
|
||||||
|
"GET",
|
||||||
|
"/api/1.0/main/whoami.json",
|
||||||
|
);
|
||||||
|
commit("setCurrentUser", userData);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const error = e as ApiException;
|
||||||
|
throw error.name;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
@@ -49,7 +49,7 @@ import { ref, computed, onMounted } from "vue";
|
|||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { TicketSimple, Motive } from "../../types";
|
import { TicketSimple, Motive, TicketFilterParams } from "../../types";
|
||||||
import type { Person } from "ChillPersonAssets/types";
|
import type { Person } from "ChillPersonAssets/types";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
@@ -60,15 +60,6 @@ import { Pagination } from "ChillMainAssets/lib/api/apiMethods";
|
|||||||
// Translations
|
// Translations
|
||||||
import { trans, CHILL_TICKET_LIST_LOADING_TICKET } from "translator";
|
import { trans, CHILL_TICKET_LIST_LOADING_TICKET } from "translator";
|
||||||
|
|
||||||
interface TicketFilterParams {
|
|
||||||
byPerson?: number[];
|
|
||||||
byCurrentState?: string[];
|
|
||||||
byCurrentStateEmergency?: string[];
|
|
||||||
byMotives?: number[];
|
|
||||||
byCreatedAfter?: string;
|
|
||||||
byCreatedBefore?: string;
|
|
||||||
byResponseTimeExceeded?: string;
|
|
||||||
}
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const title = window.title;
|
const title = window.title;
|
||||||
@@ -91,7 +82,6 @@ const handleFiltersChanged = async (filters: TicketFilterParams) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const fetchNextPage = async () => {
|
const fetchNextPage = async () => {
|
||||||
console.log("Fetching next page...");
|
|
||||||
if (pagination.value.next) {
|
if (pagination.value.next) {
|
||||||
await store.dispatch("fetchTicketListByUrl", pagination.value.next);
|
await store.dispatch("fetchTicketListByUrl", pagination.value.next);
|
||||||
}
|
}
|
||||||
@@ -99,11 +89,18 @@ const fetchNextPage = async () => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
|
const filters: TicketFilterParams = {
|
||||||
|
byCurrentState: ["open"],
|
||||||
|
byCurrentStateEmergency: [],
|
||||||
|
byCreatedAfter: "",
|
||||||
|
byCreatedBefore: "",
|
||||||
|
byResponseTimeExceeded: "",
|
||||||
|
byAddresseeToMe: false,
|
||||||
|
};
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await store.dispatch("getCurrentUser");
|
||||||
store.dispatch("fetchTicketList"),
|
await store.dispatch("fetchTicketList", filters);
|
||||||
store.dispatch("fetchMotives"),
|
await store.dispatch("fetchMotives");
|
||||||
]);
|
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
}
|
}
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<!-- Filtre par usagé -->
|
<!-- Filtre par usagé -->
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label class="form-label">{{
|
<label class="form-label" for="personSelector">{{
|
||||||
trans(CHILL_TICKET_LIST_FILTER_PERSONS_CONCERNED)
|
trans(CHILL_TICKET_LIST_FILTER_PERSONS_CONCERNED)
|
||||||
}}</label>
|
}}</label>
|
||||||
<persons-selector
|
<persons-selector
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
:suggested="availablePersons"
|
:suggested="availablePersons"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:types="['person']"
|
:types="['person']"
|
||||||
|
id="personSelector"
|
||||||
:label="trans(CHILL_TICKET_LIST_FILTER_BY_PERSON)"
|
:label="trans(CHILL_TICKET_LIST_FILTER_BY_PERSON)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -25,12 +26,13 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<!-- Filtre par motifs -->
|
<!-- Filtre par motifs -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label class="form-label">{{
|
<label class="form-label" for="motiveSelector">{{
|
||||||
trans(CHILL_TICKET_LIST_FILTER_BY_MOTIVES)
|
trans(CHILL_TICKET_LIST_FILTER_BY_MOTIVES)
|
||||||
}}</label>
|
}}</label>
|
||||||
<motive-selector
|
<motive-selector
|
||||||
v-model="selectedMotive"
|
v-model="selectedMotive"
|
||||||
:motives="availableMotives"
|
:motives="availableMotives"
|
||||||
|
id="motiveSelector"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="mt-1" style="min-height: 2.2em">
|
<div class="mt-1" style="min-height: 2.2em">
|
||||||
@@ -54,56 +56,40 @@
|
|||||||
<!-- Filtre par état actuel -->
|
<!-- Filtre par état actuel -->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-check">
|
<div class="mb-2">
|
||||||
<input
|
<label class="form-label pe-2" for="currentState">
|
||||||
class="form-check-input"
|
{{ trans(CHILL_TICKET_LIST_FILTER_CURRENT_STATE) }}
|
||||||
type="checkbox"
|
|
||||||
value="open"
|
|
||||||
v-model="filters.byCurrentState"
|
|
||||||
id="stateOpen"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="stateOpen">{{
|
|
||||||
trans(CHILL_TICKET_LIST_FILTER_OPEN)
|
|
||||||
}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
value="closed"
|
|
||||||
v-model="filters.byCurrentState"
|
|
||||||
id="stateClosed"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="stateClosed">
|
|
||||||
{{ trans(CHILL_TICKET_LIST_FILTER_CLOSED) }}
|
|
||||||
</label>
|
</label>
|
||||||
|
<toggle-component
|
||||||
|
v-model="isClosedToggled"
|
||||||
|
:on-label="trans(CHILL_TICKET_LIST_FILTER_CLOSED)"
|
||||||
|
:off-label="trans(CHILL_TICKET_LIST_FILTER_OPEN)"
|
||||||
|
:classColor="{
|
||||||
|
on: 'bg-chill-red',
|
||||||
|
off: 'bg-chill-green',
|
||||||
|
}"
|
||||||
|
@update:model-value="handleStateToggle"
|
||||||
|
id="currentState"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Filtre par état d'urgence -->
|
<!-- Filtre par état d'urgence -->
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="form-check">
|
<div class="mb-2">
|
||||||
<input
|
<label class="form-label pe-2" for="emergency">
|
||||||
class="form-check-input"
|
{{ trans(CHILL_TICKET_LIST_FILTER_EMERGENCY) }}
|
||||||
type="checkbox"
|
</label>
|
||||||
value="yes"
|
<toggle-component
|
||||||
v-model="filters.byCurrentStateEmergency"
|
v-model="isEmergencyToggled"
|
||||||
id="emergencyYes"
|
:on-label="trans(CHILL_TICKET_LIST_FILTER_EMERGENCY)"
|
||||||
|
:off-label="trans(CHILL_TICKET_LIST_FILTER_EMERGENCY)"
|
||||||
|
:classColor="{
|
||||||
|
on: 'bg-chill-red',
|
||||||
|
off: 'bg-secondary',
|
||||||
|
}"
|
||||||
|
@update:model-value="handleEmergencyToggle"
|
||||||
|
id="emergency"
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" for="emergencyYes">{{
|
|
||||||
trans(CHILL_TICKET_LIST_FILTER_EMERGENCY)
|
|
||||||
}}</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-check">
|
|
||||||
<input
|
|
||||||
class="form-check-input"
|
|
||||||
type="checkbox"
|
|
||||||
value="no"
|
|
||||||
v-model="filters.byCurrentStateEmergency"
|
|
||||||
id="emergencyNo"
|
|
||||||
/>
|
|
||||||
<label class="form-check-label" for="emergencyNo">{{
|
|
||||||
trans(CHILL_TICKET_LIST_FILTER_NO_EMERGENCY)
|
|
||||||
}}</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -135,7 +121,7 @@
|
|||||||
<div class="d-flex gap-3">
|
<div class="d-flex gap-3">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input
|
<input
|
||||||
v-model="filters.byMyTickets"
|
v-model="filters.byAddresseeToMe"
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
id="stateMe"
|
id="stateMe"
|
||||||
@@ -159,6 +145,7 @@
|
|||||||
id="byCreatedAfter"
|
id="byCreatedAfter"
|
||||||
v-model="filters.byCreatedAfter"
|
v-model="filters.byCreatedAfter"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
|
:disabled="filters.byResponseTimeExceeded"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
@@ -205,7 +192,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch, onMounted } from "vue";
|
import { ref, computed, watch } from "vue";
|
||||||
import type { Person } from "ChillPersonAssets/types";
|
import type { Person } from "ChillPersonAssets/types";
|
||||||
import type { Motive, TicketFilterParams, TicketFilters } from "../../../types";
|
import type { Motive, TicketFilterParams, TicketFilters } from "../../../types";
|
||||||
|
|
||||||
@@ -221,7 +208,6 @@ import {
|
|||||||
CHILL_TICKET_LIST_FILTER_CLOSED,
|
CHILL_TICKET_LIST_FILTER_CLOSED,
|
||||||
CHILL_TICKET_LIST_FILTER_TO_ME,
|
CHILL_TICKET_LIST_FILTER_TO_ME,
|
||||||
CHILL_TICKET_LIST_FILTER_EMERGENCY,
|
CHILL_TICKET_LIST_FILTER_EMERGENCY,
|
||||||
CHILL_TICKET_LIST_FILTER_NO_EMERGENCY,
|
|
||||||
CHILL_TICKET_LIST_FILTER_CREATED_AFTER,
|
CHILL_TICKET_LIST_FILTER_CREATED_AFTER,
|
||||||
CHILL_TICKET_LIST_FILTER_CREATED_BEFORE,
|
CHILL_TICKET_LIST_FILTER_CREATED_BEFORE,
|
||||||
CHILL_TICKET_LIST_FILTER_RESPONSE_TIME_EXCEEDED,
|
CHILL_TICKET_LIST_FILTER_RESPONSE_TIME_EXCEEDED,
|
||||||
@@ -229,11 +215,13 @@ import {
|
|||||||
CHILL_TICKET_LIST_FILTER_RESET,
|
CHILL_TICKET_LIST_FILTER_RESET,
|
||||||
CHILL_TICKET_LIST_FILTER_APPLY,
|
CHILL_TICKET_LIST_FILTER_APPLY,
|
||||||
CHILL_TICKET_LIST_FILTER_RESULT,
|
CHILL_TICKET_LIST_FILTER_RESULT,
|
||||||
|
CHILL_TICKET_LIST_FILTER_CURRENT_STATE,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import PersonsSelector from "../../TicketApp/components/Person/PersonsSelectorComponent.vue";
|
import PersonsSelector from "../../TicketApp/components/Person/PersonsSelectorComponent.vue";
|
||||||
import MotiveSelector from "../../TicketApp/components/Motive/MotiveSelectorComponent.vue";
|
import MotiveSelector from "../../TicketApp/components/Motive/MotiveSelectorComponent.vue";
|
||||||
|
import ToggleComponent from "./ToggleComponent.vue";
|
||||||
|
|
||||||
// Props
|
// Props
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -249,12 +237,12 @@ const emit = defineEmits<{
|
|||||||
|
|
||||||
// État réactif
|
// État réactif
|
||||||
const filters = ref<TicketFilters>({
|
const filters = ref<TicketFilters>({
|
||||||
byCurrentState: [],
|
byCurrentState: ["open"],
|
||||||
byCurrentStateEmergency: [],
|
byCurrentStateEmergency: [],
|
||||||
byCreatedAfter: "",
|
byCreatedAfter: "",
|
||||||
byCreatedBefore: "",
|
byCreatedBefore: "",
|
||||||
byResponseTimeExceeded: false,
|
byResponseTimeExceeded: false,
|
||||||
byMyTickets: false,
|
byAddresseeToMe: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sélection des personnes
|
// Sélection des personnes
|
||||||
@@ -283,6 +271,27 @@ const selectedMotiveIds = computed(() =>
|
|||||||
selectedMotives.value.map((motive) => motive.id),
|
selectedMotives.value.map((motive) => motive.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Nouveaux états pour les toggles
|
||||||
|
const isClosedToggled = ref(false);
|
||||||
|
const isEmergencyToggled = ref(false);
|
||||||
|
|
||||||
|
// Méthodes pour gérer les toggles
|
||||||
|
const handleStateToggle = (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
filters.value.byCurrentState = ["closed"];
|
||||||
|
} else {
|
||||||
|
filters.value.byCurrentState = ["open"];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEmergencyToggle = (value: boolean) => {
|
||||||
|
if (value) {
|
||||||
|
filters.value.byCurrentStateEmergency = ["yes"];
|
||||||
|
} else {
|
||||||
|
filters.value.byCurrentStateEmergency = ["no"];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Méthodes
|
// Méthodes
|
||||||
const formatDateToISO = (dateString: string): string => {
|
const formatDateToISO = (dateString: string): string => {
|
||||||
if (!dateString) return dateString;
|
if (!dateString) return dateString;
|
||||||
@@ -340,8 +349,8 @@ const applyFilters = (): void => {
|
|||||||
if (filters.value.byResponseTimeExceeded) {
|
if (filters.value.byResponseTimeExceeded) {
|
||||||
apiFilters.byResponseTimeExceeded = "true";
|
apiFilters.byResponseTimeExceeded = "true";
|
||||||
}
|
}
|
||||||
if (filters.value.byMyTickets) {
|
if (filters.value.byAddresseeToMe) {
|
||||||
apiFilters.byMyTickets = true;
|
apiFilters.byAddresseeToMe = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
emit("filters-changed", apiFilters);
|
emit("filters-changed", apiFilters);
|
||||||
@@ -349,31 +358,28 @@ const applyFilters = (): void => {
|
|||||||
|
|
||||||
const resetFilters = (): void => {
|
const resetFilters = (): void => {
|
||||||
filters.value = {
|
filters.value = {
|
||||||
byCurrentState: [],
|
byCurrentState: ["open"],
|
||||||
byCurrentStateEmergency: [],
|
byCurrentStateEmergency: [],
|
||||||
byCreatedAfter: "",
|
byCreatedAfter: "",
|
||||||
byCreatedBefore: "",
|
byCreatedBefore: "",
|
||||||
byResponseTimeExceeded: false,
|
byResponseTimeExceeded: false,
|
||||||
byMyTickets: false,
|
byAddresseeToMe: false,
|
||||||
};
|
};
|
||||||
selectedPersons.value = [];
|
selectedPersons.value = [];
|
||||||
selectedMotives.value = [];
|
selectedMotives.value = [];
|
||||||
selectedMotive.value = undefined;
|
selectedMotive.value = undefined;
|
||||||
|
isClosedToggled.value = true;
|
||||||
|
isEmergencyToggled.value = false;
|
||||||
applyFilters();
|
applyFilters();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleResponseTimeExceededChange = (): void => {
|
const handleResponseTimeExceededChange = (): void => {
|
||||||
if (filters.value.byResponseTimeExceeded) {
|
if (filters.value.byResponseTimeExceeded) {
|
||||||
filters.value.byCurrentState = [];
|
|
||||||
filters.value.byCreatedBefore = "";
|
filters.value.byCreatedBefore = "";
|
||||||
|
filters.value.byCreatedAfter = "";
|
||||||
|
isClosedToggled.value = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Charger les données disponibles si nécessaire
|
|
||||||
onMounted(() => {
|
|
||||||
// Ici vous pourriez faire des appels API pour charger les personnes et motifs disponibles
|
|
||||||
// si ils ne sont pas fournis en props
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@@ -13,6 +13,15 @@
|
|||||||
<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>
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
history_line.event_type === 'add_comment' &&
|
||||||
|
history_line.data.deleted
|
||||||
|
"
|
||||||
|
class="badge bg-danger ms-2"
|
||||||
|
>
|
||||||
|
{{ trans(CHILL_TICKET_TICKET_HISTORY_DELETED) }}
|
||||||
|
</span>
|
||||||
<state-component
|
<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'"
|
||||||
@@ -24,9 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span class="badge-user">
|
<span class="badge-user">
|
||||||
<user-render-box-badge
|
<user-render-box-badge :user="history_line.by" />
|
||||||
:user="history_line.by"
|
|
||||||
></user-render-box-badge>
|
|
||||||
</span>
|
</span>
|
||||||
<span class="fst-italic mx-2">
|
<span class="fst-italic mx-2">
|
||||||
{{ formatDate(history_line.at) }}
|
{{ formatDate(history_line.at) }}
|
||||||
@@ -60,7 +67,10 @@
|
|||||||
/>
|
/>
|
||||||
<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' &&
|
||||||
|
canBeDisplayed(history_line.data)
|
||||||
|
"
|
||||||
/>
|
/>
|
||||||
<addressee-component
|
<addressee-component
|
||||||
:addressees="history_line.data.addressees"
|
:addressees="history_line.data.addressees"
|
||||||
@@ -103,6 +113,7 @@ import {
|
|||||||
CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE,
|
CHILL_TICKET_TICKET_HISTORY_STATE_CHANGE,
|
||||||
CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE,
|
CHILL_TICKET_TICKET_HISTORY_EMERGENCY_CHANGE,
|
||||||
CHILL_TICKET_TICKET_HISTORY_SET_CALLER,
|
CHILL_TICKET_TICKET_HISTORY_SET_CALLER,
|
||||||
|
CHILL_TICKET_TICKET_HISTORY_DELETED,
|
||||||
} from "translator";
|
} from "translator";
|
||||||
|
|
||||||
const props = defineProps<{ history?: TicketHistoryLine[] }>();
|
const props = defineProps<{ history?: TicketHistoryLine[] }>();
|
||||||
@@ -110,6 +121,7 @@ const history = props.history ?? [];
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
|
||||||
const actionIcons = ref(store.getters.getActionIcons);
|
const actionIcons = ref(store.getters.getActionIcons);
|
||||||
|
const canBeDisplayed = ref(store.getters["canBeDisplayed"]);
|
||||||
|
|
||||||
function explainSentence(history: TicketHistoryLine): string {
|
function explainSentence(history: TicketHistoryLine): string {
|
||||||
switch (history.event_type) {
|
switch (history.event_type) {
|
||||||
|
@@ -107,9 +107,7 @@ const emit = defineEmits<{
|
|||||||
const store = useStore();
|
const store = useStore();
|
||||||
const selectedTicketId = ref<number | null>(null);
|
const selectedTicketId = ref<number | null>(null);
|
||||||
const showTicketHistoryModal = ref(false);
|
const showTicketHistoryModal = ref(false);
|
||||||
const ticketDetails = computed(
|
const ticketDetails = computed(() => store.getters.getTicket as Ticket | null);
|
||||||
() => store.getters.getTicketDetails as Ticket | null,
|
|
||||||
);
|
|
||||||
|
|
||||||
function closeHistoryModal() {
|
function closeHistoryModal() {
|
||||||
showTicketHistoryModal.value = false;
|
showTicketHistoryModal.value = false;
|
||||||
@@ -117,7 +115,7 @@ function closeHistoryModal() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleViewTicket(ticketId: number) {
|
async function handleViewTicket(ticketId: number) {
|
||||||
await store.dispatch("fetchTicketDetails", ticketId);
|
await store.dispatch("fetchTicket", ticketId);
|
||||||
|
|
||||||
selectedTicketId.value = ticketId;
|
selectedTicketId.value = ticketId;
|
||||||
showTicketHistoryModal.value = true;
|
showTicketHistoryModal.value = true;
|
||||||
|
@@ -25,7 +25,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="wh-row">
|
<div class="wh-row">
|
||||||
<div class="wh-col">#{{ ticket.id }}</div>
|
|
||||||
<div class="wh-col" v-if="ticket.createdAt">
|
<div class="wh-col" v-if="ticket.createdAt">
|
||||||
<span
|
<span
|
||||||
v-if="ticket"
|
v-if="ticket"
|
||||||
|
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="toggle-wrapper">
|
||||||
|
<input
|
||||||
|
:id="inputId"
|
||||||
|
type="checkbox"
|
||||||
|
class="toggle-input"
|
||||||
|
:checked="modelValue"
|
||||||
|
@change="handleChange"
|
||||||
|
:disabled="disabled"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
:for="inputId"
|
||||||
|
class="toggle-label"
|
||||||
|
:class="[
|
||||||
|
modelValue
|
||||||
|
? classColor?.on || 'bg-chill-green'
|
||||||
|
: classColor?.off || 'bg-chill-red',
|
||||||
|
]"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: classColor
|
||||||
|
? undefined
|
||||||
|
: !modelValue
|
||||||
|
? colorOff
|
||||||
|
: colorOn,
|
||||||
|
height: '28px',
|
||||||
|
width: toggleWidth + 'px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="toggle-slider"
|
||||||
|
:style="{
|
||||||
|
transform: modelValue ? `translateX(${toggleWidth - 26}px)` : 'none',
|
||||||
|
width: '24px',
|
||||||
|
height: '24px',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="toggle-text"
|
||||||
|
:style="{
|
||||||
|
color: 'white',
|
||||||
|
marginLeft: !modelValue ? '28px' : '0',
|
||||||
|
paddingLeft: modelValue ? '6px' : '0',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ modelValue ? onLabel : offLabel }}
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean;
|
||||||
|
onLabel?: string;
|
||||||
|
offLabel?: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
id?: string;
|
||||||
|
colorOn?: string;
|
||||||
|
colorOff?: string;
|
||||||
|
classColor?: {
|
||||||
|
on: string;
|
||||||
|
off: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
type Emits = (e: "update:modelValue", value: boolean) => void;
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
onLabel: "ON",
|
||||||
|
offLabel: "OFF",
|
||||||
|
disabled: false,
|
||||||
|
id: undefined,
|
||||||
|
colorOn: "#4caf50",
|
||||||
|
colorOff: "#ccc",
|
||||||
|
classColor: () => ({
|
||||||
|
on: "bg-chill-green",
|
||||||
|
off: "bg-chill-red",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>();
|
||||||
|
|
||||||
|
const inputId = computed(
|
||||||
|
() => props.id || `toggle-${Math.random().toString(36).substr(2, 9)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calcule la largeur du toggle basée sur le label le plus long
|
||||||
|
const toggleWidth = computed(() => {
|
||||||
|
const onLength = props.onLabel.length;
|
||||||
|
const offLength = props.offLabel.length;
|
||||||
|
const maxLength = Math.max(onLength, offLength);
|
||||||
|
|
||||||
|
// Largeur minimale: 56px, puis 7px par caractère supplémentaire au-delà de 3 caractères
|
||||||
|
const baseWidth = 56;
|
||||||
|
const extraWidth = Math.max(0, maxLength - 3) * 7;
|
||||||
|
|
||||||
|
return baseWidth + extraWidth;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleChange = (event: Event) => {
|
||||||
|
const target = event.target as HTMLInputElement;
|
||||||
|
emit("update:modelValue", target.checked);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.toggle-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-label {
|
||||||
|
display: block;
|
||||||
|
border-radius: 14px;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-label:hover {
|
||||||
|
filter: brightness(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-input:disabled + .toggle-label {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-slider {
|
||||||
|
position: absolute;
|
||||||
|
top: 2px;
|
||||||
|
left: 2px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-text {
|
||||||
|
font-size: 9px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -1,6 +1,8 @@
|
|||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import { store } from "../TicketApp/store";
|
import { store } from "../TicketApp/store";
|
||||||
|
import VueToast from "vue-toast-notification";
|
||||||
|
import "vue-toast-notification/dist/theme-sugar.css";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@@ -12,4 +14,4 @@ const _app = createApp({
|
|||||||
template: "<app></app>",
|
template: "<app></app>",
|
||||||
});
|
});
|
||||||
|
|
||||||
_app.component("app", App).use(store).mount("#ticketList");
|
_app.component("app", App).use(store).use(VueToast).mount("#ticketList");
|
||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
restore: Restaurer
|
||||||
chill_ticket:
|
chill_ticket:
|
||||||
list:
|
list:
|
||||||
title: Tickets
|
title: Tickets
|
||||||
@@ -5,6 +6,7 @@ chill_ticket:
|
|||||||
no_tickets: "Aucun ticket"
|
no_tickets: "Aucun ticket"
|
||||||
loading_ticket: "Chargement des tickets..."
|
loading_ticket: "Chargement des tickets..."
|
||||||
loading_ticket_details: "Chargement de l'historique du ticket..."
|
loading_ticket_details: "Chargement de l'historique du ticket..."
|
||||||
|
error_loading_ticket: "Erreur lors du chargement des ticket"
|
||||||
load_more: "Voir plus..."
|
load_more: "Voir plus..."
|
||||||
addressees: "Attribué à"
|
addressees: "Attribué à"
|
||||||
persons: "Usager concernés"
|
persons: "Usager concernés"
|
||||||
@@ -15,7 +17,7 @@ chill_ticket:
|
|||||||
created_between: Créés entre
|
created_between: Créés entre
|
||||||
state_change: État actuel
|
state_change: État actuel
|
||||||
title: "Filtres des tickets"
|
title: "Filtres des tickets"
|
||||||
persons_concerned: "Usager concernés"
|
persons_concerned: "Usagers concernés"
|
||||||
by_person: "Par personne"
|
by_person: "Par personne"
|
||||||
by_motives: "Par motifs"
|
by_motives: "Par motifs"
|
||||||
current_state: "État actuel"
|
current_state: "État actuel"
|
||||||
@@ -26,13 +28,19 @@ chill_ticket:
|
|||||||
created_after: "Créé après"
|
created_after: "Créé après"
|
||||||
created_before: "Créé avant"
|
created_before: "Créé avant"
|
||||||
response_time_exceeded: "Temps de réponse dépassé"
|
response_time_exceeded: "Temps de réponse dépassé"
|
||||||
response_time_warning: 'Attention : Ce filtre supprime automatiquement les filtres "État actuel" et "Créé avant"'
|
response_time_warning: 'Attention : ce filtre désactive les filtres "par dates" et affiche uniquement les tickets ouverts'
|
||||||
reset: "Réinitialiser"
|
reset: "Réinitialiser"
|
||||||
apply: "Appliquer les filtres"
|
apply: "Appliquer les filtres"
|
||||||
remove: "Supprimer"
|
remove: "Supprimer"
|
||||||
result: "{count, plural, =0 {Aucun résultat} =1 {resultat} other {resultats}}"
|
result: "{count, plural, =0 {Aucun résultat} =1 {resultat} other {resultats}}"
|
||||||
|
|
||||||
ticket:
|
ticket:
|
||||||
|
init_form:
|
||||||
|
title: "Ajouter des informations sur le ticket"
|
||||||
|
submit: "Mettre à jour le ticket"
|
||||||
|
reset: "Réinitialiser"
|
||||||
|
success: "Ticket mis à jour avec succès"
|
||||||
|
error: "Veuillez remplir tous les champs obligatoires"
|
||||||
history:
|
history:
|
||||||
add_comment: "Nouveau commentaire"
|
add_comment: "Nouveau commentaire"
|
||||||
addressees_state: "Attributions"
|
addressees_state: "Attributions"
|
||||||
@@ -42,6 +50,7 @@ chill_ticket:
|
|||||||
create_ticket: "Ticket créé"
|
create_ticket: "Ticket créé"
|
||||||
state_change: ""
|
state_change: ""
|
||||||
emergency_change: ""
|
emergency_change: ""
|
||||||
|
deleted: "Supprimé"
|
||||||
previous_tickets: "Précédents tickets"
|
previous_tickets: "Précédents tickets"
|
||||||
actions_toolbar:
|
actions_toolbar:
|
||||||
cancel: "Annuler"
|
cancel: "Annuler"
|
||||||
@@ -52,6 +61,13 @@ chill_ticket:
|
|||||||
reopen: "Rouvrir"
|
reopen: "Rouvrir"
|
||||||
reopen_success: "Rouverture du ticket réussie"
|
reopen_success: "Rouverture du ticket réussie"
|
||||||
reopen_error: "Erreur lors de la rouverture du ticket"
|
reopen_error: "Erreur lors de la rouverture du ticket"
|
||||||
|
restore_comment:
|
||||||
|
success: "Commentaire restauré"
|
||||||
|
delete_comment:
|
||||||
|
success: "Commentaire supprimé"
|
||||||
|
edit_comment:
|
||||||
|
title: "Éditer le commentaire"
|
||||||
|
success: "Commentaire modifié"
|
||||||
add_comment:
|
add_comment:
|
||||||
title: "Commentaire"
|
title: "Commentaire"
|
||||||
label: "Ajouter un commentaire"
|
label: "Ajouter un commentaire"
|
||||||
@@ -78,8 +94,8 @@ 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:
|
||||||
person: "{count, plural, =0 {Aucun usager concerné} =1 {Usager concerné} other {Usagers concernés}}"
|
person: "{count, plural, =0 {Aucun usager impliqué} =1 {Usager impliqué} other {Usagers impliqués}}"
|
||||||
speaker: "{count, plural, =0 {Aucun intervenant} =1 {Attribué à} other {Attribués à}}"
|
speaker: "{count, plural, =0 {Aucun intervenant} =1 {Intervenant attribué} other {Intervenants attribués}}"
|
||||||
caller: "{count, plural, =0 {Aucun appelant} =1 {Appelant} other {Appelants}}"
|
caller: "{count, plural, =0 {Aucun appelant} =1 {Appelant} other {Appelants}}"
|
||||||
open: "Ouvert"
|
open: "Ouvert"
|
||||||
closed: "Fermé"
|
closed: "Fermé"
|
||||||
@@ -87,27 +103,40 @@ chill_ticket:
|
|||||||
and: "et"
|
and: "et"
|
||||||
days: >-
|
days: >-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {aucun jour}
|
=0 {aucun jour}
|
||||||
=1 {1 jour}
|
=1 {1 jour}
|
||||||
other {# jours}
|
other {# jours}
|
||||||
}
|
}
|
||||||
hours: >-
|
hours: >-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {aucune heure}
|
=0 {aucune heure}
|
||||||
=1 {1 heure}
|
=1 {1 heure}
|
||||||
other {# heures}
|
other {# heures}
|
||||||
}
|
}
|
||||||
minutes: >-
|
minutes: >-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {aucune minute}
|
=0 {aucune minute}
|
||||||
=1 {1 minute}
|
=1 {1 minute}
|
||||||
other {# minutes}
|
other {# minutes}
|
||||||
}
|
}
|
||||||
seconds: >-
|
seconds: >-
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {aucune seconde}
|
=0 {aucune seconde}
|
||||||
=1 {1 seconde}
|
=1 {1 seconde}
|
||||||
other {# secondes}
|
other {# secondes}
|
||||||
}
|
}
|
||||||
no_motive: "Pas de motif"
|
no_motive: "Aucun motif"
|
||||||
emergency: "URGENT"
|
emergency: "URGENT"
|
||||||
|
emergency_success: "Ticket marqué comme urgent"
|
||||||
|
emergency_error: "Erreur lors de la tentative de marquage du ticket comme urgent"
|
||||||
|
no_emergency_success: "Ticket marqué comme non urgent"
|
||||||
|
no_emergency_error: "Erreur lors de la tentative de marquage du ticket comme non urgent"
|
||||||
|
peloton:
|
||||||
|
loading: "Chargement..."
|
||||||
|
loading_document: "Chargement du document..."
|
||||||
|
error_loading: "Erreur lors du chargement du document."
|
||||||
|
error_not_ready: "Le document n'est pas prêt ou accessible."
|
||||||
|
unsupported_type: "Type de document non supporté pour l'affichage."
|
||||||
|
open_new_tab: "Ouvrir dans un nouvel onglet"
|
||||||
|
iframe_not_supported: "Votre navigateur ne supporte pas les iframes."
|
||||||
|
click_to_open_pdf: "Cliquez ici pour ouvrir le PDF"
|
||||||
|
Reference in New Issue
Block a user