feat: add Devis and BesoinInformatique modules

- Devis: CRUD complet, génération PDF (dompdf), paramètres (DevisSetting)
- BesoinInformatique: CRUD complet avec gestion matériels/licences
- Migrations pour les 3 nouvelles tables
- Navigation mise à jour dans AuthenticatedLayout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mrKamoo
2026-06-19 14:46:16 +02:00
co-authored by Claude Sonnet 4.6
parent 030d76af53
commit 7312c7640b
24 changed files with 2887 additions and 4 deletions
@@ -0,0 +1,219 @@
<script setup>
import { Head, useForm, Link } from '@inertiajs/vue3';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
const form = useForm({
collectivite: '',
nom_referent: '',
descriptif_projet: '',
materiels_support: [
{ type: '', agent: '' }
],
licences_bureautique: [
{ licence: '', type: '', destination: '', quantite: 1 }
],
equipements_reseaux: '',
cablage_reseau: '',
telephonie_ip: '',
nouveau_batiment: '',
logiciels_metiers: '',
licences_projets: [
{ action: '', nom: '', domaine: '', quantite: 1 }
],
});
const submit = () => {
form.post(route('besoins-informatiques.store'));
};
const addMateriel = () => form.materiels_support.push({ type: '', agent: '' });
const removeMateriel = (index) => form.materiels_support.splice(index, 1);
const addLicenceBureautique = () => form.licences_bureautique.push({ licence: '', type: '', destination: '', quantite: 1 });
const removeLicenceBureautique = (index) => form.licences_bureautique.splice(index, 1);
const addLicenceProjet = () => form.licences_projets.push({ action: '', nom: '', domaine: '', quantite: 1 });
const removeLicenceProjet = (index) => form.licences_projets.splice(index, 1);
</script>
<template>
<Head title="Nouveau Besoin Informatique" />
<AuthenticatedLayout>
<template #header>
<div class="flex items-center justify-between">
<h2 class="text-xl font-bold leading-tight text-slate-800 dark:text-slate-200">
Saisir un besoin informatique (Mutualisation)
</h2>
<Link
:href="route('besoins-informatiques.index')"
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-850 dark:hover:bg-slate-800 transition-colors"
>
Retour
</Link>
</div>
</template>
<div class="py-6">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<form @submit.prevent="submit" class="space-y-6">
<!-- Entête du budget -->
<div class="bg-[#000066] text-white rounded-xl shadow-sm overflow-hidden p-6 text-center">
<h1 class="text-lg font-bold tracking-wider uppercase">PRÉPARATION BUDGET 2027</h1>
<p class="text-sm font-semibold mt-1">FICHE Synthèse des Besoins Informatiques MUTUALISATION</p>
</div>
<!-- Informations Générales -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden">
<div class="p-6 space-y-6">
<h3 class="text-md font-bold text-slate-800 dark:text-slate-200 border-b border-slate-100 dark:border-slate-850 pb-3">Informations Générales</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Collectivité :</label>
<input v-model="form.collectivite" type="text" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200" required>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Nom du référent :</label>
<input v-model="form.nom_referent" type="text" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200" required>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Descriptif / objectif du projet :</label>
<textarea v-model="form.descriptif_projet" rows="3" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
</div>
</div>
</div>
<!-- Service Support -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#002060] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-white">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Service Support et Relations Usagers</h3>
<p class="text-xxs opacity-80 mt-2 hidden md:block">Contact : Frédéric FERNANDEZ fredericfernandez@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-6">
<!-- Matériel -->
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Type de matériel (PC / Portables / Ecran / Tel...)</h4>
<button type="button" @click="addMateriel" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.materiels_support" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-2 gap-2">
<input v-model="item.type" type="text" placeholder="Type de matériel" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.agent" type="text" placeholder="Nom de l'agent / Service" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeMateriel(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
<!-- Licences -->
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Besoins de Licences Bureautique</h4>
<button type="button" @click="addLicenceBureautique" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.licences_bureautique" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-4 gap-2">
<input v-model="item.licence" type="text" placeholder="Licence bureautique" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.type" type="text" placeholder="Type de Licence" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.destination" type="text" placeholder="PC neuf ou existant" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.quantite" type="number" min="1" placeholder="Quantité" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeLicenceBureautique(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Infrastructures -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#2f5597] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-white">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Infrastructures Système et Télécommunications</h3>
<p class="text-xxs opacity-80 mt-2 hidden md:block">Contact : Jérémy BAYSE 04.99.41.33.18. jeremybayse@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-4">
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Équipements réseaux - serveur :</label>
<input v-model="form.equipements_reseaux" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Travaux de câblage réseau :</label>
<input v-model="form.cablage_reseau" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Projet dans le domaine téléphonie IP :</label>
<input v-model="form.telephonie_ip" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-rose-600 dark:text-rose-450 mb-1">Nouvelle structure ou bâtiment planifié en 2027 et nécessitant un raccordement informatique :</label>
<textarea v-model="form.nouveau_batiment" rows="2" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
</div>
</div>
<!-- Etudes Projets -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#8faadc] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-slate-900">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Etudes Projets et Dématérialisation</h3>
<p class="text-xxs opacity-85 mt-2 hidden md:block">Contact projets logiciels : Gaëtan PRULHIERE gaetanprulhiere@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-6">
<div>
<label class="block text-xs font-bold text-slate-750 dark:text-slate-300 mb-1">Projet d'acquisition de logiciels métiers ou projet de dématérialisation :</label>
<p class="text-xxs text-slate-500 dark:text-slate-400 italic mb-2">Toute demande devra intégrer un chiffrage à l'appui de la demande</p>
<textarea v-model="form.logiciels_metiers" rows="3" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Besoins ou Renouvellement de Licences</h4>
<button type="button" @click="addLicenceProjet" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.licences_projets" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-4 gap-2">
<input v-model="item.action" type="text" placeholder="Renouvellement ou achat" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.nom" type="text" placeholder="Nom de la Licence (« Adobe », « AutoCAD », GED...)" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.domaine" type="text" placeholder="Domaine (Enfance, RH, Police...)" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.quantite" type="number" min="1" placeholder="Quantité" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeLicenceProjet(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Barre d'actions -->
<div class="flex items-center justify-end gap-3 pt-6 border-t border-slate-200 dark:border-slate-800">
<Link
:href="route('besoins-informatiques.index')"
class="inline-flex items-center px-4 py-2 border border-slate-300 text-sm font-semibold rounded-lg text-slate-700 bg-white hover:bg-slate-50 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-850 dark:hover:bg-slate-800 transition-colors shadow-sm"
>
Annuler
</Link>
<button
type="submit"
:disabled="form.processing"
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-white bg-sky-600 hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-sky-500 transition-colors disabled:opacity-50"
>
Enregistrer la Fiche
</button>
</div>
</form>
</div>
</div>
</AuthenticatedLayout>
</template>
@@ -0,0 +1,231 @@
<script setup>
import { Head, useForm, Link } from '@inertiajs/vue3';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { onMounted } from 'vue';
const props = defineProps({
besoin: Object,
});
const form = useForm({
collectivite: props.besoin.collectivite || '',
nom_referent: props.besoin.nom_referent || '',
descriptif_projet: props.besoin.descriptif_projet || '',
materiels_support: props.besoin.materiels_support || [{ type: '', agent: '' }],
licences_bureautique: props.besoin.licences_bureautique || [{ licence: '', type: '', destination: '', quantite: 1 }],
equipements_reseaux: props.besoin.equipements_reseaux || '',
cablage_reseau: props.besoin.cablage_reseau || '',
telephonie_ip: props.besoin.telephonie_ip || '',
nouveau_batiment: props.besoin.nouveau_batiment || '',
logiciels_metiers: props.besoin.logiciels_metiers || '',
licences_projets: props.besoin.licences_projets || [{ action: '', nom: '', domaine: '', quantite: 1 }],
});
// Sécurités pour éviter que le formulaire crashe si les arrays sont vides depuis la DB
onMounted(() => {
if (!form.materiels_support || form.materiels_support.length === 0) {
form.materiels_support = [{ type: '', agent: '' }];
}
if (!form.licences_bureautique || form.licences_bureautique.length === 0) {
form.licences_bureautique = [{ licence: '', type: '', destination: '', quantite: 1 }];
}
if (!form.licences_projets || form.licences_projets.length === 0) {
form.licences_projets = [{ action: '', nom: '', domaine: '', quantite: 1 }];
}
});
const submit = () => {
form.put(route('besoins-informatiques.update', props.besoin.id));
};
const addMateriel = () => form.materiels_support.push({ type: '', agent: '' });
const removeMateriel = (index) => form.materiels_support.splice(index, 1);
const addLicenceBureautique = () => form.licences_bureautique.push({ licence: '', type: '', destination: '', quantite: 1 });
const removeLicenceBureautique = (index) => form.licences_bureautique.splice(index, 1);
const addLicenceProjet = () => form.licences_projets.push({ action: '', nom: '', domaine: '', quantite: 1 });
const removeLicenceProjet = (index) => form.licences_projets.splice(index, 1);
</script>
<template>
<Head title="Modifier le Besoin Informatique" />
<AuthenticatedLayout>
<template #header>
<div class="flex items-center justify-between">
<h2 class="text-xl font-bold leading-tight text-slate-800 dark:text-slate-200">
Modifier la fiche de synthèse (Mutualisation)
</h2>
<Link
:href="route('besoins-informatiques.index')"
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-850 dark:hover:bg-slate-800 transition-colors"
>
Retour
</Link>
</div>
</template>
<div class="py-6">
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8">
<form @submit.prevent="submit" class="space-y-6">
<!-- Entête du budget -->
<div class="bg-[#000066] text-white rounded-xl shadow-sm overflow-hidden p-6 text-center">
<h1 class="text-lg font-bold tracking-wider uppercase">PRÉPARATION BUDGET 2027</h1>
<p class="text-sm font-semibold mt-1">FICHE Synthèse des Besoins Informatiques MUTUALISATION</p>
</div>
<!-- Informations Générales -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden">
<div class="p-6 space-y-6">
<h3 class="text-md font-bold text-slate-800 dark:text-slate-200 border-b border-slate-100 dark:border-slate-850 pb-3">Informations Générales</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Collectivité :</label>
<input v-model="form.collectivite" type="text" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200" required>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Nom du référent :</label>
<input v-model="form.nom_referent" type="text" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200" required>
</div>
<div class="md:col-span-2">
<label class="block text-sm font-bold text-slate-700 dark:text-slate-300 mb-1">Descriptif / objectif du projet :</label>
<textarea v-model="form.descriptif_projet" rows="3" class="w-full text-sm rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
</div>
</div>
</div>
<!-- Service Support -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#002060] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-white">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Service Support et Relations Usagers</h3>
<p class="text-xxs opacity-80 mt-2 hidden md:block">Contact : Frédéric FERNANDEZ fredericfernandez@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-6">
<!-- Matériel -->
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Type de matériel (PC / Portables / Ecran / Tel...)</h4>
<button type="button" @click="addMateriel" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.materiels_support" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-2 gap-2">
<input v-model="item.type" type="text" placeholder="Type de matériel" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.agent" type="text" placeholder="Nom de l'agent / Service" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeMateriel(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
<!-- Licences -->
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Besoins de Licences Bureautique</h4>
<button type="button" @click="addLicenceBureautique" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.licences_bureautique" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-4 gap-2">
<input v-model="item.licence" type="text" placeholder="Licence bureautique" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.type" type="text" placeholder="Type de Licence" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.destination" type="text" placeholder="PC neuf ou existant" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.quantite" type="number" min="1" placeholder="Quantité" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeLicenceBureautique(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Infrastructures -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#2f5597] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-white">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Infrastructures Système et Télécommunications</h3>
<p class="text-xxs opacity-80 mt-2 hidden md:block">Contact : Jérémy BAYSE 04.99.41.33.18. jeremybayse@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-4">
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Équipements réseaux - serveur :</label>
<input v-model="form.equipements_reseaux" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Travaux de câblage réseau :</label>
<input v-model="form.cablage_reseau" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Projet dans le domaine téléphonie IP :</label>
<input v-model="form.telephonie_ip" type="text" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<div>
<label class="block text-xs font-bold text-rose-600 dark:text-rose-450 mb-1">Nouvelle structure ou bâtiment planifié en 2027 et nécessitant un raccordement informatique :</label>
<textarea v-model="form.nouveau_batiment" rows="2" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
</div>
</div>
<!-- Etudes Projets -->
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden flex flex-col md:flex-row">
<div class="bg-[#8faadc] p-6 md:w-56 flex-shrink-0 flex flex-col justify-center text-slate-900">
<h3 class="text-md font-bold tracking-wide text-center md:text-left">Etudes Projets et Dématérialisation</h3>
<p class="text-xxs opacity-85 mt-2 hidden md:block">Contact projets logiciels : Gaëtan PRULHIERE gaetanprulhiere@beziers-mediterranee.fr</p>
</div>
<div class="p-6 flex-1 space-y-6">
<div>
<label class="block text-xs font-bold text-slate-750 dark:text-slate-300 mb-1">Projet d'acquisition de logiciels métiers ou projet de dématérialisation :</label>
<p class="text-xxs text-slate-500 dark:text-slate-400 italic mb-2">Toute demande devra intégrer un chiffrage à l'appui de la demande</p>
<textarea v-model="form.logiciels_metiers" rows="3" class="w-full text-xs rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200"></textarea>
</div>
<div>
<div class="flex items-center justify-between mb-3 border-b border-slate-100 dark:border-slate-850 pb-2">
<h4 class="text-sm font-bold text-slate-850 dark:text-slate-250">Besoins ou Renouvellement de Licences</h4>
<button type="button" @click="addLicenceProjet" class="text-xs font-semibold text-sky-600 hover:text-sky-700 bg-sky-50 dark:bg-sky-950/40 px-2 py-1 rounded-md border border-sky-100 dark:border-sky-900/60">+ Ajouter</button>
</div>
<div class="space-y-3">
<div v-for="(item, index) in form.licences_projets" :key="index" class="flex items-start gap-2">
<div class="flex-1 grid grid-cols-1 sm:grid-cols-4 gap-2">
<input v-model="item.action" type="text" placeholder="Renouvellement ou achat" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.nom" type="text" placeholder="Nom de la Licence (« Adobe », « AutoCAD », GED...)" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.domaine" type="text" placeholder="Domaine (Enfance, RH, Police...)" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
<input v-model="item.quantite" type="number" min="1" placeholder="Quantité" class="text-xs w-full rounded-lg border-slate-300 shadow-sm focus:border-sky-500 focus:ring-sky-500 dark:bg-slate-950 dark:border-slate-800 dark:text-slate-200">
</div>
<button type="button" @click="removeLicenceProjet(index)" class="p-1.5 text-rose-500 hover:bg-rose-50 dark:hover:bg-rose-950/40 rounded-md border border-transparent hover:border-rose-200 dark:hover:border-rose-900/60">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Barre d'actions -->
<div class="flex items-center justify-end gap-3 pt-6 border-t border-slate-200 dark:border-slate-800">
<Link
:href="route('besoins-informatiques.index')"
class="inline-flex items-center px-4 py-2 border border-slate-300 text-sm font-semibold rounded-lg text-slate-700 bg-white hover:bg-slate-50 dark:bg-slate-900 dark:text-slate-300 dark:border-slate-850 dark:hover:bg-slate-800 transition-colors shadow-sm"
>
Annuler
</Link>
<button
type="submit"
:disabled="form.processing"
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-white bg-sky-600 hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-sky-500 transition-colors disabled:opacity-50"
>
Mettre à jour
</button>
</div>
</form>
</div>
</div>
</AuthenticatedLayout>
</template>
@@ -0,0 +1,113 @@
<script setup>
import { Head, Link } from '@inertiajs/vue3';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
defineProps({
besoins: Array,
});
</script>
<template>
<Head title="Besoins Informatiques (Mutualisation)" />
<AuthenticatedLayout>
<template #header>
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<h2 class="text-xl font-bold leading-tight text-slate-800 dark:text-slate-200">
Besoins Informatiques (Mutualisation)
</h2>
<div class="flex gap-2">
<Link
:href="route('besoins-informatiques.create')"
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-white bg-sky-600 rounded-lg hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400 transition-colors shadow-sm"
>
<svg class="w-4 h-4 mr-1.5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
Saisir un besoin
</Link>
</div>
</div>
</template>
<div class="py-6">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<!-- Notifications flash -->
<div v-if="$page.props.flash?.success" class="mb-6 p-4 bg-emerald-50 border border-emerald-200 text-emerald-800 rounded-lg dark:bg-emerald-950/40 dark:border-emerald-900 dark:text-emerald-300 flex items-center shadow-sm">
<svg class="w-5 h-5 mr-3 shrink-0" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<span>{{ $page.props.flash?.success }}</span>
</div>
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-slate-50 border-b border-slate-100 dark:bg-slate-950/40 dark:border-slate-850 text-xxs font-semibold uppercase tracking-wider text-slate-400">
<th class="px-6 py-4">Collectivité</th>
<th class="px-6 py-4">Référent</th>
<th class="px-6 py-4">Date de saisie</th>
<th class="px-6 py-4 text-right">Actions</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100 dark:divide-slate-850 text-sm">
<tr
v-for="besoin in besoins"
:key="besoin.id"
class="hover:bg-slate-50/50 dark:hover:bg-slate-950/20 transition-colors"
>
<td class="px-6 py-4 font-bold text-slate-800 dark:text-slate-200">
{{ besoin.collectivite || 'Non spécifié' }}
</td>
<td class="px-6 py-4 text-slate-800 dark:text-slate-200 font-medium">
{{ besoin.nom_referent }}
</td>
<td class="px-6 py-4 font-mono text-xs text-slate-600 dark:text-slate-350">
{{ new Date(besoin.created_at).toLocaleDateString() }}
</td>
<td class="px-6 py-4 text-right">
<div class="flex items-center justify-end gap-3">
<Link
:href="route('besoins-informatiques.edit', besoin.id)"
class="text-slate-500 hover:text-amber-500 dark:text-slate-400 dark:hover:text-amber-400"
title="Modifier la fiche"
>
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.83 18.75a4.48 4.48 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
</svg>
</Link>
<Link
:href="route('besoins-informatiques.destroy', besoin.id)"
method="delete"
as="button"
class="text-slate-500 hover:text-rose-600 dark:text-slate-400 dark:hover:text-rose-500"
title="Supprimer la fiche"
>
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
</Link>
</div>
</td>
</tr>
<tr v-if="besoins.length === 0">
<td colspan="4" class="px-6 py-12 text-center">
<div class="flex flex-col items-center justify-center text-slate-500 dark:text-slate-400">
<svg class="w-12 h-12 mb-4 text-slate-300 dark:text-slate-600" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
</svg>
<p class="text-lg font-medium">Aucun besoin informatique enregistré</p>
<p class="text-sm mt-1">Commencez par créer une nouvelle fiche de synthèse.</p>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
</template>