From e1ef65d4ca1be41662ad75d9702576eba63bb7a5 Mon Sep 17 00:00:00 2001 From: Boris Waaub Date: Mon, 22 Sep 2025 09:23:30 +0000 Subject: [PATCH] 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 --- .../Entity/StoredObject.php | 2 +- .../public/page/homepage_widget/index.ts | 6 + .../ChillMainBundle/Resources/public/types.ts | 22 ++ .../public/vuejs/HomepageWidget/App.vue | 197 +++++++++--------- .../vuejs/HomepageWidget/TabCounter.vue | 2 +- .../HomepageWidget/store/modules/homepage.ts | 26 +-- .../Resources/views/Homepage/index.html.twig | 8 + .../src/Controller/MotiveApiController.php | 9 + .../ChillTicketBundle/src/Entity/Motive.php | 2 +- .../src/Resources/public/types.ts | 2 + .../Resources/public/vuejs/TicketApp/App.vue | 16 +- .../components/ActionToolbarComponent.vue | 1 + .../Addressee/AddresseeSelectorComponent.vue | 6 +- .../Emergency/EmergencyToggleComponent.vue | 1 + .../Motive/MotiveSelectorComponent.vue | 20 +- .../components/PreviousTicketsComponent.vue | 2 +- .../components/TicketInitFormComponent.vue | 52 ++++- .../TicketApp/store/modules/ticket_list.ts | 5 + .../Resources/public/vuejs/TicketList/App.vue | 4 +- 19 files changed, 242 insertions(+), 141 deletions(-) diff --git a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php index c849abacc..35ad2ee45 100644 --- a/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php +++ b/src/Bundle/ChillDocStoreBundle/Entity/StoredObject.php @@ -94,7 +94,7 @@ class StoredObject implements Document, TrackCreationInterface /** * @var Collection&Selectable */ - #[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; /** diff --git a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts index 2ac0d85f7..f7dacbefd 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/page/homepage_widget/index.ts @@ -2,5 +2,11 @@ import App from "ChillMainAssets/vuejs/HomepageWidget/App.vue"; import { createApp } from "vue"; import { store } from "ChillMainAssets/vuejs/HomepageWidget/store"; +declare global { + interface Window { + homepage_config: string; + } +} + const _app = createApp(App); _app.use(store).mount("#homepage_widget"); diff --git a/src/Bundle/ChillMainBundle/Resources/public/types.ts b/src/Bundle/ChillMainBundle/Resources/public/types.ts index fecb9a578..54e5658a2 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/types.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/types.ts @@ -278,3 +278,25 @@ export interface addNewEntities { selected: Selected[]; 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; +} diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue index 8016266ac..c1ac025ff 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/App.vue @@ -1,83 +1,15 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue index d25d1628b..712a5247c 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/TabCounter.vue @@ -1,5 +1,5 @@ diff --git a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts index 776e5cb82..a06250c3b 100644 --- a/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts +++ b/src/Bundle/ChillMainBundle/Resources/public/vuejs/HomepageWidget/store/modules/homepage.ts @@ -13,6 +13,7 @@ import { WorflowCc, } from "ChillPersonAssets/types"; import { RootState } from ".."; +import { HomepageTabs } from "ChillMainAssets/types"; export interface TasksState { warning: PaginationResponse; @@ -82,17 +83,6 @@ export const moduleHomepage: Module = { isWorkflowsLoaded(state) { 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: { addEvaluations(state, evaluations) { @@ -129,20 +119,20 @@ export const moduleHomepage: Module = { actions: { async getByTab({ commit, getters, dispatch }, { tab, param }) { switch (tab) { - case "MyTickets": + case HomepageTabs.MyTickets: if (!getters.isTicketsLoaded) { commit("setTicketsLoading", true); // Utilise l'action du module ticket_list await dispatch( "fetchTicketList", - { byAddresseeToMe: true }, + { byAddresseeToMe: true, byCurrentState: "open" }, { root: true }, ); commit("setTicketsLoading", false); } break; - case "MyEvaluations": + case HomepageTabs.MyEvaluations: if (!getters.isEvaluationsLoaded) { commit("setLoading", true); const url = `/api/1.0/person/accompanying-period/work/evaluation/my-near-end${"?" + param}`; @@ -157,7 +147,7 @@ export const moduleHomepage: Module = { }); } break; - case "MyTasks": + case HomepageTabs.MyTasks: if (!(getters.isTasksWarningLoaded && getters.isTasksAlertLoaded)) { 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}`, @@ -182,7 +172,7 @@ export const moduleHomepage: Module = { }); } break; - case "MyAccompanyingCourses": + case HomepageTabs.MyAccompanyingCourses: if (!getters.isAccompanyingCoursesLoaded) { commit("setLoading", true); const url = `/api/1.0/person/accompanying-course/list/by-recent-attributions${"?" + param}`; @@ -197,7 +187,7 @@ export const moduleHomepage: Module = { }); } break; - case "MyNotifications": + case HomepageTabs.MyNotifications: if (!getters.isNotificationsLoaded) { commit("setLoading", true); const url = `/api/1.0/main/notification/my/unread${"?" + param}`; @@ -212,7 +202,7 @@ export const moduleHomepage: Module = { }); } break; - case "MyWorkflows": + case HomepageTabs.MyWorkflows: if (!getters.isWorflowsLoaded) { commit("setLoading", true); makeFetch("GET", "/api/1.0/main/workflow/my") diff --git a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig index 98af21171..290fd91ef 100644 --- a/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig +++ b/src/Bundle/ChillMainBundle/Resources/views/Homepage/index.html.twig @@ -11,5 +11,13 @@ {% endblock %} {% block js %} + + {{ encore_entry_script_tags('page_homepage_widget') }} + {% endblock %} diff --git a/src/Bundle/ChillTicketBundle/src/Controller/MotiveApiController.php b/src/Bundle/ChillTicketBundle/src/Controller/MotiveApiController.php index d3f682615..d57b43e7b 100644 --- a/src/Bundle/ChillTicketBundle/src/Controller/MotiveApiController.php +++ b/src/Bundle/ChillTicketBundle/src/Controller/MotiveApiController.php @@ -11,6 +11,7 @@ declare(strict_types=1); namespace Chill\TicketBundle\Controller; +use Chill\DocStoreBundle\Serializer\Normalizer\StoredObjectNormalizer; use Chill\MainBundle\CRUD\Controller\ApiController; use Doctrine\ORM\QueryBuilder; use Symfony\Component\HttpFoundation\Request; @@ -22,4 +23,12 @@ final class MotiveApiController extends ApiController /* @var $query QueryBuilder */ $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), + }; + } } diff --git a/src/Bundle/ChillTicketBundle/src/Entity/Motive.php b/src/Bundle/ChillTicketBundle/src/Entity/Motive.php index 380ea3d02..789d6b562 100644 --- a/src/Bundle/ChillTicketBundle/src/Entity/Motive.php +++ b/src/Bundle/ChillTicketBundle/src/Entity/Motive.php @@ -44,7 +44,7 @@ class Motive /** * @var Collection */ - #[ORM\ManyToMany(targetEntity: StoredObject::class)] + #[ORM\ManyToMany(targetEntity: StoredObject::class, fetch: 'EAGER')] #[ORM\JoinTable(name: 'motive_stored_objects', schema: 'chill_ticket')] private Collection $storedObjects; diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts b/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts index 42e0aff81..4c31f2341 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/types.ts @@ -186,6 +186,8 @@ export interface TicketFilterParams { export interface TicketInitForm { content: string; motive?: Motive; + addressees: UserGroupOrUser[]; persons: Person[]; caller: Person | null; + emergency: TicketEmergencyState; } diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/App.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/App.vue index b9d330543..6cdc02ac9 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/App.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/App.vue @@ -87,12 +87,16 @@ async function handleFormSubmit(ticketForm: TicketInitForm) { toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING)); return; } + if (ticketForm.content && ticketForm.content.trim() !== "") { await store.dispatch("createComment", ticketForm.content); } else { toast.warning(trans(CHILL_TICKET_TICKET_INIT_FORM_WARNING)); return; } + + await store.dispatch("setEmergency", ticketForm.emergency); + await store.dispatch("setAddressees", ticketForm.addressees); await store.dispatch("setPersons", ticketForm.persons); // Forcer le rafraîchissement des composants @@ -111,13 +115,13 @@ function closeModal() { showTicketInitFormModal.value = false; } -onMounted(async () => { +onMounted(() => { try { - await store.dispatch("getCurrentUser"); - await store.dispatch("fetchMotives"); - await store.dispatch("fetchUserGroups"); - await store.dispatch("fetchUsers"); - await store.dispatch("getSuggestedPersons"); + store.dispatch("getCurrentUser"); + store.dispatch("fetchMotives"); + store.dispatch("fetchUserGroups"); + store.dispatch("fetchUsers"); + store.dispatch("getSuggestedPersons"); showTicketInitFormModal.value = store.getters.isIncompleteTicket; } catch (error) { toast.error(error as string); diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/ActionToolbarComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/ActionToolbarComponent.vue index 589066e3d..6c437f0b7 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/ActionToolbarComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/ActionToolbarComponent.vue @@ -41,6 +41,7 @@
diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue index 074e3b7c1..02040b008 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Addressee/AddresseeSelectorComponent.vue @@ -46,10 +46,12 @@ const suggestedValues = ref([...props.suggested]); watch( () => [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) => { - return !modelValue.some((selected: Entities) => { + return !props.modelValue.some((selected: Entities) => { if (suggested.type == "user_group" && selected.type == "user_group") { switch (selected.excludeKey) { case "level": diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Emergency/EmergencyToggleComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Emergency/EmergencyToggleComponent.vue index e41d6a560..3c2022cb2 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Emergency/EmergencyToggleComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Emergency/EmergencyToggleComponent.vue @@ -9,6 +9,7 @@ }" @click="toggleEmergency" :disabled="props.disabled" + type="button" > {{ trans(CHILL_TICKET_TICKET_BANNER_EMERGENCY) }} diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Motive/MotiveSelectorComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Motive/MotiveSelectorComponent.vue index cbc428675..b1f6315d1 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Motive/MotiveSelectorComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/Motive/MotiveSelectorComponent.vue @@ -8,7 +8,7 @@ label="label" :custom-label="customLabel" track-by="id" - open-direction="top" + :open-direction="openDirection" :multiple="false" :searchable="true" :placeholder="trans(CHILL_TICKET_TICKET_SET_MOTIVE_LABEL)" @@ -46,10 +46,20 @@ import { // Component import PelotonComponent from "../PelotonComponent.vue"; -const props = defineProps<{ - modelValue?: Motive; - motives: Motive[]; -}>(); +const props = defineProps({ + modelValue: { + type: Object as () => Motive | undefined, + default: undefined, + }, + motives: { + type: Array as () => Motive[], + default: () => [], + }, + openDirection: { + type: String, + default: "bottom", + }, +}); const emit = defineEmits<(e: "update:modelValue", value: Motive | undefined) => void>(); diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/PreviousTicketsComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/PreviousTicketsComponent.vue index 8c33d804c..8570ab184 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/PreviousTicketsComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/PreviousTicketsComponent.vue @@ -84,7 +84,7 @@ const currentPersons = computed( () => store.getters.getCurrentPersons as Person[], ); const previousTickets = computed( - () => store.getters.getTicketList as TicketSimple[], + () => store.getters.getPreviousTicketList as TicketSimple[], ); onMounted(async () => { diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/TicketInitFormComponent.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/TicketInitFormComponent.vue index 19d1c4c2e..9d7e6dee1 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/TicketInitFormComponent.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/components/TicketInitFormComponent.vue @@ -12,7 +12,16 @@ :motives="motives" />
- + +
+ + +
@@ -51,6 +60,22 @@ :motive="ticketForm.motive" />
+
+ + +
@@ -67,16 +92,23 @@ diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list.ts b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list.ts index a5387245e..af60123a8 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list.ts +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketApp/store/modules/ticket_list.ts @@ -36,6 +36,11 @@ export const moduleTicketList: Module = { getTicketList(state): TicketSimple[] { return state.ticket_list; }, + getPreviousTicketList(state, getters, rootState): TicketSimple[] { + return state.ticket_list.filter( + (ticket) => ticket.id !== rootState.ticket.ticket.id, + ); + }, getPagination(state) { return state.pagination; }, diff --git a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketList/App.vue b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketList/App.vue index 1f4cfd891..de34d0d28 100644 --- a/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketList/App.vue +++ b/src/Bundle/ChillTicketBundle/src/Resources/public/vuejs/TicketList/App.vue @@ -98,9 +98,9 @@ onMounted(async () => { byAddresseeToMe: false, }; try { - await store.dispatch("getCurrentUser"); await store.dispatch("fetchTicketList", filters); - await store.dispatch("fetchMotives"); + store.dispatch("getCurrentUser"); + store.dispatch("fetchMotives"); } finally { isLoading.value = false; }