<template>
  <q-page class="q-pa-md" style="max-width:1000px;margin:0 auto">
    <div class="text-h5 q-mb-md">Sous-traitants payés à l'acte</div>

    <!-- Générer / envoyer un lien de saisie -->
    <q-card flat bordered class="q-mb-md">
      <q-card-section>
        <div class="text-subtitle1 q-mb-sm">Envoyer un lien de saisie</div>
        <div class="row q-col-gutter-sm items-end">
          <q-input class="col-12 col-sm-3" dense outlined v-model="send.ticket" label="N° ticket" />
          <q-input class="col-12 col-sm-3" dense outlined v-model="send.sub" label="Sous-traitant" />
          <q-input class="col-12 col-sm-3" dense outlined v-model="send.phone" label="Tél (SMS, optionnel)" />
          <div class="col-12 col-sm-3">
            <q-btn color="primary" :label="send.phone ? 'Envoyer (SMS)' : 'Générer le lien'" :loading="send.loading" :disable="!send.ticket" @click="doSend" class="full-width" />
          </div>
        </div>
        <div v-if="send.link" class="q-mt-sm">
          <q-input dense outlined readonly :model-value="send.link" @focus="e => e.target.select()">
            <template #append><q-btn flat dense round icon="content_copy" @click="copy(send.link)" /></template>
          </q-input>
          <div v-if="send.sent" class="text-positive text-caption q-mt-xs">Lien envoyé par {{ send.sent }}.</div>
        </div>
      </q-card-section>
    </q-card>

    <!-- Entente de services : lien/PDF des conditions montrées au sous-traitant à la certification -->
    <q-card flat bordered class="q-mb-md">
      <q-card-section class="q-py-sm">
        <div class="row items-center q-col-gutter-sm">
          <div class="col-12 col-sm-auto text-subtitle2">Entente de services</div>
          <div class="col">
            <span v-if="entente.url" class="text-caption">Conditions actuelles :
              <a :href="entente.url" target="_blank" rel="noopener">{{ entente.isPdf ? 'PDF de l’entente' : entente.url }}</a>
            </span>
            <span v-else class="text-caption text-grey">Aucune — le formulaire affiche « entente de services » sans lien.</span>
          </div>
          <q-btn v-if="canApprove" class="col-auto" dense flat icon="edit" :label="entente.edit ? 'Fermer' : 'Modifier'" @click="entente.edit = !entente.edit" />
        </div>
        <div v-if="canApprove && entente.edit" class="q-mt-sm">
          <div class="row q-col-gutter-sm items-end">
            <q-input class="col" dense outlined v-model="entente.input" label="Lien web des conditions" placeholder="https://…" @keyup.enter="saveEntenteUrl" />
            <q-btn class="col-auto" dense unelevated color="primary" label="Enregistrer le lien" :loading="entente.saving" :disable="!entente.input" @click="saveEntenteUrl" />
          </div>
          <div class="row items-center q-gutter-sm q-mt-xs">
            <q-btn dense outline icon="upload_file" label="Téléverser un PDF" :loading="entente.saving" @click="$refs.entFile.click()" />
            <input ref="entFile" type="file" accept="application/pdf" style="display:none" @change="onEntPdf" />
            <q-space />
            <q-btn v-if="entente.url" dense flat color="negative" label="Retirer" @click="clearEntente" />
          </div>
          <div class="text-caption text-grey q-mt-xs">Lien web (page de conditions) ou PDF (max 10 Mo). Affiché sur le formulaire du sous-traitant, près de la case « Je certifie… ».</div>
        </div>
      </q-card-section>
    </q-card>

    <q-tabs v-model="tab" dense align="left" class="q-mb-sm text-primary" active-color="primary" indicator-color="primary">
      <q-tab name="bareme" label="Barème" />
      <q-tab name="soum" label="Soumissions" />
      <q-tab name="releve" label="Relevés / Paie" />
    </q-tabs>

    <!-- Barème : prix par acte -->
    <q-card v-show="tab === 'bareme'" flat bordered>
      <q-card-section>
        <div class="row items-center q-gutter-sm q-mb-sm">
          <q-select dense outlined style="min-width:210px" v-model="rates.editSub" :options="editSubOptions" emit-value map-options label="Barème à éditer" @update:model-value="loadRates" />
          <q-select dense outlined style="min-width:250px" v-model="addTechModel" :options="techOptions" use-input input-debounce="200" clearable emit-value map-options label="+ Ajouter (chercher un technicien)" @filter="filterTechs" @update:model-value="onAddTech">
            <template #no-option><q-item><q-item-section class="text-grey-6">Tapez pour chercher un technicien…</q-item-section></q-item></template>
          </q-select>
          <q-space />
          <q-btn v-if="rates.editSub" flat dense no-caps icon="content_copy" label="Copier depuis" color="primary">
            <q-menu auto-close>
              <q-list dense style="min-width:190px">
                <q-item clickable @click="copyFrom('')"><q-item-section>Barème global (modèle)</q-item-section></q-item>
                <template v-for="n in subNames" :key="n"><q-item v-if="n !== rates.editSub" clickable @click="copyFrom(n)"><q-item-section>{{ n }}</q-item-section></q-item></template>
              </q-list>
            </q-menu>
          </q-btn>
          <q-btn color="primary" dense unelevated label="Enregistrer" :loading="rates.saving" @click="saveRatesNow" />
        </div>
        <div class="text-caption text-grey q-mb-sm">
          <template v-if="rates.editSub"><q-icon name="person" size="14px" /> Liste de prix de <b>{{ rates.editSub }}</b> — hérite du barème global ; un prix modifié le surcharge. 0 $ = non défini.</template>
          <template v-else>Barème global — modèle de base pour tous les sous-traitants. 0 $ = non défini.</template>
        </div>
        <q-inner-loading :showing="rates.loading" />
        <q-list separator>
          <q-item v-for="r in rates.list" :key="r.code">
            <q-item-section>
              <q-item-label>{{ r.label }}<q-badge v-if="rates.editSub && r._override" color="green-2" text-color="green-9" label="perso" class="q-ml-xs" /></q-item-label>
              <q-item-label caption>
                {{ unitLabel(r.unit) }}<span v-if="r.grp"> · groupe « {{ r.grp }} »</span><span v-if="r.implies"> · coche « {{ r.implies }} »</span>
              </q-item-label>
            </q-item-section>
            <q-item-section side style="min-width:128px">
              <q-input dense outlined type="number" step="0.01" min="0" v-model.number="r.price" prefix="$" class="no-spin" @focus="ev => { rateFocus(r); ev.target && ev.target.select && ev.target.select() }" @blur="rateBlur(r)" />
            </q-item-section>
          </q-item>
        </q-list>
      </q-card-section>
    </q-card>

    <!-- Soumissions : revue -->
    <q-card v-show="tab === 'soum'" flat bordered>
      <q-card-section>
        <div class="row items-center q-mb-sm q-gutter-sm">
          <q-input dense outlined v-model="soum.ticket" label="Filtrer par n° ticket" clearable style="max-width:240px" @keyup.enter="loadSubs" @clear="loadSubs" />
          <q-btn flat dense round icon="search" @click="loadSubs" />
          <q-space /><q-btn flat dense round icon="refresh" @click="loadSubs" />
        </div>
        <q-inner-loading :showing="soum.loading" />
        <div v-if="!soum.loading && !soum.list.length" class="text-grey q-pa-lg text-center">Aucune soumission.</div>
        <q-list separator>
          <q-item v-for="s in soum.list" :key="s.id">
            <q-item-section>
              <q-item-label class="text-weight-medium">{{ s.ref }} · ticket #{{ s.ticket }}</q-item-label>
              <q-item-label caption>{{ s.subcontractor || '—' }} · {{ fmtDate(s.created_at) }}</q-item-label>
              <q-item-label caption :class="s.status === 'infructueuse' ? 'text-warning' : ''">{{ actsSummary(s) }}</q-item-label>
              <q-item-label v-if="s.notes" caption class="text-italic text-grey-8">📝 {{ s.notes }}</q-item-label>
              <div v-if="photosOf(s).length" class="row q-gutter-sm q-mt-xs">
                <div v-for="(p, i) in photosOf(s)" :key="i" class="acte-ph">
                  <img :src="imgUrl(p.file)" class="acte-thumb" @click="viewImg(p.file)" />
                  <a v-if="p.lat" :href="mapsUrl(p.lat, p.lon)" target="_blank" rel="noopener" class="acte-geo" :title="'Position de la photo (±' + (p.acc || '?') + ' m)'" @click.stop>📍</a>
                </div>
              </div>
            </q-item-section>
            <q-item-section side top class="text-right">
              <q-chip dense :color="statusColor(s.status)" text-color="white" class="q-ma-none">{{ statusLabel(s.status) }}</q-chip>
              <div class="text-weight-bold q-mt-xs">{{ money(s.amount) }}</div>
              <div v-if="canApprove && s.status === 'soumis'" class="q-mt-xs q-gutter-xs">
                <q-btn dense unelevated size="sm" color="green-7" label="Approuver" @click="doApprove(s, 'approve')" />
                <q-btn dense outline size="sm" color="grey-7" label="Refuser" @click="doApprove(s, 'reject')" />
              </div>
              <div v-else-if="s.approved_by" class="text-caption text-grey-7 q-mt-xs">par {{ s.approved_by }}</div>
              <div v-if="s.reject_reason" class="text-caption text-warning">{{ s.reject_reason }}</div>
            </q-item-section>
          </q-item>
        </q-list>
      </q-card-section>
    </q-card>

    <!-- Relevés / Paie : autofacturation par sous-traitant + période -->
    <q-card v-show="tab === 'releve'" flat bordered>
      <q-card-section>
        <div class="row q-col-gutter-sm items-end q-mb-md">
          <q-select class="col-12 col-sm-4" dense outlined v-model="rel.sub" :options="subNames" label="Sous-traitant" use-input input-debounce="0" new-value-mode="add-unique" @filter="filterSubs" />
          <q-input class="col-6 col-sm-3" dense outlined type="date" v-model="rel.from" label="Du" stack-label />
          <q-input class="col-6 col-sm-3" dense outlined type="date" v-model="rel.to" label="Au" stack-label />
          <div class="col-12 col-sm-2"><q-btn color="primary" class="full-width" label="Relevé" :loading="rel.loading" :disable="!rel.sub" @click="loadReleve" /></div>
        </div>

        <!-- statut fiscal du sous-traitant (sert à l'autofacturation) -->
        <q-banner v-if="rel.data" dense rounded class="bg-grey-2 q-mb-md">
          <div class="row items-center q-col-gutter-md">
            <q-toggle class="col-auto" v-model="cfg.registrant" label="Inscrit TPS/TVQ" :disable="!canApprove" />
            <q-input v-if="cfg.registrant" class="col-auto" dense outlined v-model="cfg.gst_no" label="N° TPS" :disable="!canApprove" style="min-width:160px" />
            <q-input v-if="cfg.registrant" class="col-auto" dense outlined v-model="cfg.qst_no" label="N° TVQ" :disable="!canApprove" style="min-width:160px" />
            <q-input class="col-auto" dense outlined v-model="cfg.email" label="Courriel (copie)" :disable="!canApprove" style="min-width:200px" />
            <q-btn v-if="canApprove" class="col-auto" flat dense color="primary" label="Enregistrer le statut" :loading="cfg.saving" @click="saveCfg" />
          </div>
          <div class="text-caption text-grey-7 q-mt-xs">{{ cfg.registrant ? 'Taxes ajoutées au relevé (autofacturation d\'un fournisseur inscrit).' : 'Aucune taxe — petit fournisseur / non inscrit.' }}</div>
        </q-banner>

        <q-inner-loading :showing="rel.loading" />
        <template v-if="rel.data">
          <q-list separator>
            <q-item v-for="l in rel.data.lines" :key="l.id">
              <q-item-section>
                <q-item-label>{{ l.ref }} · ticket #{{ l.ticket }}</q-item-label>
                <q-item-label caption>{{ fmtDate(l.created_at) }}<span v-if="l.paid_at"> · déjà payé</span></q-item-label>
              </q-item-section>
              <q-item-section side><div class="text-weight-medium">{{ money(l.amount) }}</div></q-item-section>
            </q-item>
          </q-list>
          <div v-if="!rel.data.lines.length" class="text-grey q-pa-lg text-center">Aucune soumission approuvée sur la période.</div>
          <q-separator class="q-my-sm" />
          <div class="releve-tot">
            <div class="row"><div class="col">Sous-total ({{ rel.data.count }} acte{{ rel.data.count > 1 ? 's' : '' }})</div><div class="col-auto">{{ money(rel.data.subtotal) }}</div></div>
            <div v-if="rel.data.config.registrant" class="row text-grey-8"><div class="col">TPS (5 %)</div><div class="col-auto">{{ money(rel.data.tps) }}</div></div>
            <div v-if="rel.data.config.registrant" class="row text-grey-8"><div class="col">TVQ (9,975 %)</div><div class="col-auto">{{ money(rel.data.tvq) }}</div></div>
            <div class="row text-weight-bold text-subtitle1 q-mt-xs"><div class="col">Total</div><div class="col-auto">{{ money(rel.data.total) }}</div></div>
          </div>
          <div class="row q-gutter-sm q-mt-md justify-end">
            <q-btn outline color="primary" icon="download" label="Export CSV (Acomba)" @click="exportCsv" :disable="!rel.data.count" />
            <q-btn v-if="canApprove" unelevated color="info" icon="paid" label="Marquer payé" @click="confirmPaid" :disable="!rel.data.count" />
          </div>
        </template>
      </q-card-section>
    </q-card>

    <q-dialog v-model="img.show">
      <q-card><q-img :src="img.url" style="max-width:90vw;max-height:85vh" fit="contain" /></q-card>
    </q-dialog>
  </q-page>
</template>

<script setup>
import { ref, reactive, computed, onMounted } from 'vue'
import { useQuasar } from 'quasar'
import { usePermissions } from 'src/composables/usePermissions'
import { formatMoney as money, formatDate as fmtDate } from 'src/composables/useFormatters'
import { getRates, saveRates, listSubmissions, sendLink, approve, imgUrl, getSubs, saveSub, getReleve, markPaid, exportUrl, getEntente, saveEntente } from 'src/api/subcontractor'
import { listDocs } from 'src/api/erp'

const $q = useQuasar()
const { isAdmin, hasGroup } = usePermissions()
const canApprove = computed(() => isAdmin.value || hasGroup('superviseur') || hasGroup('supervisor'))
const tab = ref('bareme')
const send = reactive({ ticket: '', sub: '', phone: '', link: '', sent: null, loading: false })
const rates = reactive({ list: [], loading: false, saving: false, editSub: null }) // editSub=null → barème global ; sinon liste de prix de ce sous-traitant
const techOptions = ref([]); const addTechModel = ref(null); let _allTechs = []
const editSubOptions = computed(() => [{ label: '— Barème global (modèle) —', value: null }, ...subNames.value.map(n => ({ label: n, value: n }))])
const soum = reactive({ list: [], ticket: '', loading: false })
const img = reactive({ show: false, url: '' })
const rel = reactive({ sub: '', from: '', to: '', loading: false, data: null })
const cfg = reactive({ registrant: false, gst_no: '', qst_no: '', email: '', saving: false })
const subNames = ref([])
let _allNames = []
const entente = reactive({ url: '', isPdf: false, edit: false, input: '', saving: false })

function unitLabel (u) { return ({ case: 'Case (oui/non)', qte: 'Quantité', metres: 'Mètres', heures: 'Heures' })[u] || u }
// money() unifié via useFormatters (import ci-dessus) — format fr-CA cohérent
// fmtDate → useFormatters.formatDate (date canonique, source unique)
function statusColor (s) { return s === 'approuvé' ? 'green-7' : s === 'payé' ? 'blue-7' : s === 'refusé' ? 'red-7' : s === 'infructueuse' ? 'orange-8' : 'grey-6' }
function statusLabel (s) { return s === 'infructueuse' ? 'infructueuse' : (s || 'soumis') }
function photosOf (s) { return (Array.isArray(s.photos) ? s.photos : []).map(p => (typeof p === 'string' ? { file: p } : p)) }
function mapsUrl (lat, lon) { return 'https://www.google.com/maps?q=' + lat + ',' + lon }
function actsSummary (s) {
  const a = s.acts || {}
  if (s.status === 'infructueuse' || a._outcome === 'infructueuse') return 'Visite infructueuse — ' + (a._reason || '')
  const codes = Object.keys(a).filter(k => k[0] !== '_')
  return codes.length ? codes.length + ' acte' + (codes.length > 1 ? 's' : '') + ' : ' + codes.join(', ') : 'Aucun acte'
}
function copy (txt) { try { navigator.clipboard.writeText(txt); $q.notify({ type: 'positive', message: 'Lien copié', timeout: 1200 }) } catch (e) { /* */ } }
function viewImg (f) { img.url = imgUrl(f); img.show = true }

async function doSend () {
  send.loading = true; send.link = ''; send.sent = null
  try {
    const r = await sendLink(send.ticket.trim(), send.sub.trim(), send.phone.trim() || undefined)
    send.link = r.link; send.sent = r.sent
    $q.notify({ type: 'positive', message: r.sent ? ('Lien envoyé par ' + r.sent) : 'Lien généré', timeout: 1500 })
  } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) } finally { send.loading = false }
}
async function loadRates () {
  rates.loading = true
  try { const r = await getRates(rates.editSub || undefined); rates.list = (r.rates || []).map(x => ({ ...x, price: Number(x.price) || 0 })) } catch (e) { $q.notify({ type: 'negative', message: 'Barème : ' + e.message }) } finally { rates.loading = false }
}
async function saveRatesNow () {
  rates.saving = true
  try {
    await saveRates(rates.list.map(r => ({ code: r.code, price: Number(r.price) || 0 })), rates.editSub || undefined)
    $q.notify({ type: 'positive', message: rates.editSub ? ('Liste de prix de ' + rates.editSub + ' enregistrée') : 'Barème global enregistré' })
    await loadRates()
  } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) } finally { rates.saving = false }
}
// Le 0 « non défini » disparaît au focus (pour taper directement) et revient si laissé vide.
function rateFocus (r) { if (Number(r.price) === 0) r.price = null }
function rateBlur (r) { if (r.price === null || r.price === '' || Number.isNaN(Number(r.price))) r.price = 0 }
// Techniciens (Dispatch Technician) pour l'auto-suggest « ajouter un sous-traitant ».
async function loadTechs () {
  try {
    const rows = await listDocs('Dispatch Technician', { fields: ['name', 'full_name'], limit: 300 })
    _allTechs = (rows || []).map(t => { const n = (t.full_name || t.name || '').trim(); return { label: n, value: n } }).filter(t => t.value).sort((a, b) => a.label.localeCompare(b.label))
    techOptions.value = _allTechs.slice()
  } catch (e) { $q.notify({ type: 'negative', message: 'Techniciens : ' + e.message }) }
}
function filterTechs (val, update) { update(() => { const v = (val || '').toLowerCase(); techOptions.value = v ? _allTechs.filter(t => t.label.toLowerCase().includes(v)) : _allTechs.slice() }) }
// Ajoute le technicien choisi comme sous-traitant (crée l'entrée) puis bascule l'édition sur SA liste de prix (héritée du global).
async function onAddTech (val) {
  if (!val) return
  try {
    await saveSub({ name: val })
    await loadSubsCfg()
    rates.editSub = val
    await loadRates()
    addTechModel.value = null
    $q.notify({ type: 'positive', message: val + ' ajouté — édite sa liste de prix (hérite du barème global).' })
  } catch (e) { $q.notify({ type: 'negative', message: 'Ajout : ' + e.message }) }
}
// Copie les prix d'un modèle (barème global si src vide, sinon un autre sous-traitant) dans la liste éditée — sans enregistrer (revue puis « Enregistrer »).
async function copyFrom (src) {
  try {
    const r = await getRates(src || undefined)
    const map = {}; for (const x of (r.rates || [])) map[x.code] = Number(x.price) || 0
    rates.list.forEach(row => { if (map[row.code] != null) row.price = map[row.code] })
    $q.notify({ type: 'info', message: 'Prix copiés depuis ' + (src || 'le barème global') + ' — vérifie puis « Enregistrer ».' })
  } catch (e) { $q.notify({ type: 'negative', message: 'Copie : ' + e.message }) }
}
async function loadSubs () {
  soum.loading = true
  try { const r = await listSubmissions(soum.ticket && soum.ticket.trim()); soum.list = r.submissions || [] } catch (e) { $q.notify({ type: 'negative', message: 'Soumissions : ' + e.message }) } finally { soum.loading = false }
}
function doApprove (s, action) {
  if (action === 'reject') { $q.dialog({ title: 'Refuser la soumission', message: 'Raison du refus (visible au sous-traitant) :', prompt: { model: '', type: 'text' }, cancel: true }).onOk(reason => sendApprove(s, 'reject', reason)); return }
  sendApprove(s, 'approve')
}
async function sendApprove (s, action, reason) {
  try { const r = await approve(s.id, action, reason); $q.notify({ type: 'positive', message: 'Soumission ' + r.status }); loadSubs() } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) }
}

