feat(ui): T5 — DynamicFilter sur RDV (worklist : recherche + chips Type)
Worklist de rendez-vous : recherche (job/client/lieu) + chips service_type (dynamiques) + presets ('filter.rdv') ;
`filteredJobs` applique le filtre APRÈS la portée `view` (à planifier/à recontacter/tous), qui reste un toggle séparé.
Client-side, réactif. Build vert, leak 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7def94199c
commit
f6f4303e2e
|
|
@ -14,6 +14,8 @@
|
|||
<div class="row q-col-gutter-md">
|
||||
<!-- Worklist -->
|
||||
<div class="col-12 col-md-4">
|
||||
<DynamicFilter v-model="filterState" dense search-placeholder="Job, client, lieu…" :chip-groups="chipGroups"
|
||||
preset-key="filter.rdv" :filtered-count="filteredJobs.length" class="q-mb-sm" />
|
||||
<q-list bordered separator class="rounded-borders">
|
||||
<q-item-label header>
|
||||
{{ view === 'reschedule' ? 'À recontacter' : 'Jobs' }} ({{ filteredJobs.length }})
|
||||
|
|
@ -124,6 +126,7 @@
|
|||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { fmtDur } from 'src/composables/useHelpers' // durée canonique (source unique : «30m»/«1h»/«1h30»)
|
||||
import DynamicFilter from 'src/components/shared/DynamicFilter.vue'
|
||||
import { MapPin } from 'lucide-vue-next' // pin monochrome outline (style menu Dispatch)
|
||||
import { useQuasar } from 'quasar'
|
||||
import * as roster from 'src/api/roster'
|
||||
|
|
@ -161,10 +164,17 @@ function wkMon (iso) { const dt = d2(iso); const off = (dt.getUTCDay() + 6) % 7;
|
|||
function wkLabel (m) { const a = d2(m); const b = new Date(a); b.setUTCDate(b.getUTCDate() + 6); return 'Semaine du ' + a.getUTCDate() + ' ' + MO[a.getUTCMonth()] + ' – ' + b.getUTCDate() + ' ' + MO[b.getUTCMonth()] }
|
||||
|
||||
const isReschedule = computed(() => view.value === 'reschedule' || (sel.value && sel.value.booking_status === 'À reporter'))
|
||||
// Filtre dynamique (client) de la worklist : recherche (job/client/lieu) + chips Type. La PORTÉE (à planifier/à recontacter/tous) reste le toggle `view`.
|
||||
const filterState = ref({ search: '', chips: {} })
|
||||
const serviceTypes = computed(() => [...new Set(jobs.value.map(j => (j.service_type || '').trim()).filter(Boolean))].sort().map(t => ({ value: t, label: t })))
|
||||
const chipGroups = computed(() => [{ key: 'type', label: 'Type', icon: 'category', options: serviceTypes.value }])
|
||||
const filteredJobs = computed(() => {
|
||||
if (view.value === 'reschedule') return rescheduleJobs.value
|
||||
if (view.value === 'all') return jobs.value
|
||||
return jobs.value.filter(j => !j.scheduled_date || j.booking_status !== 'Confirmé')
|
||||
let base = view.value === 'reschedule' ? rescheduleJobs.value : (view.value === 'all' ? jobs.value : jobs.value.filter(j => !j.scheduled_date || j.booking_status !== 'Confirmé'))
|
||||
const t = filterState.value.chips.type
|
||||
if (t) base = base.filter(j => (j.service_type || '').trim() === t)
|
||||
const q = (filterState.value.search || '').toLowerCase().trim()
|
||||
if (q) base = base.filter(j => (jobTitle(j) + ' ' + jobLoc(j) + ' ' + (j.customer_name || '')).toLowerCase().includes(q))
|
||||
return base
|
||||
})
|
||||
// Créneaux groupés Semaine → Jour
|
||||
const slotWeeks = computed(() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user