feat: infrastructure assets management with warranty tracking and EAN lookup integration

This commit is contained in:
jeremy bayse
2026-04-09 21:51:43 +02:00
parent 3544c77bd1
commit b28c56c94c
46 changed files with 2961 additions and 414 deletions

View File

@@ -5,6 +5,7 @@ import { Head, Link, useForm } from '@inertiajs/vue3'
const props = defineProps({
services: Array,
fournisseurs: Array,
communes: Array,
statuts: Object,
})
@@ -12,6 +13,7 @@ const form = useForm({
titre: '',
description: '',
fournisseur_id: '',
commune_id: '',
service_id: props.services.length === 1 ? props.services[0].id : '',
date_debut: '',
date_echeance: '',
@@ -79,6 +81,15 @@ function submit() {
class="w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-500" />
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-700">Ville / Commune</label>
<select v-model="form.commune_id"
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none">
<option value=""> Non défini </option>
<option v-for="c in communes" :key="c.id" :value="c.id">{{ c.nom }}</option>
</select>
</div>
<div class="sm:col-span-2">
<label class="mb-1 block text-sm font-medium text-gray-700">Description</label>
<textarea v-model="form.description" rows="3"

View File

@@ -6,6 +6,7 @@ const props = defineProps({
contrat: Object,
services: Array,
fournisseurs: Array,
communes: Array,
statuts: Object,
})
@@ -13,6 +14,7 @@ const form = useForm({
titre: props.contrat.titre ?? '',
description: props.contrat.description ?? '',
fournisseur_id: props.contrat.fournisseur_id ?? '',
commune_id: props.contrat.commune_id ?? '',
service_id: props.contrat.service_id ?? (props.services.length === 1 ? props.services[0].id : ''),
date_debut: props.contrat.date_debut ?? '',
date_echeance: props.contrat.date_echeance ?? '',
@@ -80,6 +82,15 @@ function submit() {
class="w-full rounded-lg border border-gray-200 bg-gray-50 px-3 py-2 text-sm text-gray-500" />
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-700">Ville / Commune</label>
<select v-model="form.commune_id"
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-blue-500 focus:outline-none">
<option value=""> Non défini </option>
<option v-for="c in communes" :key="c.id" :value="c.id">{{ c.nom }}</option>
</select>
</div>
<div class="sm:col-span-2">
<label class="mb-1 block text-sm font-medium text-gray-700">Description</label>
<textarea v-model="form.description" rows="3"

View File

@@ -9,13 +9,21 @@ const props = defineProps({
contrats: Object,
services: Array,
fournisseurs: Array,
communes: Array,
filters: Object,
statuts: Object,
})
const page = usePage()
const filters = reactive({ ...props.filters })
const filters = reactive({
search: '',
service_id: '',
fournisseur_id: '',
commune_id: '',
statut: '',
...props.filters
})
const deleteTarget = ref(null)
function applyFilters() {
@@ -93,6 +101,12 @@ const statutColors = {
<option value="">Tous les statuts</option>
<option v-for="(label, key) in statuts" :key="key" :value="key">{{ label }}</option>
</select>
<select v-model="filters.commune_id" @change="applyFilters"
class="rounded-lg border border-gray-300 px-3 py-2 text-sm focus:border-indigo-500 focus:outline-none">
<option value="">Toutes communes</option>
<option v-for="c in communes" :key="c.id" :value="c.id">{{ c.nom }}</option>
</select>
</div>
<div class="mt-3 flex justify-end">
@@ -111,6 +125,7 @@ const statutColors = {
<tr>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Titre</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Service</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Ville</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Fournisseur</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Statut</th>
<th class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Date début</th>
@@ -128,6 +143,7 @@ const statutColors = {
</Link>
</td>
<td class="px-4 py-3 whitespace-nowrap text-gray-600">{{ cnt.service?.nom }}</td>
<td class="px-4 py-3 whitespace-nowrap text-gray-600">{{ cnt.commune?.nom ?? '—' }}</td>
<td class="px-4 py-3 whitespace-nowrap text-gray-600">{{ cnt.fournisseur?.nom ?? '—' }}</td>
<td class="px-4 py-3">
<span :class="['inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium', statutColors[cnt.statut]]">

View File

@@ -71,6 +71,10 @@ const statutColors = {
<p class="text-xs text-gray-500">Service concerné</p>
<p class="mt-0.5 font-medium text-gray-900">{{ contrat.service?.nom ?? '—' }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Ville / Commune</p>
<p class="mt-0.5 font-medium text-gray-900">{{ contrat.commune?.nom ?? '—' }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Fournisseur</p>
<p class="mt-0.5 font-medium text-gray-900">{{ contrat.fournisseur?.nom ?? '—' }}</p>
@@ -101,6 +105,20 @@ const statutColors = {
</div>
</div>
</div>
<!-- Licences associées -->
<div v-if="contrat.licences?.length" class="mt-6 rounded-xl bg-white p-5 shadow-sm border border-gray-100">
<h2 class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500">Licences & Abonnements liés</h2>
<div class="space-y-3">
<div v-for="lic in contrat.licences" :key="lic.id" class="flex items-center justify-between p-3 rounded-lg border border-gray-50 hover:bg-gray-50 transition-colors">
<div>
<div class="font-medium text-gray-900">{{ lic.nom }}</div>
<div class="text-xs text-gray-500">{{ lic.nombre_sieges_utilises }} / {{ lic.nombre_sieges_total }} sièges utilisés</div>
</div>
<Link :href="route('licences.edit', lic.id)" class="text-xs font-semibold text-indigo-600 hover:text-indigo-800">Afficher</Link>
</div>
</div>
</div>
</div>
<!-- Colonne droite (Pièces jointes) -->