// ── Relevés / Paie (autofacturation) ──
function filterSubs (val, update) { update(() => { const v = (val || '').toLowerCase(); subNames.value = v ? _allNames.filter(n => n.toLowerCase().includes(v)) : _allNames.slice() }) }
async function loadSubsCfg () {
  try { const r = await getSubs(); const names = new Set([...(r.names || []), ...((r.subs || []).map(s => s.name))]); _allNames = [...names].sort((a, b) => a.localeCompare(b)); subNames.value = _allNames.slice() } catch (e) { /* silencieux */ }
}
function applyCfg (c) { cfg.registrant = !!(c && c.registrant); cfg.gst_no = (c && c.gst_no) || ''; cfg.qst_no = (c && c.qst_no) || ''; cfg.email = (c && c.email) || '' }
async function loadReleve () {
  if (!rel.sub) return
  rel.loading = true
  try { const r = await getReleve(rel.sub, rel.from || undefined, rel.to || undefined); rel.data = r.releve; applyCfg(r.releve.config) } catch (e) { $q.notify({ type: 'negative', message: 'Relevé : ' + e.message }) } finally { rel.loading = false }
}
async function saveCfg () {
  cfg.saving = true
  try { await saveSub({ name: rel.sub, registrant: cfg.registrant, gst_no: cfg.gst_no || null, qst_no: cfg.qst_no || null, email: cfg.email || null }); $q.notify({ type: 'positive', message: 'Statut fiscal enregistré' }); await loadReleve(); loadSubsCfg() } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) } finally { cfg.saving = false }
}
function exportCsv () { try { const a = document.createElement('a'); a.href = exportUrl(rel.sub, rel.from || undefined, rel.to || undefined); a.download = ''; document.body.appendChild(a); a.click(); a.remove() } catch (e) { $q.notify({ type: 'negative', message: 'Export : ' + e.message }) } }
function confirmPaid () {
  $q.dialog({ title: 'Marquer payé', message: 'Marquer ' + rel.data.count + ' soumission(s) approuvée(s) de « ' + rel.sub + ' » comme PAYÉES (' + money(rel.data.total) + ') ? Réservé et non réversible ici.', cancel: true, ok: { label: 'Marquer payé', color: 'blue-7' } }).onOk(async () => {
    try { const r = await markPaid({ subcontractor: rel.sub, from: rel.from || undefined, to: rel.to || undefined }); $q.notify({ type: 'positive', message: (r.paid || 0) + ' soumission(s) payée(s)' }); await loadReleve(); loadSubs() } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) }
  })
}
// ── Entente de services ──
async function loadEntente () {
  try { const r = await getEntente(); entente.url = r.url || ''; entente.isPdf = /\/acte\/entente\/doc/.test(entente.url); if (!entente.isPdf) entente.input = entente.url } catch (e) { /* silencieux */ }
}
async function saveEntenteUrl () {
  entente.saving = true
  try { const r = await saveEntente({ url: entente.input.trim() }); entente.url = r.url; entente.isPdf = false; entente.edit = false; $q.notify({ type: 'positive', message: 'Lien de l’entente enregistré' }) } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) } finally { entente.saving = false }
}
function onEntPdf (e) {
  const f = e.target.files && e.target.files[0]; e.target.value = ''
  if (!f) return
  if (f.size > 10 * 1024 * 1024) { $q.notify({ type: 'negative', message: 'PDF trop volumineux (max 10 Mo)' }); return }
  const reader = new FileReader()
  reader.onload = async () => {
    entente.saving = true
    try { const r = await saveEntente({ pdf: reader.result }); entente.url = r.url; entente.isPdf = true; entente.input = ''; entente.edit = false; $q.notify({ type: 'positive', message: 'PDF de l’entente enregistré' }) } catch (err) { $q.notify({ type: 'negative', message: 'Échec : ' + err.message }) } finally { entente.saving = false }
  }
  reader.readAsDataURL(f)
}
function clearEntente () {
  $q.dialog({ title: 'Retirer l’entente', message: 'Retirer le lien/PDF des conditions ? Le formulaire n’affichera plus de lien.', cancel: true }).onOk(async () => {
    try { await saveEntente({ clear: true }); entente.url = ''; entente.isPdf = false; entente.input = ''; $q.notify({ type: 'positive', message: 'Entente retirée' }) } catch (e) { $q.notify({ type: 'negative', message: 'Échec : ' + e.message }) }
  })
}
onMounted(() => { loadRates(); loadSubs(); loadSubsCfg(); loadEntente(); loadTechs() })
</script>

<style scoped>
.acte-ph { position: relative; }
.acte-thumb { width: 56px; height: 56px; object-fit: cover; border-radius: 6px; cursor: pointer; border: 1px solid var(--ops-line, #e4e7eb); display: block; }
.acte-geo { position: absolute; left: 1px; bottom: 1px; font-size: 13px; text-decoration: none; filter: drop-shadow(0 0 2px #000); }
.releve-tot { max-width: 360px; margin-left: auto; }
.releve-tot .row { padding: 2px 0; }
/* Flèches haut/bas des champs nombre = inutiles → masquées */
.no-spin :deep(input[type=number]) { -moz-appearance: textfield; }
.no-spin :deep(input[type=number])::-webkit-outer-spin-button,
.no-spin :deep(input[type=number])::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
</style>
