Merge branch '71-fix-bug-add-urgent-on-init-modal-add-config-homepage' into 'ticket-app-master'

Correction de bugs, ajout champs urgents dans la modal d'initialisation du ticket et ajout d'un configuration pour l'affichage des tabs dans la homepage

See merge request Chill-Projet/chill-bundles!884
This commit is contained in:
2025-09-22 09:23:30 +00:00
19 changed files with 242 additions and 141 deletions

View File

@@ -94,7 +94,7 @@ class StoredObject implements Document, TrackCreationInterface
/** /**
* @var Collection<int, StoredObjectVersion>&Selectable<int, StoredObjectVersion> * @var Collection<int, StoredObjectVersion>&Selectable<int, StoredObjectVersion>
*/ */
#[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true)] #[ORM\OneToMany(mappedBy: 'storedObject', targetEntity: StoredObjectVersion::class, cascade: ['persist'], orphanRemoval: true, fetch: 'EAGER')]
private Collection&Selectable $versions; private Collection&Selectable $versions;
/** /**

View File

@@ -2,5 +2,11 @@ import App from "ChillMainAssets/vuejs/HomepageWidget/App.vue";
import { createApp } from "vue"; import { createApp } from "vue";
import { store } from "ChillMainAssets/vuejs/HomepageWidget/store"; import { store } from "ChillMainAssets/vuejs/HomepageWidget/store";
declare global {
interface Window {
homepage_config: string;
}
}
const _app = createApp(App); const _app = createApp(App);
_app.use(store).mount("#homepage_widget"); _app.use(store).mount("#homepage_widget");

View File

@@ -278,3 +278,25 @@ export interface addNewEntities {
selected: Selected[]; selected: Selected[];
modal: Modal; modal: Modal;
} }
export enum HomepageTabs {
MyCustoms,
MyTickets,
MyNotifications,
MyAccompanyingCourses,
MyEvaluations,
MyTasks,
MyWorkflows,
}
export interface HomepageConfig {
defaultTab: HomepageTabs;
displayTabs: HomepageTabs[];
}
export interface TabDefinition {
key: HomepageTabs;
label: string;
icon: string | null;
counter: () => number;
}

View File

@@ -1,83 +1,15 @@
<template> <template>
<h2>{{ trans(MAIN_TITLE) }}</h2> <h2>{{ trans(MAIN_TITLE) }}</h2>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="nav-item"> <li v-for="tab in displayedTabs" :key="tab.key" class="nav-item">
<a <a
class="nav-link" class="nav-link"
:class="{ active: activeTab === 'MyCustoms' }" :class="{ active: activeTab === tab.key }"
@click="selectTab('MyCustoms')" @click="selectTab(tab.key)"
> >
<i class="fa fa-dashboard" /> <i v-if="tab.icon" :class="tab.icon" />
</a> <span v-else>{{ tab.label }}</span>
</li> <tab-counter v-if="tab.counter" :count="tab.counter()" />
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyTickets' }"
@click="selectTab('MyTickets')"
>
{{ trans(MY_TICKETS_TAB) }}
<tab-counter :count="ticketListState.value?.count || 0" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyNotifications' }"
@click="selectTab('MyNotifications')"
>
{{ trans(MY_NOTIFICATIONS_TAB) }}
<tab-counter :count="state.value?.notifications?.count || 0" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyAccompanyingCourses' }"
@click="selectTab('MyAccompanyingCourses')"
>
{{ trans(MY_ACCOMPANYING_COURSES_TAB) }}
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyEvaluations' }"
@click="selectTab('MyEvaluations')"
>
{{ trans(MY_EVALUATIONS_TAB) }}
<tab-counter :count="state.value?.evaluations?.count || 0" />
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyTasks' }"
@click="selectTab('MyTasks')"
>
{{ trans(MY_TASKS_TAB) }}
<tab-counter
:count="
(state.value?.tasks?.warning?.count || 0) +
(state.value?.tasks?.alert?.count || 0)
"
/>
</a>
</li>
<li class="nav-item">
<a
class="nav-link"
:class="{ active: activeTab === 'MyWorkflows' }"
@click="selectTab('MyWorkflows')"
>
{{ trans(MY_WORKFLOWS_TAB) }}
<tab-counter
:count="
(state.value?.workflows?.count || 0) +
(state.value?.workflowsCc?.count || 0)
"
/>
</a> </a>
</li> </li>
<li class="nav-item loading ms-auto py-2" v-if="loading"> <li class="nav-item loading ms-auto py-2" v-if="loading">
@@ -87,26 +19,25 @@
/> />
</li> </li>
</ul> </ul>
<div class="my-4"> <div class="my-4">
<my-tickets v-if="activeTab == 'MyTickets'" /> <MyCustoms v-if="activeTab === HomepageTabs.MyCustoms" />
<my-customs v-if="activeTab === 'MyCustoms'" /> <MyTickets v-else-if="activeTab === HomepageTabs.MyTickets" />
<my-works v-else-if="activeTab === 'MyWorks'" /> <MyNotifications v-else-if="activeTab === HomepageTabs.MyNotifications" />
<my-evaluations v-else-if="activeTab === 'MyEvaluations'" /> <MyAccompanyingCourses
<my-tasks v-else-if="activeTab === 'MyTasks'" /> v-else-if="activeTab === HomepageTabs.MyAccompanyingCourses"
<my-accompanying-courses
v-else-if="activeTab === 'MyAccompanyingCourses'"
/> />
<my-notifications v-else-if="activeTab === 'MyNotifications'" /> <MyEvaluations v-else-if="activeTab === HomepageTabs.MyEvaluations" />
<my-workflows v-else-if="activeTab === 'MyWorkflows'" /> <MyTasks v-else-if="activeTab === HomepageTabs.MyTasks" />
<MyWorkflows v-else-if="activeTab === HomepageTabs.MyWorkflows" />
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, computed, onMounted } from "vue"; import { ref, computed, onMounted } from "vue";
import { useStore } from "vuex"; import { useStore } from "vuex";
// Components
import MyCustoms from "./MyCustoms.vue"; import MyCustoms from "./MyCustoms.vue";
import MyWorks from "./MyWorks.vue";
import MyEvaluations from "./MyEvaluations.vue"; import MyEvaluations from "./MyEvaluations.vue";
import MyTasks from "./MyTasks.vue"; import MyTasks from "./MyTasks.vue";
import MyAccompanyingCourses from "./MyAccompanyingCourses.vue"; import MyAccompanyingCourses from "./MyAccompanyingCourses.vue";
@@ -114,6 +45,8 @@ import MyNotifications from "./MyNotifications.vue";
import MyWorkflows from "./MyWorkflows.vue"; import MyWorkflows from "./MyWorkflows.vue";
import MyTickets from "./MyTickets.vue"; import MyTickets from "./MyTickets.vue";
import TabCounter from "./TabCounter.vue"; import TabCounter from "./TabCounter.vue";
// Translations
import { import {
MAIN_TITLE, MAIN_TITLE,
MY_TICKETS_TAB, MY_TICKETS_TAB,
@@ -125,33 +58,97 @@ import {
LOADING, LOADING,
trans, trans,
} from "translator"; } from "translator";
// Types
import {
HomepageConfig,
HomepageTabs,
TabDefinition,
} from "ChillMainAssets/types";
const store = useStore(); const store = useStore();
const activeTab = ref("MyCustoms"); defineExpose({ HomepageTabs });
const loading = computed(() => store.state.loading);
const homepageConfig = ref<HomepageConfig>(JSON.parse(window.homepage_config));
const state = computed(() => store.state.homepage); const state = computed(() => store.state.homepage);
const ticketListState = computed(() => store.state.ticketList); const ticketListState = computed(() => store.state.ticketList);
function selectTab(tab: string) { const tabDefinitions: TabDefinition[] = [
if (tab !== "MyCustoms") { {
key: HomepageTabs.MyCustoms,
label: "",
icon: "fa fa-dashboard",
counter: () => 0,
},
{
key: HomepageTabs.MyTickets,
label: trans(MY_TICKETS_TAB),
icon: null,
counter: () => ticketListState.value?.count,
},
{
key: HomepageTabs.MyNotifications,
label: trans(MY_NOTIFICATIONS_TAB),
icon: null,
counter: () => state.value?.notifications?.count,
},
{
key: HomepageTabs.MyAccompanyingCourses,
label: trans(MY_ACCOMPANYING_COURSES_TAB),
icon: null,
counter: () => state.value?.accompanyingCourses?.count,
},
{
key: HomepageTabs.MyEvaluations,
label: trans(MY_EVALUATIONS_TAB),
icon: null,
counter: () => state.value?.evaluations?.count,
},
{
key: HomepageTabs.MyTasks,
label: trans(MY_TASKS_TAB),
icon: null,
counter: () =>
state.value?.tasks?.warning?.count + state.value?.tasks?.alert?.count,
},
{
key: HomepageTabs.MyWorkflows,
label: trans(MY_WORKFLOWS_TAB),
icon: null,
counter: () =>
state.value?.workflows?.count + state.value?.workflowsCc?.count,
},
];
const displayedTabs = computed(() => {
// Always show MyCustoms first if present
const tabs = [] as TabDefinition[];
for (const tabEnum of homepageConfig.value.displayTabs) {
const def = tabDefinitions.find(
(t) => t.key === Number(HomepageTabs[tabEnum]),
);
if (def) tabs.push(def);
}
return tabs.filter(Boolean);
});
const activeTab = ref(Number(HomepageTabs[homepageConfig.value.defaultTab]));
const loading = computed(() => store.state.loading);
function selectTab(tab: HomepageTabs) {
if (tab !== HomepageTabs.MyCustoms) {
store.dispatch("getByTab", { tab: tab }); store.dispatch("getByTab", { tab: tab });
} }
activeTab.value = tab; activeTab.value = tab;
} }
onMounted(() => { onMounted(() => {
for (const m of [ for (const tab of displayedTabs.value) {
"MyTickets", if (tab.key !== HomepageTabs.MyCustoms) {
"MyNotifications", store.dispatch("getByTab", { tab: tab.key, param: "countOnly=1" });
"MyAccompanyingCourses", }
// 'MyWorks',
"MyEvaluations",
"MyTasks",
"MyWorkflows",
]) {
store.dispatch("getByTab", { tab: m, param: "countOnly=1" });
} }
}); });
</script> </script>

View File

@@ -1,5 +1,5 @@
<template> <template>
<span v-if="isCounterAvailable" class="badge rounded-pill bg-danger"> <span v-if="isCounterAvailable" class="badge rounded-pill bg-danger mx-2">
{{ count }} {{ count }}
</span> </span>
</template> </template>

View File

@@ -13,6 +13,7 @@ import {
WorflowCc, WorflowCc,
} from "ChillPersonAssets/types"; } from "ChillPersonAssets/types";
import { RootState } from ".."; import { RootState } from "..";
import { HomepageTabs } from "ChillMainAssets/types";
export interface TasksState { export interface TasksState {
warning: PaginationResponse<Warning>; warning: PaginationResponse<Warning>;
@@ -82,17 +83,6 @@ export const moduleHomepage: Module<State, RootState> = {
isWorkflowsLoaded(state) { isWorkflowsLoaded(state) {
return Array.isArray(state.workflows.results); return Array.isArray(state.workflows.results);
}, },
counter(state, getters, rootState, rootGetters) {
return {
tickets: rootGetters["ticketList/getCount"] || 0,
evaluations: state.evaluations.count || 0,
tasksWarning: state.tasks.warning.count || 0,
tasksAlert: state.tasks.alert.count || 0,
accompanyingCourses: state.accompanyingCourses.count || 0,
notifications: state.notifications.count || 0,
workflows: state.workflows.count || 0,
};
},
}, },
mutations: { mutations: {
addEvaluations(state, evaluations) { addEvaluations(state, evaluations) {
@@ -129,20 +119,20 @@ export const moduleHomepage: Module<State, RootState> = {
actions: { actions: {
async getByTab({ commit, getters, dispatch }, { tab, param }) { async getByTab({ commit, getters, dispatch }, { tab, param }) {
switch (tab) { switch (tab) {
case "MyTickets": case HomepageTabs.MyTickets:
if (!getters.isTicketsLoaded) { if (!getters.isTicketsLoaded) {
commit("setTicketsLoading", true); commit("setTicketsLoading", true);
// Utilise l'action du module ticket_list // Utilise l'action du module ticket_list
await dispatch( await dispatch(
"fetchTicketList", "fetchTicketList",
{ byAddresseeToMe: true }, { byAddresseeToMe: true, byCurrentState: "open" },
{ root: true }, { root: true },
); );
commit("setTicketsLoading", false); commit("setTicketsLoading", false);
} }
break; break;
case "MyEvaluations": case HomepageTabs.MyEvaluations:
if (!getters.isEvaluationsLoaded) { if (!getters.isEvaluationsLoaded) {
commit("setLoading", true); commit("setLoading", true);
const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${"?" + param}`; const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${"?" + param}`;
@@ -157,7 +147,7 @@ export const moduleHomepage: Module<State, RootState> = {
}); });
} }
break; break;
case "MyTasks": case HomepageTabs.MyTasks:
if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) { if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) {
commit("setLoading", true); commit("setLoading", true);
const urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${"&" + param}`, const urlWarning = `/api/1.0/task/single-task/list/my?f[q]=&f[checkboxes][status][]=warning&f[checkboxes][states][]=new&f[checkboxes][states][]=in_progress${"&" + param}`,
@@ -182,7 +172,7 @@ export const moduleHomepage: Module<State, RootState> = {
}); });
} }
break; break;
case "MyAccompanyingCourses": case HomepageTabs.MyAccompanyingCourses:
if (!getters.isAccompanyingCoursesLoaded) { if (!getters.isAccompanyingCoursesLoaded) {
commit("setLoading", true); commit("setLoading", true);
const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${"?" + param}`; const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${"?" + param}`;
@@ -197,7 +187,7 @@ export const moduleHomepage: Module<State, RootState> = {
}); });
} }
break; break;
case "MyNotifications": case HomepageTabs.MyNotifications:
if (!getters.isNotificationsLoaded) { if (!getters.isNotificationsLoaded) {
commit("setLoading", true); commit("setLoading", true);
const url = `/api/1.0/main/notification/my/unread${"?" + param}`; const url = `/api/1.0/main/notification/my/unread${"?" + param}`;
@@ -212,7 +202,7 @@ export const moduleHomepage: Module<State, RootState> = {
}); });
} }
break; break;
case "MyWorkflows": case HomepageTabs.MyWorkflows:
if (!getters.isWorflowsLoaded) { if (!getters.isWorflowsLoaded) {
commit("setLoading", true); commit("setLoading", true);
makeFetch("GET", "/api/1.0/main/workflow/my") makeFetch("GET", "/api/1.0/main/workflow/my")

View File

@@ -11,5 +11,13 @@
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script type="text/javascript">
window.homepage_config = JSON.stringify({
defaultTab: 'MyTickets',
displayTabs: ['MyCustoms', 'MyTickets', 'MyNotifications']
});
</script>
{{ encore_entry_script_tags('page_homepage_widget') }} {{ encore_entry_script_tags('page_homepage_widget') }}
{% endblock %} {% endblock %}

View File

@@ -11,6 +11,7 @@ declare(strict_types=1);
namespace Chill\TicketBundle\Controller; namespace Chill\TicketBundle\Controller;
use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer;
use Chill\MainBundle\CRUD\Controller\ApiController; use Chill\MainBundle\CRUD\Controller\ApiController;
use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\QueryBuilder;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
@@ -22,4 +23,12 @@ final class MotiveApiController extends ApiController
/* @var $query QueryBuilder */ /* @var $query QueryBuilder */
$query->andWhere('e.active = TRUE'); $query->andWhere('e.active = TRUE');
} }
protected function getContextForSerialization(string $action, Request $request, string $_format, $entity): array
{
return match ($request->getMethod()) {
Request::METHOD_GET => ['groups' => ['read', StoredObjectNormalizer::DOWNLOAD_LINK_ONLY]],
default => parent::getContextForSerialization($action, $request, $_format, $entity),
};
}
} }

