gigafibre-fsm/apps/ops/src/components/shared/PageHeader.vue
louispaulb 512c4a5f1b feat(ops): dispatch auto complet + perf Boîte/rapports + fix session
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>
2026-07-02 08:49:35 -04:00

38 lines
1.9 KiB
Vue

<template>
<!-- En-tête de page unifié : bouton retour optionnel + titre (+ compteur) + actions à droite + barre de filtres.
Usage : <PageHeader title="Clients" :count="total"><template #actions></template><template #filters></template></PageHeader> -->
<div class="page-header">
<div class="ph-title-row">
<q-btn v-if="back" flat dense round icon="arrow_back" :to="back" class="ph-back"><q-tooltip>Retour</q-tooltip></q-btn>
<h1 class="ph-title">
{{ title }}
<span v-if="count !== null && count !== undefined" class="ph-count">{{ count }}</span>
</h1>
<q-space />
<div v-if="$slots.actions" class="ph-actions"><slot name="actions" /></div>
</div>
<div v-if="$slots.filters" class="ph-filters"><slot name="filters" /></div>
<div v-if="$slots.default" class="ph-sub"><slot /></div>
</div>
</template>
<script setup>
defineProps({
title: { type: String, default: '' },
count: { type: [Number, String], default: null },
back: { type: [String, Object], default: null },
})
</script>
<style scoped>
.page-header { margin-bottom: 16px; }
.ph-title-row { display: flex; align-items: center; gap: 8px; min-height: 34px; }
.ph-back { margin-left: -6px; }
.ph-title { font-size: 1.4rem; font-weight: 720; letter-spacing: -0.01em; margin: 0; display: flex; align-items: center; gap: 10px; text-wrap: balance; }
.ph-count { font-size: 0.78rem; font-weight: 600; color: var(--ops-text-muted); background: var(--ops-bg-light); border: 1px solid var(--ops-border); border-radius: 20px; padding: 2px 10px; font-variant-numeric: tabular-nums; }
.ph-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.ph-filters { display: flex; flex-wrap: wrap; gap: 8px; align-items: flex-end; margin-top: 12px; }
.ph-sub { margin-top: 8px; }
@media (max-width: 599px) { .ph-title { font-size: 1.2rem; } }
</style>