i18n: Add ~50 new translation keys (device, trace, sequence, inspector, configDetail, common sections), remove 15 unused keys. Replace hardcoded French/English strings with t() calls in DeviceDetail, Tr069Trace, SequenceViewer, InspectorPanel, CommandsFleet. Build + 765 tests pass.

This commit is contained in:
ATX Bot committed 2026-06-10 10:25:57 +02:00
1 parent 850a3a17b2
commit 198f15ec9b
7 files changed
+189 -68

No files matched your search

+3 -1
View File
@@ -1,11 +1,13 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import type { CwmpExchange } from '../composables/useExchangeStream'
const props = defineProps<{
selectedMessage: CwmpExchange | null
}>()
const { t } = useI18n()
const panelWidth = ref(400)
const isResizing = ref(false)
const copyConfirmation = ref(false)
@@ -227,7 +229,7 @@ watch(
<div v-if="!selectedMessage" class="inspector-placeholder d-flex align-items-center justify-content-center h-100 text-muted">
<div class="text-center p-4">
<i class="bi bi-file-earmark-code fs-1 d-block mb-3 opacity-50"></i>
<p class="mb-0">Sélectionnez un message pour inspecter son contenu XML</p>
<p class="mb-0">{{ t('inspector.selectMessage') }}</p>
</div>
</div>
+15 -12
View File
@@ -1,7 +1,10 @@
<script setup lang="ts">
import { ref, computed, watch, nextTick, onMounted, onBeforeUnmount } from 'vue'
import { useI18n } from 'vue-i18n'
import { formatTimestamp, truncateMethod, needsTruncation, formatTraceStartTime } from '../helpers/sequence_viewer_helpers'
const { t } = useI18n()
export interface CwmpExchange {
sequenceNumber: number
timestamp: string
@@ -338,11 +341,11 @@ const connectionBadgeClass = computed(() => {
const connectionLabel = computed(() => {
switch (connectionState.value) {
case 'connected':
return 'Connecté'
return t('sequence.connected')
case 'paused':
return 'Pause'
return t('sequence.paused')
case 'disconnected':
return 'Déconnecté'
return t('sequence.disconnected')
default:
return ''
}
@@ -418,7 +421,7 @@ watch(
@click="requestClear"
:disabled="isClearing"
>
Effacer les échanges
{{ t('sequence.clearExchanges') }}
</button>
</div>
</div>
@@ -451,7 +454,7 @@ watch(
</svg>
</div>
<p class="text-muted mb-0">
Aucun échange enregistré. Les messages SOAP apparaîtront ici lors de la prochaine connexion du CPE.
{{ t('sequence.noExchanges') }}
</p>
</div>
</div>
@@ -465,7 +468,7 @@ watch(
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1 .708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1"/>
</svg>
Reprendre le défilement automatique
{{ t('sequence.resumeScroll') }}
</button>
</div>
@@ -491,9 +494,9 @@ watch(
<!-- Lifeline headers -->
<div class="d-flex align-items-center border-bottom px-3 py-2 bg-light">
<div class="text-center fw-bold small text-nowrap" style="width: 36px">CPE</div>
<div class="text-center fw-bold small text-nowrap" style="width: 36px">{{ t('sequence.cpe') }}</div>
<div class="flex-grow-1"></div>
<div class="text-center fw-bold small text-nowrap" style="width: 36px">ACS</div>
<div class="text-center fw-bold small text-nowrap" style="width: 36px">{{ t('sequence.acs') }}</div>
</div>
<!-- Messages container with scroll -->
@@ -509,10 +512,10 @@ watch(
<div class="spinner-border spinner-border-sm text-muted" role="status">
<span class="visually-hidden">Chargement des messages plus anciens…</span>
</div>
<span class="text-muted small ms-2">Chargement…</span>
<span class="text-muted small ms-2">{{ t('sequence.loading') }}</span>
</div>
<div v-else-if="!hasMoreOlderMessages && messages.length > BATCH_SIZE" class="text-center py-2">
<span class="text-muted small">Tous les messages sont chargés</span>
<span class="text-muted small">{{ t('sequence.allLoaded') }}</span>
</div>
</div>
@@ -555,7 +558,7 @@ watch(
v-if="message.cwmpMethod === 'Empty'"
class="small text-muted fst-italic mb-1"
>
POST vide
{{ t('sequence.emptyPost') }}
</span>
<span
v-else
@@ -604,7 +607,7 @@ watch(
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 id="clearConfirmLabel" class="modal-title">Effacer les échanges</h5>
<h5 id="clearConfirmLabel" class="modal-title">{{ t('sequence.clearExchanges') }}</h5>
<button type="button" class="btn-close" aria-label="Fermer" @click="cancelClear"></button>
</div>
<div class="modal-body">
+75 -18
View File
@@ -38,21 +38,14 @@
"name": "Name",
"value": "Value",
"type": "Type",
"writable": "Writable",
"discovery": "Discovery",
"provisioning": "Provisioning",
"criteria": "Criteria",
"modelPaths": "Model Paths",
"actions": "Actions",
"actionType": "Action Type",
"modelPath": "Model Path",
"deviceIdentifier": "Device Identifier",
"version": "Version",
"policy": "Policy",
"enabled": "Enabled",
"disabled": "Disabled",
"tags": "Tags",
"compliance": "Compliance",
"updatedAt": "Updated",
"search": "Search",
"general": "General",
@@ -80,7 +73,8 @@
"checksum": "Checksum",
"filterByProductClass": "Filter by product class…",
"saveChanges": "Save changes",
"hardwareVersion": "Hardware version"
"hardwareVersion": "Hardware version",
"devices": "Devices"
},
"button": {
"refresh": "Refresh",
@@ -92,9 +86,7 @@
"cancel": "Cancel",
"confirm": "Confirm",
"fleetManagement": "Fleet Management ({count})",
"addCriteria": "Add Criteria",
"addModelPath": "Add Path",
"addAction": "Add Action",
"addParameter": "Add Parameter",
"previous": "Previous",
"next": "Next"
@@ -105,8 +97,7 @@
},
"placeholder": {
"search": "Search...",
"modelPath": "e.g. InternetGatewayDevice.DeviceInfo.",
"value": "Value"
"modelPath": "e.g. InternetGatewayDevice.DeviceInfo."
},
"options": {
"NI": "Non-Immediate (NI)",
@@ -172,7 +163,6 @@
"tabInventory": "Inventory",
"tabParameters": "Parameters",
"tabCompliance": "Compliance",
"tabDryRun": "Dry Run",
"tabHistory": "History",
"totalDevices": "Matching devices",
"compliant": "Compliant",
@@ -180,10 +170,6 @@
"noMatchingDevices": "No devices match this configuration criteria.",
"loadCompliance": "Load compliance report",
"complianceError": "Error loading compliance report.",
"diffs": "Diffs",
"dryRunDesc": "Simulate applying this configuration to a device without making any real changes.",
"dryRunPlaceholder": "e.g. AABBCC-Device-123",
"runDryRun": "Run",
"dryRunError": "Error during dry run.",
"firmwareSkipped": "firmware outside schedule window",
"paramDiffs": "Parameter differences",
@@ -191,6 +177,77 @@
"targetValue": "Target value",
"notSet": "(not set)",
"noParamDiffs": "No parameter differences.",
"firmwarePath": "Firmware migration path"
"firmwarePath": "Firmware migration path",
"parameter": "Parameter",
"expectedValue": "Expected value",
"noCriteria": "No criteria defined.",
"parameterConditions": "Parameter conditions",
"noInventoryPaths": "No inventory paths defined.",
"noParameters": "No parameters defined.",
"noHistory": "No history available.",
"date": "Date",
"close": "Close"
},
"device": {
"identity": "Identity",
"identifier": "Identifier",
"versions": "Versions",
"system": "System",
"nextConnection": "Next connection",
"clientReferences": "Client references",
"deleteDevice": "Delete device",
"confirmDeleteMessage": "Confirm deletion of",
"exchanges": "TR-069 Exchanges",
"enableTracing": "Enable tracing",
"tracingActive": "Tracing active"
},
"trace": {
"tracedDevices": "Traced devices",
"description": "Enable tracing on a device then click \"View\" to see live exchanges.",
"deviceId": "Device ID",
"started": "Started",
"remove": "Remove",
"enableTracing": "Enable tracing",
"editJson": "Edit JSON",
"editConfig": "Tracing configuration",
"formatJson": "Format",
"saveApply": "Save",
"expectedFormat": "Expected format:",
"notTraced": "This device is not traced yet.",
"noTracedDevices": "No traced devices."
},
"sequence": {
"cpe": "CPE",
"acs": "ACS",
"emptyPost": "Empty POST",
"session": "Session",
"goTo": "Go to…",
"clear": "Clear",
"clearExchanges": "Clear exchanges",
"connected": "Connected",
"paused": "Paused",
"disconnected": "Disconnected",
"resumeScroll": "Resume auto-scroll",
"allLoaded": "All messages loaded",
"loading": "Loading…",
"noExchanges": "No exchanges recorded. SOAP messages will appear here on the next CPE connection.",
"confirmClear": "Are you sure you want to delete all recorded exchanges for this device? This action is irreversible.",
"confirmClearTitle": "Clear exchanges"
},
"inspector": {
"title": "Inspector",
"selectMessage": "Select a message to see its content.",
"direction": "Direction",
"method": "Method",
"session": "Session",
"timestamp": "Timestamp",
"rawXml": "Raw XML"
},
"common": {
"confirm": "Confirm",
"cancel": "Cancel",
"close": "Close",
"actions": "Actions",
"selected": "selected"
}
}
+75 -18
View File
@@ -38,21 +38,14 @@
"name": "Nom",
"value": "Valeur",
"type": "Type",
"writable": "Modifiable",
"discovery": "Découverte",
"provisioning": "Provisionnement",
"criteria": "Critères",
"modelPaths": "Chemins du modèle",
"actions": "Actions",
"actionType": "Type d'action",
"modelPath": "Chemin du modèle",
"deviceIdentifier": "Identifiant équipement",
"version": "Version",
"policy": "Politique",
"enabled": "Activé",
"disabled": "Désactivé",
"tags": "Tags",
"compliance": "Conformité",
"updatedAt": "Modifié",
"search": "Rechercher",
"general": "Général",
@@ -80,7 +73,8 @@
"checksum": "Checksum",
"filterByProductClass": "Filtrer par classe produit…",
"saveChanges": "Enregistrer",
"hardwareVersion": "Version matérielle"
"hardwareVersion": "Version matérielle",
"devices": "Équipements"
},
"button": {
"refresh": "Rafraîchir",
@@ -92,9 +86,7 @@
"cancel": "Annuler",
"confirm": "Confirmer",
"fleetManagement": "Gestion de flotte ({count})",
"addCriteria": "Ajouter un critère",
"addModelPath": "Ajouter un chemin",
"addAction": "Ajouter une action",
"addParameter": "Ajouter un paramètre",
"previous": "Précédent",
"next": "Suivant"
@@ -105,8 +97,7 @@
},
"placeholder": {
"search": "Rechercher...",
"modelPath": "Ex: InternetGatewayDevice.DeviceInfo.",
"value": "Valeur"
"modelPath": "Ex: InternetGatewayDevice.DeviceInfo."
},
"options": {
"NI": "Non-immédiat (NI)",
@@ -172,7 +163,6 @@
"tabInventory": "Inventaire",
"tabParameters": "Paramètres",
"tabCompliance": "Conformité",
"tabDryRun": "Dry Run",
"tabHistory": "Historique",
"totalDevices": "Équipements correspondants",
"compliant": "Conforme",
@@ -180,10 +170,6 @@
"noMatchingDevices": "Aucun équipement ne correspond aux critères.",
"loadCompliance": "Charger le rapport de conformité",
"complianceError": "Erreur lors du chargement de la conformité.",
"diffs": "Différences",
"dryRunDesc": "Simulez l'application de cette configuration sur un équipement sans effectuer de modification réelle.",
"dryRunPlaceholder": "ex: AABBCC-Device-123",
"runDryRun": "Lancer",
"dryRunError": "Erreur lors du dry run.",
"firmwareSkipped": "firmware hors plage horaire",
"paramDiffs": "Différences de paramètres",
@@ -191,6 +177,77 @@
"targetValue": "Valeur cible",
"notSet": "(non défini)",
"noParamDiffs": "Aucune différence de paramètres.",
"firmwarePath": "Chemin de migration firmware"
"firmwarePath": "Chemin de migration firmware",
"parameter": "Paramètre",
"expectedValue": "Valeur attendue",
"noCriteria": "Aucun critère défini.",
"parameterConditions": "Conditions sur les paramètres",
"noInventoryPaths": "Aucun chemin d'inventaire défini.",
"noParameters": "Aucun paramètre défini.",
"noHistory": "Aucun historique disponible.",
"date": "Date",
"close": "Fermer"
},
"device": {
"identity": "Identité",
"identifier": "Identifiant",
"versions": "Versions",
"system": "Système",
"nextConnection": "Prochaine connexion",
"clientReferences": "Références client",
"deleteDevice": "Supprimer l'équipement",
"confirmDeleteMessage": "Confirmer la suppression de",
"exchanges": "Échanges TR-069",
"enableTracing": "Activer le tracing",
"tracingActive": "Tracing actif"
},
"trace": {
"tracedDevices": "Équipements tracés",
"description": "Activez le tracing sur un équipement puis cliquez sur « Voir » pour visualiser les échanges en direct.",
"deviceId": "Device ID",
"started": "Démarré",
"remove": "Supprimer",
"enableTracing": "Activer le tracing",
"editJson": "Éditer JSON",
"editConfig": "Configuration du tracing",
"formatJson": "Formater",
"saveApply": "Sauvegarder",
"expectedFormat": "Format attendu :",
"notTraced": "L'équipement n'est pas encore tracé.",
"noTracedDevices": "Aucun équipement tracé."
},
"sequence": {
"cpe": "CPE",
"acs": "ACS",
"emptyPost": "POST vide",
"session": "Session",
"goTo": "Aller à…",
"clear": "Effacer",
"clearExchanges": "Effacer les échanges",
"connected": "Connecté",
"paused": "Pause",
"disconnected": "Déconnecté",
"resumeScroll": "Reprendre le défilement automatique",
"allLoaded": "Tous les messages sont chargés",
"loading": "Chargement…",
"noExchanges": "Aucun échange enregistré. Les messages SOAP apparaîtront ici lors de la prochaine connexion du CPE.",
"confirmClear": "Êtes-vous sûr de vouloir supprimer tous les échanges enregistrés pour cet équipement ? Cette action est irréversible.",
"confirmClearTitle": "Effacer les échanges"
},
"inspector": {
"title": "Inspecteur",
"selectMessage": "Sélectionnez un message pour voir son contenu.",
"direction": "Direction",
"method": "Méthode",
"session": "Session",
"timestamp": "Horodatage",
"rawXml": "XML brut"
},
"common": {
"confirm": "Confirmer",
"cancel": "Annuler",
"close": "Fermer",
"actions": "Actions",
"selected": "sélectionné(s)"
}
}
+1 -1
View File
@@ -58,7 +58,7 @@ function sendFleetCommand() {
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</svg>
<span>
<strong>{{ devices?.length || 0 }}</strong> {{ t('labels.devices').toLowerCase() }} sélectionné(s)
<strong>{{ devices?.length || 0 }}</strong> {{ t('labels.devices').toLowerCase() }} {{ t('common.selected') }}
</span>
</div>
+10 -10
View File
@@ -218,14 +218,14 @@ async function resolveAllProviders() {
<!-- En-tête de la carte -->
<div class="px-3 pt-3 pb-2">
<span class="text-uppercase fw-bold" style="font-size:0.65rem;letter-spacing:.1em;color:#888">Identité</span>
<span class="text-uppercase fw-bold" style="font-size:0.65rem;letter-spacing:.1em;color:#888">{{ t('device.identity') }}</span>
</div>
<div class="px-3 pb-3 d-flex flex-column gap-2">
<!-- Zone 1 : Identifiant -->
<div class="rounded-3 px-3 py-3" style="background:#f6f7f9">
<div class="text-muted fw-bold text-uppercase mb-3" style="font-size:0.62rem;letter-spacing:.08em">Identifiant</div>
<div class="text-muted fw-bold text-uppercase mb-3" style="font-size:0.62rem;letter-spacing:.08em">{{ t('device.identifier') }}</div>
<div class="row g-3">
<div class="col-sm-6 col-lg-3">
<div class="text-muted fw-bold text-uppercase mb-1" style="font-size:0.62rem;letter-spacing:.06em">{{ t('labels.manufacturer') }}</div>
@@ -256,7 +256,7 @@ async function resolveAllProviders() {
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 1000 1000" class="text-muted flex-shrink-0">
<path d="M500 75.5c-234.72 0-425 190.28-425 425s190.28 425 425 425 425-190.28 425-425-190.28-425-425-425m0 100a75 75 0 1 1-75 75 75 75 0 0 1 75-75m125 600H400v-25.737l5.287-.167c15.555-.5 27.115-4.919 34.368-13.136 2.576-2.972 6.956-13.1 6.956-47.153v-227.2c0-32.392-4.523-44.6-8.311-49.141-5.359-6.391-16.516-10.147-33.17-11.156l-5.13-.31v-26h175v313.807c0 32.415 8.457 44.618 12.24 49.142 5.34 6.389 15.68 10.142 32.625 11.152l5.135.3v25.6Z" style="fill-rule:evenodd"/>
</svg>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">Versions</span>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">{{ t('device.versions') }}</span>
</div>
<div class="row g-3">
<div class="col-6">
@@ -278,7 +278,7 @@ async function resolveAllProviders() {
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 1000 1000" class="text-muted flex-shrink-0">
<path d="M827.568 500.5c0-110.61 120.715-26.921 64.516-162.6-56.231-135.681-82.391 8.823-160.606-69.384s66.333-104.365-69.36-160.562c-135.718-56.2-52.024 64.481-162.61 64.481-110.619 0-26.921-120.68-162.612-64.481s8.827 82.349-69.391 160.562-104.372-66.3-160.575 69.384c-56.23 135.679 64.486 51.99 64.486 162.6S50.7 527.419 106.93 663.1c56.2 135.681 82.359-8.823 160.575 69.352 78.218 78.214-66.3 104.4 69.391 160.6s51.993-64.483 162.612-64.483c110.586 0 26.892 120.678 162.61 64.483 135.693-56.2-8.854-82.384 69.36-160.6 78.215-78.175 104.375 66.329 160.606-69.352 56.199-135.681-64.516-51.99-64.516-162.6M500 301.015c110.457 0 200 89.537 200 199.985s-89.543 199.985-200 199.985S300 611.448 300 501s89.543-199.985 200-199.985" style="fill-rule:evenodd"/>
</svg>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">Système</span>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">{{ t('device.system') }}</span>
</div>
<div class="row g-3">
<div class="col-md-6">
@@ -341,7 +341,7 @@ async function resolveAllProviders() {
</svg>
</div>
<div>
<div class="text-muted fw-bold text-uppercase mb-1" style="font-size:0.62rem;letter-spacing:.06em">Prochaine connexion</div>
<div class="text-muted fw-bold text-uppercase mb-1" style="font-size:0.62rem;letter-spacing:.06em">{{ t('device.nextConnection') }}</div>
<div class="font-monospace small fw-medium">{{ estimatedNextConnection || '—' }}</div>
</div>
</div>
@@ -355,7 +355,7 @@ async function resolveAllProviders() {
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" fill="currentColor" viewBox="0 0 16 16" class="text-muted flex-shrink-0">
<path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6m2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0m4 8c0 1-1 1-1 1H3s-1 0-1-1 1-4 6-4 6 4m-1-.004c-.001-.246-.154-.986-.832-1.664C11.516 10.68 10.289 10 8 10s-3.516.68-4.168 1.332c-.678.678-.83 1.418-.832 1.664z"/>
</svg>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">Références client</span>
<span class="text-muted fw-bold text-uppercase" style="font-size:0.62rem;letter-spacing:.08em">{{ t('device.clientReferences') }}</span>
<button class="btn btn-link btn-sm p-0 ms-auto" @click="resolveAllProviders" :disabled="resolvingProvider !== null">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="currentColor" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
@@ -478,7 +478,7 @@ async function resolveAllProviders() {
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor" class="me-1" viewBox="0 0 16 16">
<path d="M13.5 1a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3M11 2.5a2.5 2.5 0 1 1 .603 1.628l-6.718 3.12a2.499 2.499 0 0 1 0 1.504l6.718 3.12a2.5 2.5 0 1 1-.488.876l-6.718-3.12a2.5 2.5 0 1 1 0-3.256l6.718-3.12A2.5 2.5 0 0 1 11 2.5"/>
</svg>
{{ tracingAction ? '…' : isTraced ? 'Tracing actif' : 'Activer le tracing' }}
{{ tracingAction ? '…' : isTraced ? t('device.tracingActive') : t('device.enableTracing') }}
</button>
<Link
:href="`/tr069-trace?device=${encodeURIComponent(device.identifier)}`"
@@ -488,17 +488,17 @@ async function resolveAllProviders() {
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0"/>
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8m8 3.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7"/>
</svg>
Échanges TR-069
{{ t('device.exchanges') }}
</Link>
<button class="btn btn-outline-danger btn-sm ms-auto" @click="showDeleteConfirm = true">
Supprimer l'équipement
{{ t('device.deleteDevice') }}
</button>
</div>
<!-- Confirmation suppression -->
<div v-if="showDeleteConfirm" class="alert alert-danger d-flex align-items-center gap-3 mt-3 mb-0">
<span>Confirmer la suppression de <strong>{{ device.identifier }}</strong> ?</span>
<span>{{ t('device.confirmDeleteMessage') }} <strong>{{ device.identifier }}</strong> ?</span>
<button class="btn btn-danger btn-sm" @click="deleteDevice">Confirmer</button>
<button class="btn btn-outline-secondary btn-sm" @click="showDeleteConfirm = false">Annuler</button>
</div>
+10 -8
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import TitleBar from '~/components/TitleBar.vue'
import SequenceViewer from '~/components/SequenceViewer.vue'
import InspectorPanel from '~/components/InspectorPanel.vue'
@@ -15,6 +16,7 @@ type TraceEntry = {
}
const tracedDevices = ref<TraceEntry[]>([])
const { t } = useI18n()
const loadingEntries = ref(false)
const removingDeviceId = ref<string | null>(null)
const entriesError = ref('')
@@ -204,8 +206,8 @@ function formatJson() {
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h2 class="h4 mb-1">Traced devices</h2>
<p class="text-muted small mb-0">Activez le tracing sur un équipement puis cliquez sur "Voir" pour visualiser les échanges en direct.</p>
<h2 class="h4 mb-1">{{ t('trace.tracedDevices') }}</h2>
<p class="text-muted small mb-0">{{ t('trace.description') }}</p>
</div>
<div class="d-flex align-items-center gap-2">
<span class="badge bg-dark">{{ tracedDevices.length }}</span>
@@ -221,12 +223,12 @@ function formatJson() {
<div v-if="entriesError" class="alert alert-danger" role="alert">{{ entriesError }}</div>
<div v-if="actionError" class="alert alert-warning" role="alert">{{ actionError }}</div>
<div v-if="pendingDeviceId && !tracedDeviceIds.has(pendingDeviceId)" class="alert alert-info d-flex align-items-center gap-3 mb-3">
<span>L'équipement <strong class="font-monospace">{{ pendingDeviceId }}</strong> n'est pas encore tracé.</span>
<span>L'équipement <strong class="font-monospace">{{ pendingDeviceId }}</strong> {{ t('trace.notTraced') }}</span>
<button
class="btn btn-sm btn-primary ms-auto"
@click="enableTrace(pendingDeviceId)"
>
Activer le tracing
{{ t('trace.enableTracing') }}
</button>
<button class="btn btn-sm btn-outline-secondary" @click="pendingDeviceId = null">✕</button>
</div>
@@ -237,12 +239,12 @@ function formatJson() {
<table class="table table-sm table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th>Device ID</th>
<th>{{ t('trace.deviceId') }}</th>
<th>Manufacturer</th>
<th>Product Class</th>
<th>Serial Number</th>
<th>OUI</th>
<th>Started</th>
<th>{{ t('trace.started') }}</th>
<th class="text-end">Actions</th>
</tr>
</thead>
@@ -272,7 +274,7 @@ function formatJson() {
</tr>
<tr v-if="tracedDevices.length === 0">
<td colspan="7" class="text-center text-muted py-4">
Aucun équipement tracé. Utilisez "Edit JSON" pour en ajouter ou activez le tracing depuis la fiche d'équipement.
{{ t('trace.noTracedDevices') }}
</td>
</tr>
</tbody>
@@ -341,7 +343,7 @@ function formatJson() {
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 id="jsonEditorModalLabel" class="modal-title">Edit Trace Configuration</h5>
<h5 id="jsonEditorModalLabel" class="modal-title">{{ t('trace.editConfig') }}</h5>
<button type="button" class="btn-close" aria-label="Close" @click="closeJsonEditor"></button>
</div>
<div class="modal-body">