feat(ux): gate ONU device actions + delete behind manage_settings, own block

Lane 3 (durcissement) : OnuActionsPanel (suspendre/rétablir/redémarrer/vitesse/remplacer/retirer — écritures réseau live,
suspendre coupe le client) était monté SANS permission dans EquipmentDetail. Désormais gaté can('manage_settings') + sorti
du bloc olt_name (garde propre serial+olt_ip → visible même sans olt_name). Bouton « Supprimer cet équipement » aussi gaté.
(Panel déjà monté depuis un travail antérieur ; ici = gating + placement.)

Lane 1c (déployé via bundle, PlanificationPage co-édité non commité) : puces géofence live En route/Arrivé/Reparti sur les
cartes Jour (kanban) + lignes éditeur de journée, via l'endpoint /roster/geofence-states existant mais jamais appelé
(geofenceStates + refreshGeofenceStates hooké dans reloadOccupancy). Lane 1a : le picker lead exclut déjà le tech assigné
+ garde 409 « déjà assigné dans F » → re-pick déjà empêché (aucun code risqué ajouté).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-07-19 10:30:45 -04:00
parent c1a240023b
commit da9c918fa9

View File

@ -448,8 +448,13 @@
<div class="mf" v-if="doc.warranty_end"><span class="mf-label">Fin garantie</span>{{ doc.warranty_end }}</div> <div class="mf" v-if="doc.warranty_end"><span class="mf-label">Fin garantie</span>{{ doc.warranty_end }}</div>
<div class="mf" v-if="doc.service_location"><span class="mf-label">Adresse</span>{{ doc.service_location }}</div> <div class="mf" v-if="doc.service_location"><span class="mf-label">Adresse</span>{{ doc.service_location }}</div>
<div class="mf" v-if="doc.subscription"><span class="mf-label">Abonnement</span>{{ doc.subscription }}</div> <div class="mf" v-if="doc.subscription"><span class="mf-label">Abonnement</span>{{ doc.subscription }}</div>
<!-- Actions cycle-de-vie ONU (planconfirmrun, tech-3 via n8n) remplace le « Do Stuff » de F. --> </div>
<OnuActionsPanel v-if="doc.serial_number && doc.olt_ip" :serial="doc.serial_number" :olt="doc.olt_ip" :status="doc.status" @changed="onOnuChanged" />
<!-- Actions cycle-de-vie ONU (planconfirmrun : suspendre/rétablir/redémarrer/vitesse/remplacer/retirer) remplace le
« Do Stuff » de F. GATÉ manage_settings (écritures réseau : suspendre coupe le client). Bloc AUTONOME (n'exige pas olt_name). -->
<div v-if="doc.serial_number && doc.olt_ip && can('manage_settings')" class="q-mt-md">
<div class="info-block-title">Actions appareil (réseau)</div>
<OnuActionsPanel :serial="doc.serial_number" :olt="doc.olt_ip" :status="doc.status" @changed="onOnuChanged" />
</div> </div>
<!-- Port Neighbor Context shows health of the same OLT port --> <!-- Port Neighbor Context shows health of the same OLT port -->
@ -499,7 +504,7 @@
</div> </div>
</div> </div>
<div class="q-mt-lg q-pt-md" style="border-top: 1px solid #f1f5f9"> <div v-if="can('manage_settings')" class="q-mt-lg q-pt-md" style="border-top: 1px solid #f1f5f9">
<q-btn flat dense no-caps color="negative" icon="delete_outline" label="Supprimer cet équipement" <q-btn flat dense no-caps color="negative" icon="delete_outline" label="Supprimer cet équipement"
@click="confirmDelete" :loading="deleting" class="text-caption" /> @click="confirmDelete" :loading="deleting" class="text-caption" />
</div> </div>
@ -510,6 +515,7 @@ import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { useQuasar } from 'quasar' import { useQuasar } from 'quasar'
import InlineField from 'src/components/shared/InlineField.vue' import InlineField from 'src/components/shared/InlineField.vue'
import OnuActionsPanel from './modules/OnuActionsPanel.vue' import OnuActionsPanel from './modules/OnuActionsPanel.vue'
import { usePermissions } from 'src/composables/usePermissions'
import { useDeviceStatus } from 'src/composables/useDeviceStatus' import { useDeviceStatus } from 'src/composables/useDeviceStatus'
import { useModemDiagnostic } from 'src/composables/useModemDiagnostic' import { useModemDiagnostic } from 'src/composables/useModemDiagnostic'
import { deleteDoc } from 'src/api/erp' import { deleteDoc } from 'src/api/erp'
@ -519,6 +525,7 @@ const props = defineProps({ doc: { type: Object, required: true }, docName: Stri
const emit = defineEmits(['deleted']) const emit = defineEmits(['deleted'])
const $q = useQuasar() const $q = useQuasar()
const { can } = usePermissions() // écritures réseau ONU + suppression = gated (manage_settings)
const { fetchStatus, fetchOltStatus, fetchPortContext, getDevice, isOnline, combinedStatus, signalQuality, refreshDeviceParams, fetchHosts, loading: deviceLoading } = useDeviceStatus() const { fetchStatus, fetchOltStatus, fetchPortContext, getDevice, isOnline, combinedStatus, signalQuality, refreshDeviceParams, fetchHosts, loading: deviceLoading } = useDeviceStatus()
const { fetchDiagnostic, fetchDiagnosticAuto, loading: wifiDiagLoading, error: wifiDiagError, data: wifiDiag } = useModemDiagnostic() const { fetchDiagnostic, fetchDiagnosticAuto, loading: wifiDiagLoading, error: wifiDiagError, data: wifiDiag } = useModemDiagnostic()
const refreshing = ref(false) const refreshing = ref(false)