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>
44 lines
2.3 KiB
JavaScript
44 lines
2.3 KiB
JavaScript
import { ref } from 'vue'
|
|
import { Notify } from 'quasar'
|
|
import { createDoc } from 'src/api/erp'
|
|
|
|
// Softphone PARTAGÉ (Twilio WebRTC + repli SIP) — état singleton pilotable de n'importe où (ConversationPanel, fiche client…).
|
|
// Le dialer lui-même = <PhoneModal> monté UNE seule fois globalement (MainLayout) et lié à cet état. Remplace l'ancien
|
|
// montage exclusif dans ChatterPanel → l'appel intégré existe maintenant dans le panneau de comms unifié.
|
|
const open = ref(false)
|
|
const number = ref('')
|
|
const customer = ref('') // nom ERPNext (C-…) pour lier le log d'appel à la bonne fiche
|
|
const customerName = ref('') // nom affiché dans le dialer
|
|
const provider = ref('twilio')
|
|
|
|
// Ouvre le dialer pré-rempli + (si fournie) la fiche à laquelle lier le log d'appel.
|
|
function call (num, ctx = {}) {
|
|
number.value = String(num || '').trim()
|
|
customer.value = ctx.customer || ''
|
|
customerName.value = ctx.customerName || ''
|
|
provider.value = ctx.provider || 'twilio'
|
|
open.value = true
|
|
}
|
|
|
|
// Log d'appel = créateur UNIQUE (corrige l'ancien double-log PhoneModal+ChatterPanel) : un seul doc Communication par appel.
|
|
async function handleCallEnded (info) {
|
|
const phone = (info && info.remote) || number.value
|
|
if (!customer.value || !phone) return // pas de fiche liée → on ne crée pas de Communication orpheline
|
|
const dur = (info && info.duration) || 0
|
|
const durationStr = `${Math.floor(dur / 60)}m${String(dur % 60).padStart(2, '0')}s`
|
|
const incoming = info && info.direction === 'in'
|
|
try {
|
|
await createDoc('Communication', {
|
|
subject: (incoming ? 'Appel de ' : 'Appel vers ') + phone,
|
|
communication_type: 'Communication', communication_medium: 'Phone',
|
|
sent_or_received: incoming ? 'Received' : 'Sent',
|
|
status: 'Linked', phone_no: phone, sender: 'sms@gigafibre.ca',
|
|
sender_full_name: incoming ? ((info && info.remoteName) || phone) : 'Targo Ops',
|
|
content: `Appel ${incoming ? 'reçu de' : 'vers'} ${phone} — Durée: ${durationStr}`,
|
|
reference_doctype: 'Customer', reference_name: customer.value,
|
|
})
|
|
} catch (e) { Notify.create({ type: 'negative', message: 'Erreur enregistrement appel : ' + (e.message || e), timeout: 3000 }) }
|
|
}
|
|
|
|
export function useSoftphone () { return { open, number, customer, customerName, provider, call, handleCallEnded } }
|