Accumulated work on the dispatch/legacy-writeback branch: - Communications UI: CommunicationsPage, ConversationFullPage, DepartmentBoard, PipelineBoard, ReaderStack, Orchestrator/NewTicket/ServiceStatus/Outbox dialogs; hub gmail.js, ticket-collab.js, outbox.js, coupon-triage.js, client-diag.js. - Billing/sync mirror (F→ERPNext): legacy-payments.js, legacy-sync.js, sync-orchestrator.js, supplier-invoices.js, municipality.js + incremental migration scripts; LegacySyncPage, SupplierInvoices + negative-billing / terminated-active reports. - Roster/campaigns/network/voice: roster + roster-assistant, campaigns, giftbit, olt-snmp, traccar, twilio, vision, tech-absence-sms, ai/agent/config/helpers, legacy-dispatch-sync; ops PlanificationPage, RapportsPage, Settings, Tickets, ClientDetail updates. - docs/ PLATFORM_GUIDE + UI_AND_OPTIMIZATION; .gitignore __pycache__. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
40 lines
1.8 KiB
Vue
40 lines
1.8 KiB
Vue
<template>
|
|
<q-page class="cfp-page">
|
|
<div class="cfp-bar">
|
|
<q-btn flat dense icon="arrow_back" label="Boîte" no-caps color="indigo-7" @click="$router.push('/communications')" />
|
|
</div>
|
|
<ConversationPanel inline />
|
|
</q-page>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted, watch } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import ConversationPanel from 'src/components/shared/ConversationPanel.vue'
|
|
import { useConversations } from 'src/composables/useConversations'
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const { discussions, activeDiscussion, fetchList, openDiscussion, openConversation } = useConversations()
|
|
|
|
async function openByToken (token) {
|
|
if (!token) return
|
|
const find = () => (discussions.value || []).find(d => d.token === token || (d.conversations || []).some(c => c.token === token))
|
|
let disc = find()
|
|
if (!disc) { await fetchList(); disc = find() }
|
|
if (disc) { openDiscussion(disc); return } // ouvre le fil (active la vue thread plein écran)
|
|
await openConversation(token) // repli : ouverture directe par jeton (pose aussi activeDiscussion)
|
|
if (!activeDiscussion.value) router.push('/communications') // introuvable/supprimée → retour à la boîte, pas de loader infini
|
|
}
|
|
|
|
onMounted(() => openByToken(route.params.token))
|
|
watch(() => route.params.token, t => openByToken(t))
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* Plein écran : la page occupe la hauteur du viewport (moins l'en-tête) → le fil défile à l'intérieur, le bloc de réponse reste ancré en bas */
|
|
.cfp-page { padding: 0; height: calc(100vh - 50px); display: flex; flex-direction: column; overflow: hidden; }
|
|
.cfp-bar { padding: 4px 8px; border-bottom: 1px solid var(--ops-border, #e2e8f0); background: #fff; flex: 0 0 auto; }
|
|
.cfp-page :deep(.conv-fullpage) { flex: 1 1 auto; min-height: 0; }
|
|
</style>
|