feat(ui): T5 — DynamicFilter sur Évaluations (mode client-side)

Recherche client/commentaire + chips Note (5★/3–4★/≤2★) + presets ('filter.evaluations'). Prouve le mode CLIENT-SIDE :
le computed `sorted` filtre (matchNote + recherche) puis trie ; réagit à filterState sans rechargement.
Build vert, leak 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-04 19:29:54 -04:00
parent 4d940eefc4
commit 7def94199c

View File

@ -10,6 +10,10 @@
<q-btn flat round dense icon="refresh" color="grey-7" :loading="loading" @click="load"><q-tooltip>Rafraîchir</q-tooltip></q-btn>
</div>
<!-- Filtre dynamique (client) : recherche client/commentaire + chips note + presets. Le computed `sorted` réagit à filterState. -->
<DynamicFilter v-model="filterState" search-placeholder="Client, commentaire" :chip-groups="chipGroups"
preset-key="filter.evaluations" :filtered-count="sorted.length" :total-count="ratings.length" class="q-mb-md" />
<div v-if="loading && !ratings.length" class="flex flex-center q-pa-xl"><q-spinner size="36px" color="primary" /></div>
<div v-else-if="!sorted.length" class="ops-card text-center text-grey-6 q-pa-xl" style="border-radius:12px">
<q-icon name="reviews" size="40px" color="grey-4" class="q-mb-sm" /><br>
@ -69,6 +73,7 @@ import { useQuasar } from 'quasar'
import { HUB_URL } from 'src/config/hub'
import { formatDateTime } from 'src/composables/useFormatters'
import { useConversations } from 'src/composables/useConversations'
import DynamicFilter from 'src/components/shared/DynamicFilter.vue'
const router = useRouter()
const $q = useQuasar()
@ -83,9 +88,14 @@ const sortOptions = [
{ label: 'Étoiles ↓ (hautes d\'abord)', value: 'stars_desc' },
]
const dateOf = (r) => String(r.stars_ts || r.comment_ts || r.created || '')
// Tout afficher (plus de distinction « à traiter ») + tri par date ou par nombre d'étoiles.
// Filtre dynamique (client) : recherche client/commentaire + chips note (5 / 34 / 2).
const filterState = ref({ search: '', chips: {} })
const chipGroups = [{ key: 'note', label: 'Note', icon: 'star', color: 'amber-7', options: [{ value: '5', label: '5★' }, { value: '34', label: '34★' }, { value: '12', label: '≤2★' }] }]
function matchNote (r) { const n = filterState.value.chips.note; if (!n) return true; const s = r.stars; if (n === '5') return s >= 5; if (n === '34') return s === 3 || s === 4; if (n === '12') return s != null && s <= 2; return true }
// Tout afficher + FILTRE (recherche/note) puis tri (date ou étoiles).
const sorted = computed(() => {
const arr = [...ratings.value]
const q = (filterState.value.search || '').toLowerCase().trim()
const arr = ratings.value.filter(r => matchNote(r) && (!q || String(r.name || r.customerName || r.customer || r.email || '').toLowerCase().includes(q) || String(r.comment || '').toLowerCase().includes(q)))
if (sortKey.value === 'stars_asc') arr.sort((a, b) => (a.stars ?? 99) - (b.stars ?? 99) || dateOf(b).localeCompare(dateOf(a)))
else if (sortKey.value === 'stars_desc') arr.sort((a, b) => (b.stars ?? -1) - (a.stars ?? -1) || dateOf(b).localeCompare(dateOf(a)))
else arr.sort((a, b) => dateOf(b).localeCompare(dateOf(a)))