- Docker infrastructure (Dockerfile, docker-compose.yml, genieacs-sim) - AdonisJS v7 app with Inertia.js + Vue 3 - MongoDB integration - CWMP/TR-069 state machine engine - Configuration engine with compliance tracking - Branding: ADAM ACS (favicon, navbar, locales) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
163 lines
6.7 KiB
Vue
163 lines
6.7 KiB
Vue
<script setup lang="ts">
|
|
import { ref, computed } from 'vue'
|
|
import { router, Link } from '@inertiajs/vue3'
|
|
import { useI18n } from 'vue-i18n'
|
|
import TitleBar from '../components/TitleBar.vue'
|
|
|
|
const { t } = useI18n()
|
|
|
|
const { commands, filters } = defineProps<{
|
|
commands: any[]
|
|
filters: { device: string; status: string; action: string; mode: string }
|
|
}>()
|
|
|
|
const localFilters = ref({
|
|
device: filters.device || '',
|
|
status: filters.status || '',
|
|
action: filters.action || '',
|
|
mode: filters.mode || '',
|
|
})
|
|
|
|
const statuses = ['PENDING', 'WAITING', 'SENT', 'COMPLETED', 'FAILED']
|
|
const actions = [
|
|
'GET_PARAMETER_VALUES', 'GET_PARAMETER_NAMES', 'GET_PARAMETER_ATTRIBUTES',
|
|
'SET_PARAMETER_VALUES', 'SET_PARAMETER_ATTRIBUTES',
|
|
'DOWNLOAD', 'UPLOAD', 'REBOOT', 'FACTORY_RESET', 'SCHEDULE_INFORM', 'REFRESH',
|
|
]
|
|
const modes = ['NI', 'I']
|
|
|
|
const hasActiveFilters = computed(() => {
|
|
return Object.values(localFilters.value).some((v) => v !== '')
|
|
})
|
|
|
|
function applyFilters() {
|
|
const params: Record<string, string> = {}
|
|
if (localFilters.value.device) params.device = localFilters.value.device
|
|
if (localFilters.value.status) params.status = localFilters.value.status
|
|
if (localFilters.value.action) params.action = localFilters.value.action
|
|
if (localFilters.value.mode) params.mode = localFilters.value.mode
|
|
|
|
const qs = new URLSearchParams(params).toString()
|
|
router.visit(`/commands${qs ? '?' + qs : ''}`, { preserveState: false })
|
|
}
|
|
|
|
function clearFilters() {
|
|
localFilters.value = { device: '', status: '', action: '', mode: '' }
|
|
router.visit('/commands', { preserveState: false })
|
|
}
|
|
|
|
function removeFilter(key: keyof typeof localFilters.value) {
|
|
localFilters.value[key] = ''
|
|
applyFilters()
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<TitleBar section="commands" />
|
|
|
|
<div class="container-xxl py-4">
|
|
<!-- Filters bar -->
|
|
<div class="card border-0 shadow-sm mb-3">
|
|
<div class="card-body p-3">
|
|
<div class="row g-2 align-items-end">
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted mb-1">{{ t('labels.deviceIdentifier') }}</label>
|
|
<input
|
|
type="text"
|
|
class="form-control form-control-sm font-monospace"
|
|
v-model="localFilters.device"
|
|
:placeholder="t('placeholder.search')"
|
|
@keyup.enter="applyFilters"
|
|
/>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label small text-muted mb-1">{{ t('labels.status') }}</label>
|
|
<select class="form-select form-select-sm" v-model="localFilters.status" @change="applyFilters">
|
|
<option value="">—</option>
|
|
<option v-for="s in statuses" :key="s" :value="s">{{ s }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label small text-muted mb-1">{{ t('labels.action') }}</label>
|
|
<select class="form-select form-select-sm" v-model="localFilters.action" @change="applyFilters">
|
|
<option value="">—</option>
|
|
<option v-for="a in actions" :key="a" :value="a">{{ a }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<label class="form-label small text-muted mb-1">{{ t('labels.mode') }}</label>
|
|
<select class="form-select form-select-sm" v-model="localFilters.mode" @change="applyFilters">
|
|
<option value="">—</option>
|
|
<option v-for="m in modes" :key="m" :value="m">{{ m }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2 d-flex gap-1">
|
|
<button class="btn btn-primary btn-sm" @click="applyFilters">Filtrer</button>
|
|
<button v-if="hasActiveFilters" class="btn btn-outline-secondary btn-sm" @click="clearFilters">✕</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active filters badges -->
|
|
<div v-if="hasActiveFilters" class="mb-3 d-flex gap-2 flex-wrap">
|
|
<span v-if="filters.device" class="badge bg-light text-dark border d-flex align-items-center gap-1">
|
|
Équipement: <strong class="font-monospace">{{ filters.device }}</strong>
|
|
<button class="btn-close btn-close-sm ms-1" style="font-size: 0.6rem;" @click="removeFilter('device')"></button>
|
|
</span>
|
|
<span v-if="filters.status" class="badge bg-light text-dark border d-flex align-items-center gap-1">
|
|
Statut: <strong>{{ filters.status }}</strong>
|
|
<button class="btn-close btn-close-sm ms-1" style="font-size: 0.6rem;" @click="removeFilter('status')"></button>
|
|
</span>
|
|
<span v-if="filters.action" class="badge bg-light text-dark border d-flex align-items-center gap-1">
|
|
Action: <strong>{{ filters.action }}</strong>
|
|
<button class="btn-close btn-close-sm ms-1" style="font-size: 0.6rem;" @click="removeFilter('action')"></button>
|
|
</span>
|
|
<span v-if="filters.mode" class="badge bg-light text-dark border d-flex align-items-center gap-1">
|
|
Mode: <strong>{{ filters.mode }}</strong>
|
|
<button class="btn-close btn-close-sm ms-1" style="font-size: 0.6rem;" @click="removeFilter('mode')"></button>
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Results count -->
|
|
<p class="text-muted small mb-2">{{ commands.length }} résultat(s)</p>
|
|
|
|
<!-- Table -->
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-hover table-striped">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>{{ t('labels.deviceIdentifier') }}</th>
|
|
<th>{{ t('labels.action') }}</th>
|
|
<th>{{ t('labels.status') }}</th>
|
|
<th>{{ t('labels.mode') }}</th>
|
|
<th>{{ t('labels.priority') }}</th>
|
|
<th>{{ t('labels.creationDate') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="cmd in commands" :key="cmd._id">
|
|
<td class="font-monospace small">{{ cmd.deviceIdentifier }}</td>
|
|
<td class="small">{{ cmd.action }}</td>
|
|
<td>
|
|
<span class="badge" :class="{
|
|
'bg-success': cmd.status === 'COMPLETED',
|
|
'bg-danger': cmd.status === 'FAILED',
|
|
'bg-warning text-dark': cmd.status === 'PENDING',
|
|
'bg-info': cmd.status === 'SENT',
|
|
'bg-secondary': cmd.status === 'WAITING',
|
|
}">{{ cmd.status }}</span>
|
|
</td>
|
|
<td class="small">{{ cmd.mode }}</td>
|
|
<td class="small">{{ cmd.priority }}</td>
|
|
<td class="small">{{ cmd.createdAt ? new Date(cmd.createdAt).toLocaleString() : '' }}</td>
|
|
</tr>
|
|
<tr v-if="commands.length === 0">
|
|
<td colspan="6" class="text-center text-muted py-3">Aucune commande trouvée.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</template>
|