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

This commit is contained in:
Boris Waaub
2025-09-22 09:23:30 +00:00
committed by Julien Fastré
parent ec9d0be70b
commit e1ef65d4ca
19 changed files with 242 additions and 141 deletions

View File

@@ -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),
};
}
}

View File

@@ -44,7 +44,7 @@ class Motive
/**
* @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')]
private Collection $storedObjects;

View File

@@ -186,6 +186,8 @@ export interface TicketFilterParams {
export interface TicketInitForm {
content: string;
motive?: Motive;
addressees: UserGroupOrUser[];
persons: Person[];
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));
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);

View File

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

View File

@@ -46,10 +46,12 @@ const suggestedValues = ref<Entities[]>([...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":

View File

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

View File

@@ -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>();

View File

@@ -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 () => {

View File

@@ -12,7 +12,16 @@
:motives="motives"
/>
</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 -->
<div class="row mb-3">
<div class="col-md-6">
@@ -51,6 +60,22 @@
:motive="ticketForm.motive"
/>
</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 -->
<div class="d-flex justify-content-end gap-2 mt-4">
@@ -67,16 +92,23 @@
</template>
<script setup lang="ts">
import { reactive, watch } from "vue";
import { computed, reactive, ref, 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";
import AddresseeSelectorComponent from "./Addressee/AddresseeSelectorComponent.vue";
import ToggleComponent from "../../TicketList/components/ToggleComponent.vue";
// Types
import { Motive, Ticket, TicketInitForm } from "../../../types";
import {
Motive,
Ticket,
TicketEmergencyState,
TicketInitForm,
} from "../../../types";
import { Person } from "ChillPersonAssets/types";
// Translations
@@ -90,7 +122,9 @@ import {
CHILL_TICKET_TICKET_SET_PERSONS_TITLE_PERSON,
CHILL_TICKET_TICKET_SET_PERSONS_CALLER_LABEL,
CHILL_TICKET_TICKET_SET_PERSONS_USER_LABEL,
CHILL_TICKET_LIST_FILTER_EMERGENCY,
} from "translator";
import { UserGroup, UserGroupOrUser } from "ChillMainAssets/types";
const props = defineProps<{
ticket: Ticket;
@@ -107,11 +141,19 @@ const store = useStore();
const ticketForm = reactive({
content: "",
addressees: props.ticket.currentAddressees as UserGroupOrUser[],
motive: props.ticket.currentMotive as Motive | null,
persons: props.ticket.currentPersons as Person[],
caller: props.ticket.caller as Person | null,
emergency: props.ticket.emergency as TicketEmergencyState,
} as TicketInitForm);
const isEmergency = ref<boolean>(
props.ticket.emergency == "yes" ? true : false,
);
const userGroups = computed(() => store.getters.getUserGroups as UserGroup[]);
watch(
() => ticketForm.caller,
async (newCaller) => {
@@ -124,16 +166,18 @@ function submitForm() {
emit("submit", {
content: ticketForm.content,
motive: ticketForm.motive,
addressees: [...ticketForm.addressees],
persons: [...ticketForm.persons],
caller: ticketForm.caller,
emergency: isEmergency.value ? "yes" : "no",
});
}
function resetForm() {
ticketForm.content = "";
ticketForm.motive = undefined;
ticketForm.persons = [];
ticketForm.caller = null;
ticketForm.emergency = props.ticket.emergency as TicketEmergencyState;
}
</script>

View File

@@ -36,6 +36,11 @@ export const moduleTicketList: Module<State, RootState> = {
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;
},

View File

@@ -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;
}