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

@@ -0,0 +1,151 @@
<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue'
import { Head, Link } from '@inertiajs/vue3'
const props = defineProps({
asset: Object,
})
function formatDate(d) {
if (!d) return '—'
return new Intl.DateTimeFormat('fr-FR').format(new Date(d))
}
const statutLabels = {
en_service: 'En service',
hors_service: 'Hors service',
en_reparation: 'En réparation',
stock: 'En stock',
}
const statutColors = {
en_service: 'bg-green-100 text-green-800',
hors_service: 'bg-red-100 text-red-800',
en_reparation: 'bg-orange-100 text-orange-800',
stock: 'bg-blue-100 text-blue-800',
}
function checkWarranty(date) {
if (!date) return 'text-gray-400'
const today = new Date()
const exp = new Date(date)
if (exp < today) return 'text-red-600 font-bold'
const diff = (exp - today) / (1000 * 60 * 60 * 24)
if (diff <= 30) return 'text-orange-600 font-bold'
return 'text-gray-600'
}
</script>
<template>
<Head :title="asset.nom" />
<AuthenticatedLayout>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<Link :href="route('assets.index')" class="text-gray-400 hover:text-gray-600 transition-colors">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</Link>
<div>
<h1 class="text-xl font-semibold text-gray-900">{{ asset.nom }}</h1>
<p class="text-sm text-gray-500">{{ asset.type }} - {{ asset.marque }} {{ asset.modele }}</p>
</div>
</div>
<!-- Action Buttons: Edit -->
<Link :href="route('assets.edit', asset.id)"
class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 transition-colors">
Modifier
</Link>
</div>
</template>
<div class="grid gap-6 lg:grid-cols-3">
<div class="space-y-6 lg:col-span-2">
<!-- Informations de base -->
<div class="rounded-xl bg-white p-6 shadow-sm border border-gray-100">
<h2 class="mb-5 text-sm font-semibold uppercase tracking-wide text-gray-500">Détails du matériel</h2>
<div class="grid gap-4 sm:grid-cols-2">
<div>
<p class="text-xs text-gray-500">Nom / Hostname</p>
<p class="mt-0.5 font-medium text-gray-900">{{ asset.nom }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Type de matériel</p>
<p class="mt-0.5 font-medium text-gray-900">{{ asset.type }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Marque & Modèle</p>
<p class="mt-0.5 font-medium text-gray-900">{{ asset.marque || '—' }} {{ asset.modele || '' }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Code EAN/UPC</p>
<p class="mt-0.5 font-mono text-sm text-gray-900">{{ asset.code_ean || '—' }}</p>
</div>
<div>
<p class="text-xs text-gray-500 font-bold">Numéro de série / SN</p>
<p class="mt-0.5 font-mono text-sm text-gray-900">{{ asset.numero_serie || '—' }}</p>
</div>
<div>
<p class="text-xs text-gray-500 border-t pt-4 mt-2">Statut actuel</p>
<span :class="['mt-2 inline-block px-2 py-0.5 rounded-full text-xs font-medium', statutColors[asset.statut]]">
{{ statutLabels[asset.statut] }}
</span>
</div>
</div>
</div>
<!-- Notes / Config -->
<div v-if="asset.notes" class="rounded-xl bg-white p-6 shadow-sm border border-gray-100">
<h2 class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500">Notes & Configuration</h2>
<div class="whitespace-pre-wrap text-sm text-gray-700">{{ asset.notes }}</div>
</div>
</div>
<div class="space-y-6 lg:col-span-1">
<!-- Localisation -->
<div class="rounded-xl bg-white p-6 shadow-sm border border-gray-100">
<h2 class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500">Localisation</h2>
<div class="space-y-4">
<div>
<p class="text-xs text-gray-500">Ville / Commune</p>
<p class="mt-0.5 font-medium text-gray-900">{{ asset.commune?.nom || 'Infrastructure Globale (Agglo)' }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Emplacement précis</p>
<p class="mt-0.5 font-medium text-gray-900">{{ asset.emplacement || '—' }}</p>
</div>
</div>
</div>
<!-- Cycle de vie & Garantie -->
<div class="rounded-xl bg-white p-6 shadow-sm border border-gray-100">
<h2 class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500">Cycle de vie</h2>
<div class="space-y-4">
<div>
<p class="text-xs text-gray-500">Date d'achat</p>
<p class="mt-0.5 font-medium text-gray-900">{{ formatDate(asset.date_achat) }}</p>
</div>
<div>
<p class="text-xs text-gray-500">Fin de garantie (constructeur)</p>
<p class="mt-0.5 font-medium" :class="checkWarranty(asset.date_fin_garantie)">
{{ formatDate(asset.date_fin_garantie) }}
<span v-if="checkWarranty(asset.date_fin_garantie).includes('red')" class="ml-1" title="Expirée">⚠️ Expirée</span>
<span v-else-if="checkWarranty(asset.date_fin_garantie).includes('orange')" class="ml-1" title="Proche échéance">⏳ À renouveler</span>
</p>
</div>
</div>
</div>
<!-- Commande associée -->
<div v-if="asset.commande" class="rounded-xl bg-white p-6 shadow-sm border border-gray-100">
<h2 class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500">Commande associée</h2>
<Link :href="route('commandes.show', asset.commande.id)" class="block rounded-lg border border-gray-100 bg-gray-50 p-4 hover:bg-gray-100 transition-colors">
<div class="font-medium text-indigo-600">{{ asset.commande.numero_commande }}</div>
<div class="mt-1 text-xs text-gray-600">{{ asset.commande.objet }}</div>
</Link>
</div>
</div>
</div>
</AuthenticatedLayout>
</template>