Planificateur « Suggérer » : 4 stratégies (smart / meilleurs d'abord / équilibré / juste ce qu'il faut), compétences+niveaux par tech (édition inline), niveau requis par compétence + par job, carte des tournées (1 couleur/tech, domicile→arrêts, sélecteur de jour), fenêtre de dispatch auj.+demain (dates sélectionnées), règle week-end + placeholder « en attente du quart », clustering + lasso + filtre-date sur la carte, accès rapide « À assigner » (badge). Boîte : liste /conversations allégée (45 Mo → ~1 Mo, 17×) + messages chargés à l'ouverture. Rapports : cache SWR sur revenue-explorer (22×). Session : keep-alive + timeout fetch global + authFetch durci → fin des rechargements manuels. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
4.7 KiB
Vue
66 lines
4.7 KiB
Vue
<template>
|
|
<q-btn flat round dense icon="notifications" :color="dark ? 'white' : 'grey-8'">
|
|
<q-badge v-if="unread" floating color="red" rounded>{{ unread > 9 ? '9+' : unread }}</q-badge>
|
|
<q-tooltip>Notifications</q-tooltip>
|
|
<q-menu anchor="bottom right" self="top right" @show="onShow">
|
|
<div style="min-width:330px;max-width:390px">
|
|
<div class="row items-center q-px-md q-py-sm" style="border-bottom:1px solid #eef1f6">
|
|
<q-icon name="notifications" color="primary" class="q-mr-xs" />
|
|
<span class="text-weight-bold">Notifications</span>
|
|
<q-space />
|
|
<q-btn flat dense round size="sm" icon="tune" :color="showPrefs ? 'primary' : 'grey-7'" @click.stop="showPrefs = !showPrefs"><q-tooltip>Choisir mes notifications</q-tooltip></q-btn>
|
|
<q-btn v-if="notifications.length" flat dense size="sm" no-caps label="Tout lu" color="grey-7" @click="markAllRead" />
|
|
</div>
|
|
|
|
<!-- Préférences PAR UTILISATEUR : activer/désactiver chaque feed (suivent le compte) -->
|
|
<div v-if="showPrefs" class="q-px-md q-py-sm" style="border-bottom:1px solid #eef1f6;background:#fafbfc">
|
|
<div class="text-caption text-grey-6 q-mb-xs">Me notifier pour :</div>
|
|
<div v-for="f in FEEDS" :key="f.key" class="row items-center no-wrap q-py-xs">
|
|
<q-icon :name="f.icon" :color="f.color" size="18px" class="q-mr-sm" />
|
|
<span class="text-body2 col">{{ f.label }}</span>
|
|
<q-toggle :model-value="prefs[f.key]" @update:model-value="v => setFeed(f.key, v)" dense color="primary" />
|
|
</div>
|
|
<!-- Mes FILES (self-service) : m'abonner + (dés)activer la notif par file → pilote qui SONNE sur appel entrant + le screen-pop. -->
|
|
<div v-if="(myQueues.queues || []).length" class="q-mt-sm q-pt-sm" style="border-top:1px solid #eef1f6">
|
|
<div class="text-caption text-grey-6 q-mb-xs">Mes files (appels + conversations) :</div>
|
|
<div v-for="q in myQueues.queues" :key="q" class="row items-center no-wrap q-py-xs">
|
|
<q-icon name="groups" :color="myQueues.mine.includes(q) ? 'primary' : 'grey-5'" size="18px" class="q-mr-sm" />
|
|
<span class="text-body2 col" :class="{ 'text-grey-5': !myQueues.mine.includes(q) }">{{ (myQueues.labels && myQueues.labels[q]) || q }}</span>
|
|
<q-toggle :model-value="myQueues.mine.includes(q)" @update:model-value="v => setQueueSub(q, v)" dense color="primary"><q-tooltip>M'abonner à cette file</q-tooltip></q-toggle>
|
|
<q-btn flat dense round size="sm" :icon="myQueues.mine.includes(q) && (myQueues.notify[q] !== false) ? 'notifications_active' : 'notifications_off'" :color="myQueues.mine.includes(q) && (myQueues.notify[q] !== false) ? 'green-7' : 'grey-5'" :disable="!myQueues.mine.includes(q)" class="q-ml-xs" @click="setQueueNotify(q, myQueues.notify[q] === false)"><q-tooltip>{{ myQueues.notify[q] === false ? 'Activer la notif de cette file' : 'Couper la notif de cette file' }}</q-tooltip></q-btn>
|
|
</div>
|
|
</div>
|
|
<div class="text-caption text-grey-5 q-mt-xs">Préférences propres à ton compte.</div>
|
|
</div>
|
|
|
|
<q-list v-if="notifications.length" style="max-height:60vh;overflow:auto">
|
|
<q-item v-for="n in notifications" :key="n.id" clickable v-close-popup @click="go(n)">
|
|
<q-item-section avatar>
|
|
<q-icon :name="n.icon || (n.type === 'comment' ? 'rate_review' : 'star')" :color="n.low ? 'orange-8' : (n.type === 'comment' ? 'primary' : (n.type === 'rating' ? 'amber-7' : 'teal-7'))" size="22px" />
|
|
</q-item-section>
|
|
<q-item-section>
|
|
<q-item-label lines="1">{{ n.title }}</q-item-label>
|
|
<q-item-label caption lines="2">{{ n.caption }}</q-item-label>
|
|
</q-item-section>
|
|
<q-item-section side><q-item-label caption>{{ relTime(n.ts) }}</q-item-label></q-item-section>
|
|
</q-item>
|
|
</q-list>
|
|
<div v-else class="text-grey-6 text-center q-pa-lg"><q-icon name="notifications_none" size="32px" color="grey-4" /><br>Aucune notification</div>
|
|
</div>
|
|
</q-menu>
|
|
</q-btn>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, ref } from 'vue'
|
|
import { useNotifications } from 'src/composables/useNotifications'
|
|
import { relTime } from 'src/composables/useFormatters'
|
|
|
|
defineProps({ dark: Boolean })
|
|
const { notifications, unread, prefs, FEEDS, myQueues, setQueueSub, setQueueNotify, initNotifications, markAllRead, savePrefs, go } = useNotifications()
|
|
const showPrefs = ref(false)
|
|
function onShow () { markAllRead() } // ouvrir = marquer lu (pastille à zéro)
|
|
function setFeed (key, v) { prefs.value[key] = v; savePrefs() }
|
|
onMounted(initNotifications)
|
|
</script>
|