feat(ui): T5 — DynamicFilter sur Tickets (recherche + chips statut/type/priorité + presets)
Remplace search + 3 q-select par <DynamicFilter> ('filter.tickets') ; chipGroups computed (types dynamiques) ;
statut défaut = not_closed préservé ; mappe l'état → refs existantes (search/statusFilter/typeFilter/priorityFilter) → resetAndLoad débouncé.
La PORTÉE « Mes tickets / Mes départements / Tous » reste un toggle séparé (logique de scope/pagination distincte). Compte redondant retiré.
Vérifié préview : 3 groupes, « Non fermés » actif par défaut, chip Urgent 542→3, toggle portée conservé. Build vert, leak 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3d52ae32c4
commit
4d940eefc4
|
|
@ -2,30 +2,12 @@
|
||||||
<q-page padding>
|
<q-page padding>
|
||||||
<!-- Filters row -->
|
<!-- Filters row -->
|
||||||
<div class="row q-col-gutter-sm q-mb-md items-end">
|
<div class="row q-col-gutter-sm q-mb-md items-end">
|
||||||
<div class="col-12 col-md-3">
|
<div class="col-12 col-md">
|
||||||
<div class="filter-label">Recherche</div>
|
<DynamicFilter v-model="filterState" search-placeholder="Sujet, client, #ticket…"
|
||||||
<q-input v-model="search" dense outlined placeholder="Sujet, client, #ticket..." class="ops-search"
|
:chip-groups="chipGroups" preset-key="filter.tickets" :filtered-count="total" @change="onFilterChange" />
|
||||||
@keyup.enter="loadTickets" clearable @clear="loadTickets">
|
|
||||||
<template #prepend><q-icon name="search" /></template>
|
|
||||||
</q-input>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-md-2">
|
<div class="col-6 col-md-2">
|
||||||
<div class="filter-label">Statut</div>
|
<div class="filter-label">Portée</div>
|
||||||
<q-select v-model="statusFilter" dense outlined emit-value map-options
|
|
||||||
:options="statusOptions" @update:model-value="resetAndLoad" />
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-md-2">
|
|
||||||
<div class="filter-label">Type / Departement</div>
|
|
||||||
<q-select v-model="typeFilter" dense outlined emit-value map-options clearable
|
|
||||||
:options="issueTypes" @update:model-value="resetAndLoad" placeholder="Tous" />
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-md-2">
|
|
||||||
<div class="filter-label">Priorite</div>
|
|
||||||
<q-select v-model="priorityFilter" dense outlined emit-value map-options clearable
|
|
||||||
:options="priorityOptions" @update:model-value="resetAndLoad" placeholder="Toutes" />
|
|
||||||
</div>
|
|
||||||
<div class="col-6 col-md-2">
|
|
||||||
<div class="filter-label">Mes tickets</div>
|
|
||||||
<q-btn-toggle v-model="ownerFilter" no-caps dense unelevated
|
<q-btn-toggle v-model="ownerFilter" no-caps dense unelevated
|
||||||
toggle-color="primary" color="grey-3" text-color="grey-8"
|
toggle-color="primary" color="grey-3" text-color="grey-8"
|
||||||
:options="[
|
:options="[
|
||||||
|
|
@ -36,9 +18,6 @@
|
||||||
@update:model-value="resetAndLoad"
|
@update:model-value="resetAndLoad"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-auto">
|
|
||||||
<div class="text-caption text-grey-6 q-mt-sm">{{ total.toLocaleString() }} tickets</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<FlowQuickButton flat dense icon="account_tree" label="Flows tickets"
|
<FlowQuickButton flat dense icon="account_tree" label="Flows tickets"
|
||||||
tooltip="Configurer les automatisations pour les tickets"
|
tooltip="Configurer les automatisations pour les tickets"
|
||||||
|
|
@ -185,6 +164,7 @@ import { formatDate } from 'src/composables/useFormatters'
|
||||||
import { ticketStatusClass as statusClass, priorityClass } from 'src/composables/useStatusClasses'
|
import { ticketStatusClass as statusClass, priorityClass } from 'src/composables/useStatusClasses'
|
||||||
import { useDetailModal } from 'src/composables/useDetailModal'
|
import { useDetailModal } from 'src/composables/useDetailModal'
|
||||||
import { statusOptions, priorityOptions, columns, buildFilters, getSortField } from 'src/config/ticket-config'
|
import { statusOptions, priorityOptions, columns, buildFilters, getSortField } from 'src/config/ticket-config'
|
||||||
|
import DynamicFilter from 'src/components/shared/DynamicFilter.vue'
|
||||||
import { useSla, SLA_BADGE } from 'src/composables/useSla'
|
import { useSla, SLA_BADGE } from 'src/composables/useSla'
|
||||||
import { useAuthStore } from 'src/stores/auth'
|
import { useAuthStore } from 'src/stores/auth'
|
||||||
import { HUB_URL } from 'src/config/hub' // pour résoudre « mes files » (queue-members) → issue_types de mes départements
|
import { HUB_URL } from 'src/config/hub' // pour résoudre « mes files » (queue-members) → issue_types de mes départements
|
||||||
|
|
@ -206,6 +186,22 @@ const statusFilter = ref('not_closed') // défaut = NON FERMÉS → évite les v
|
||||||
const typeFilter = ref(null)
|
const typeFilter = ref(null)
|
||||||
const priorityFilter = ref(null)
|
const priorityFilter = ref(null)
|
||||||
const ownerFilter = ref('mine') // défaut « façon F » : on atterrit sur MES tickets assignés (priorisés), bascule « Tous » dispo
|
const ownerFilter = ref('mine') // défaut « façon F » : on atterrit sur MES tickets assignés (priorisés), bascule « Tous » dispo
|
||||||
|
// DynamicFilter (recherche + chips statut/type/priorité + presets) → mappe vers les refs existantes qui alimentent buildFilters.
|
||||||
|
const filterState = ref({ search: '', chips: { statut: statusFilter.value } }) // statut défaut = not_closed
|
||||||
|
const chipGroups = computed(() => [
|
||||||
|
{ key: 'statut', label: 'Statut', icon: 'label', options: statusOptions.filter(o => o.value !== 'all') },
|
||||||
|
{ key: 'type', label: 'Type', icon: 'category', options: issueTypes.value },
|
||||||
|
{ key: 'priorite', label: 'Priorité', icon: 'priority_high', options: priorityOptions },
|
||||||
|
])
|
||||||
|
let filterTimer = null
|
||||||
|
function onFilterChange (s) {
|
||||||
|
search.value = s.search || ''
|
||||||
|
statusFilter.value = s.chips.statut || 'all'
|
||||||
|
typeFilter.value = s.chips.type || null
|
||||||
|
priorityFilter.value = s.chips.priorite || null
|
||||||
|
clearTimeout(filterTimer)
|
||||||
|
filterTimer = setTimeout(() => resetAndLoad(), 300)
|
||||||
|
}
|
||||||
const tickets = ref([])
|
const tickets = ref([])
|
||||||
// Tri « façon F » : dans les vues scopées (Mes tickets), RANGER par priorité (Urgent→Bas) côté client —
|
// Tri « façon F » : dans les vues scopées (Mes tickets), RANGER par priorité (Urgent→Bas) côté client —
|
||||||
// ERPNext+Postgres ne range pas une priorité Select dans l'ORDER BY, et l'ensemble scopé tient sur 1 page.
|
// ERPNext+Postgres ne range pas une priorité Select dans l'ORDER BY, et l'ensemble scopé tient sur 1 page.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user