View File

@@ -44,7 +44,7 @@ class Motive
/** /**
* @var Collection<int, StoredObject> * @var Collection<int, StoredObject>
*/ */
#[ORM\ManyToMany(targetEntity: StoredObject::class)] #[ORM\ManyToMany(targetEntity: StoredObject::class, fetch: 'EAGER')]
#[ORM\JoinTable(name: 'motive_stored_objects', schema: 'chill_ticket')] #[ORM\JoinTable(name: 'motive_stored_objects', schema: 'chill_ticket')]
private Collection $storedObjects; private Collection $storedObjects;

View File

@@ -186,6 +186,8 @@ export interface TicketFilterParams {
export interface TicketInitForm { export interface TicketInitForm {
content: string; content: string;
motive?: Motive; motive?: Motive;
addressees: UserGroupOrUser[];
persons: Person[]; persons: Person[];
caller: Person | null; caller: Person | null;
emergency: TicketEmergencyState;
} }

View File

@@ -87,12 +87,16 @@ async function handleFormSubmit(ticketForm: TicketInitForm) {
toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING)); toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING));
return; return;
} }
if (ticketForm.content && ticketForm.content.trim() !== "") { if (ticketForm.content && ticketForm.content.trim() !== "") {
await store.dispatch("createComment", ticketForm.content); await store.dispatch("createComment", ticketForm.content);
} else { } else {
toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING)); toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING));
return; return;
} }
await store.dispatch("setEmergency", ticketForm.emergency);
await store.dispatch("setAddressees", ticketForm.addressees);
await store.dispatch("setPersons", ticketForm.persons); await store.dispatch("setPersons", ticketForm.persons);
// Forcer le rafraîchissement des composants // Forcer le rafraîchissement des composants
@@ -111,13 +115,13 @@ function closeModal() {
showTicketInitFormModal.value = false; showTicketInitFormModal.value = false;
} }
onMounted(async () => { onMounted(() => {
try { try {
await store.dispatch("getCurrentUser"); store.dispatch("getCurrentUser");
await store.dispatch("fetchMotives"); store.dispatch("fetchMotives");
await store.dispatch("fetchUserGroups"); store.dispatch("fetchUserGroups");
await store.dispatch("fetchUsers"); store.dispatch("fetchUsers");
await store.dispatch("getSuggestedPersons"); store.dispatch("getSuggestedPersons");
showTicketInitFormModal.value = store.getters.isIncompleteTicket; showTicketInitFormModal.value = store.getters.isIncompleteTicket;
} catch (error) { } catch (error) {
toast.error(error as string); toast.error(error as string);

View File

@@ -41,6 +41,7 @@
<motive-selector-component <motive-selector-component
v-model="motive" v-model="motive"
:motives="motives" :motives="motives"
open-direction="top"
v-if="activeTab === 'set_motive'" v-if="activeTab === 'set_motive'"
/> />
<div v-if="activeTab === 'persons_state'"> <div v-if="activeTab === 'persons_state'">

View File

@@ -46,10 +46,12 @@ const suggestedValues = ref<Entities[]>([...props.suggested]);
watch( watch(
() => [props.suggested, props.modelValue], () => [props.suggested, props.modelValue],
() => { () => {
const modelValue = props.modelValue ?? []; // Mise à jour des entités sélectionnées
selectedEntities.value = [...(props.modelValue as Entities[])];
// Filtrage des suggestions
suggestedValues.value = props.suggested.filter((suggested: Entities) => { suggestedValues.value = props.suggested.filter((suggested: Entities) => {
return !modelValue.some((selected: Entities) => { return !props.modelValue.some((selected: Entities) => {
if (suggested.type == "user_group" && selected.type == "user_group") { if (suggested.type == "user_group" && selected.type == "user_group") {
switch (selected.excludeKey) { switch (selected.excludeKey) {
case "level": case "level":

View File

@@ -9,6 +9,7 @@
}" }"
@click="toggleEmergency" @click="toggleEmergency"
:disabled="props.disabled" :disabled="props.disabled"
type="button"
> >
{{ trans(CHILL_TICKET_TICKET_BANNER_EMERGENCY) }} {{ trans(CHILL_TICKET_TICKET_BANNER_EMERGENCY) }}
</button> </button>

View File

@@ -8,7 +8,7 @@
label="label" label="label"
:custom-label="customLabel" :custom-label="customLabel"
track-by="id" track-by="id"
open-direction="top" :open-direction="openDirection"
:multiple="false" :multiple="false"
:searchable="true" :searchable="true"
:placeholder="trans(CHILL_TICKET_TICKET_SET_MOTIVE_LABEL)" :placeholder="trans(CHILL_TICKET_TICKET_SET_MOTIVE_LABEL)"
@@ -46,10 +46,20 @@ import {
// Component // Component
import PelotonComponent from "../PelotonComponent.vue"; import PelotonComponent from "../PelotonComponent.vue";
const props = defineProps<{ const props = defineProps({
modelValue?: Motive; modelValue: {
motives: Motive[]; type: Object as () => Motive | undefined,
}>(); default: undefined,
},
motives: {
type: Array as () => Motive[],
default: () => [],
},
openDirection: {
type: String,
default: "bottom",
},
});
const emit = const emit =
defineEmits<(e: "update:modelValue", value: Motive | undefined) => void>(); defineEmits<(e: "update:modelValue", value: Motive | undefined) => void>();

View File

@@ -84,7 +84,7 @@ const currentPersons = computed(
() => store.getters.getCurrentPersons as Person[], () => store.getters.getCurrentPersons as Person[],
); );
const previousTickets = computed( const previousTickets = computed(
() => store.getters.getTicketList as TicketSimple[], () => store.getters.getPreviousTicketList as TicketSimple[],
); );
onMounted(async () => { onMounted(async () => {

View File

@@ -12,7 +12,16 @@
:motives="motives" :motives="motives"
/> />
</div> </div>
<!-- Attribution des tickets -->
<div class="mb-3">
<label class="form-label">
{{ trans(CHILL_TICKET_TICKET_ADD_COMMENT_TITLE) }}
</label>
<addressee-selector-component
v-model="ticketForm.addressees"
:suggested="userGroups"
/>
</div>
<!-- Sélection des personnes --> <!-- Sélection des personnes -->
<div class="row mb-3"> <div class="row mb-3">
<div class="col-md-6"> <div class="col-md-6">
@@ -51,6 +60,22 @@
:motive="ticketForm.motive" :motive="ticketForm.motive"
/> />
</div> </div>
<div class="mb-3">
<label class="form-label pe-2" for="emergency">
{{ trans(CHILL_TICKET_LIST_FILTER_EMERGENCY) }}
</label>
<toggle-component
v-model="isEmergency"
:on-label="trans(CHILL_TICKET_LIST_FILTER_EMERGENCY)"
:off-label="trans(CHILL_TICKET_LIST_FILTER_EMERGENCY)"
:classColor="{
on: 'bg-warning',
off: 'bg-secondary',
}"
id="emergency"
class="float-end"
/>
</div>
<!-- Boutons d'action --> <!-- Boutons d'action -->
<div class="d-flex justify-content-end gap-2 mt-4"> <div class="d-flex justify-content-end gap-2 mt-4">
@@ -67,16 +92,23 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, watch } from "vue"; import { computed, reactive, ref, watch } from "vue";
import { useStore } from "vuex"; import { useStore } from "vuex";
// Components // Components
import MotiveSelectorComponent from "./Motive/MotiveSelectorComponent.vue"; import MotiveSelectorComponent from "./Motive/MotiveSelectorComponent.vue";
import CommentEditorComponent from "./Comment/CommentEditorComponent.vue"; import CommentEditorComponent from "./Comment/CommentEditorComponent.vue";
import PersonsSelectorComponent from "./Person/PersonsSelectorComponent.vue"; import PersonsSelectorComponent from "./Person/PersonsSelectorComponent.vue";
import AddresseeSelectorComponent from "./Addressee/AddresseeSelectorComponent.vue";
import ToggleComponent from "../../TicketList/components/ToggleComponent.vue";
// Types // Types
import { Motive, Ticket, TicketInitForm } from "../../../types"; import {
Motive,
Ticket,
TicketEmergencyState,
TicketInitForm,
} from "../../../types";
import { Person } from "ChillPersonAssets/types"; import { Person } from "ChillPersonAssets/types";
// Translations // Translations
@@ -90,7 +122,9 @@ import {
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_PERSON, CHILL_TICKET_TICKET_SET_PERSONS_TITLE_PERSON,
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL, CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL, CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL,
CHILL_TICKET_LIST_FILTER_EMERGENCY,
} from "translator"; } from "translator";
import { UserGroup, UserGroupOrUser } from "ChillMainAssets/types";
const props = defineProps<{ const props = defineProps<{
ticket: Ticket; ticket: Ticket;
@@ -107,11 +141,19 @@ const store = useStore();
const ticketForm = reactive({ const ticketForm = reactive({
content: "", content: "",
addressees: props.ticket.currentAddressees as UserGroupOrUser[],
motive: props.ticket.currentMotive as Motive | null, motive: props.ticket.currentMotive as Motive | null,
persons: props.ticket.currentPersons as Person[], persons: props.ticket.currentPersons as Person[],
caller: props.ticket.caller as Person | null, caller: props.ticket.caller as Person | null,
emergency: props.ticket.emergency as TicketEmergencyState,
} as TicketInitForm); } as TicketInitForm);
const isEmergency = ref<boolean>(
props.ticket.emergency == "yes" ? true : false,
);
const userGroups = computed(() => store.getters.getUserGroups as UserGroup[]);
watch( watch(
() => ticketForm.caller, () => ticketForm.caller,
async (newCaller) => { async (newCaller) => {
@@ -124,16 +166,18 @@ function submitForm() {
emit("submit", { emit("submit", {
content: ticketForm.content, content: ticketForm.content,
motive: ticketForm.motive, motive: ticketForm.motive,
addressees: [...ticketForm.addressees],
persons: [...ticketForm.persons], persons: [...ticketForm.persons],
caller: ticketForm.caller, caller: ticketForm.caller,
emergency: isEmergency.value ? "yes" : "no",
}); });
} }
function resetForm() { function resetForm() {
ticketForm.content = ""; ticketForm.content = "";
ticketForm.motive = undefined; ticketForm.motive = undefined;
ticketForm.persons = []; ticketForm.persons = [];
ticketForm.caller = null; ticketForm.caller = null;
ticketForm.emergency = props.ticket.emergency as TicketEmergencyState;
} }
</script> </script>

View File

@@ -36,6 +36,11 @@ export const moduleTicketList: Module<State, RootState> = {
getTicketList(state): TicketSimple[] { getTicketList(state): TicketSimple[] {
return state.ticket_list; return state.ticket_list;
}, },
getPreviousTicketList(state, getters, rootState): TicketSimple[] {
return state.ticket_list.filter(
(ticket) => ticket.id !== rootState.ticket.ticket.id,
);
},
getPagination(state) { getPagination(state) {
return state.pagination; return state.pagination;
}, },

View File

@@ -98,9 +98,9 @@ onMounted(async () => {
byAddresseeToMe: false, byAddresseeToMe: false,
}; };
try { try {
await store.dispatch("getCurrentUser");
await store.dispatch("fetchTicketList", filters); await store.dispatch("fetchTicketList", filters);
await store.dispatch("fetchMotives"); store.dispatch("getCurrentUser");
store.dispatch("fetchMotives");
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }