feat: implement user management system and integrate AI services with Ollama and Gemini
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<script setup>
|
||||
import { Head, Link, useForm } from '@inertiajs/vue3';
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
|
||||
import InputLabel from '@/Components/InputLabel.vue';
|
||||
import TextInput from '@/Components/TextInput.vue';
|
||||
import InputError from '@/Components/InputError.vue';
|
||||
import PrimaryButton from '@/Components/PrimaryButton.vue';
|
||||
|
||||
const props = defineProps({
|
||||
user: Object,
|
||||
isEdit: Boolean,
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
name: props.user?.name || '',
|
||||
email: props.user?.email || '',
|
||||
password: '',
|
||||
role: props.user?.role || 'admin_reseau',
|
||||
});
|
||||
|
||||
const submit = () => {
|
||||
if (props.isEdit) {
|
||||
form.put(route('users.update', props.user.id));
|
||||
} else {
|
||||
form.post(route('users.store'));
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head :title="isEdit ? `Modifier l'Utilisateur ${user.name}` : 'Nouvel Utilisateur'" />
|
||||
|
||||
<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">
|
||||
{{ isEdit ? `Modifier l'Utilisateur` : 'Créer un Utilisateur' }}
|
||||
</h2>
|
||||
<Link
|
||||
:href="route('users.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-xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-xl shadow-sm overflow-hidden p-6">
|
||||
<form @submit.prevent="submit" class="space-y-6">
|
||||
<!-- Nom -->
|
||||
<div>
|
||||
<InputLabel for="name" value="Nom complet" class="font-bold" />
|
||||
<TextInput
|
||||
id="name"
|
||||
type="text"
|
||||
class="mt-1 block w-full"
|
||||
v-model="form.name"
|
||||
required
|
||||
placeholder="Ex: Sylvain Martin"
|
||||
/>
|
||||
<InputError class="mt-2" :message="form.errors.name" />
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<InputLabel for="email" value="Adresse email" class="font-bold" />
|
||||
<TextInput
|
||||
id="email"
|
||||
type="email"
|
||||
class="mt-1 block w-full"
|
||||
v-model="form.email"
|
||||
required
|
||||
placeholder="Ex: s.martin@beziers-mediterranee.fr"
|
||||
/>
|
||||
<InputError class="mt-2" :message="form.errors.email" />
|
||||
</div>
|
||||
|
||||
<!-- Mot de passe -->
|
||||
<div>
|
||||
<InputLabel
|
||||
for="password"
|
||||
:value="isEdit ? 'Nouveau mot de passe (laisser vide pour conserver)' : 'Mot de passe'"
|
||||
class="font-bold"
|
||||
/>
|
||||
<TextInput
|
||||
id="password"
|
||||
type="password"
|
||||
class="mt-1 block w-full"
|
||||
v-model="form.password"
|
||||
:required="!isEdit"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
<InputError class="mt-2" :message="form.errors.password" />
|
||||
</div>
|
||||
|
||||
<!-- Rôle -->
|
||||
<div>
|
||||
<InputLabel for="role" value="Rôle d'accès" class="font-bold" />
|
||||
<select
|
||||
id="role"
|
||||
v-model="form.role"
|
||||
class="mt-1 block 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
|
||||
>
|
||||
<option value="admin_reseau">Admin Réseau (accès standard)</option>
|
||||
<option value="chef_service">Chef de Service (accès administrateur)</option>
|
||||
</select>
|
||||
<InputError class="mt-2" :message="form.errors.role" />
|
||||
</div>
|
||||
|
||||
<!-- Boutons -->
|
||||
<div class="flex items-center justify-end gap-3 pt-6 border-t border-slate-100 dark:border-slate-850">
|
||||
<Link
|
||||
:href="route('users.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>
|
||||
|
||||
<PrimaryButton
|
||||
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 rounded-lg shadow-sm focus:outline-none focus:ring-2 focus:ring-sky-500 transition-colors disabled:opacity-50"
|
||||
>
|
||||
<svg v-if="form.processing" class="animate-spin -ml-1 mr-3 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
{{ isEdit ? 'Enregistrer les modifications' : 'Créer l\'utilisateur' }}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
</template>
|
||||
@@ -0,0 +1,118 @@
|
||||
<script setup>
|
||||
import { Head, Link, router } from '@inertiajs/vue3';
|
||||
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
|
||||
|
||||
const props = defineProps({
|
||||
users: Array,
|
||||
});
|
||||
|
||||
const deleteUser = (user) => {
|
||||
if (confirm(`Êtes-vous sûr de vouloir supprimer l'utilisateur "${user.name}" ?`)) {
|
||||
router.delete(route('users.destroy', user.id));
|
||||
}
|
||||
};
|
||||
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return '-';
|
||||
return new Date(dateString).toLocaleDateString('fr-FR', {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Head title="Gestion des Utilisateurs" />
|
||||
|
||||
<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">
|
||||
Gestion des Utilisateurs
|
||||
</h2>
|
||||
<Link
|
||||
:href="route('users.create')"
|
||||
class="inline-flex items-center px-4 py-2 text-sm font-semibold text-white bg-sky-600 hover:bg-sky-500 rounded-lg shadow-sm focus:outline-none transition-colors"
|
||||
>
|
||||
+ Nouvel Utilisateur
|
||||
</Link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="py-6">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<!-- Session Flash Messages -->
|
||||
<div v-if="$page.props.flash.success" class="mb-4 p-4 text-sm text-emerald-800 bg-emerald-50 dark:bg-emerald-950/20 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-900/40 rounded-xl">
|
||||
{{ $page.props.flash.success }}
|
||||
</div>
|
||||
<div v-if="$page.props.errors.error" class="mb-4 p-4 text-sm text-rose-800 bg-rose-50 dark:bg-rose-950/20 dark:text-rose-300 border border-rose-100 dark:border-rose-900/40 rounded-xl">
|
||||
{{ $page.props.errors.error }}
|
||||
</div>
|
||||
|
||||
<!-- Users list -->
|
||||
<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 dark:bg-slate-950 border-b border-slate-100 dark:border-slate-850">
|
||||
<th class="px-6 py-4 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400">Nom</th>
|
||||
<th class="px-6 py-4 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400">Email</th>
|
||||
<th class="px-6 py-4 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400">Rôle</th>
|
||||
<th class="px-6 py-4 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400">Inscrit le</th>
|
||||
<th class="px-6 py-4 text-xs font-bold uppercase tracking-wider text-slate-500 dark:text-slate-400 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100 dark:divide-slate-850">
|
||||
<tr v-for="user in users" :key="user.id" class="hover:bg-slate-50/50 dark:hover:bg-slate-850/20 transition-colors">
|
||||
<td class="px-6 py-4 text-sm font-semibold text-slate-800 dark:text-slate-200">
|
||||
{{ user.name }}
|
||||
<span v-if="user.id === $page.props.auth.user.id" class="ml-2 text-xxs font-bold text-sky-600 bg-sky-50 dark:bg-sky-950/40 px-2 py-0.5 rounded-full">
|
||||
Moi
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-600 dark:text-slate-300">
|
||||
{{ user.email }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm">
|
||||
<span
|
||||
v-if="user.role === 'chef_service'"
|
||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-50 text-purple-700 dark:bg-purple-950/40 dark:text-purple-300 border border-purple-100 dark:border-purple-900/60"
|
||||
>
|
||||
Chef de Service
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-50 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300 border border-blue-100 dark:border-blue-900/60"
|
||||
>
|
||||
Admin Réseau
|
||||
</span>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-slate-500 dark:text-slate-400">
|
||||
{{ formatDate(user.created_at) }}
|
||||
</td>
|
||||
<td class="px-6 py-4 text-sm text-right space-x-2">
|
||||
<Link
|
||||
:href="route('users.edit', user.id)"
|
||||
class="inline-flex items-center text-xs font-semibold text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-350"
|
||||
>
|
||||
Modifier
|
||||
</Link>
|
||||
<button
|
||||
v-if="user.id !== $page.props.auth.user.id"
|
||||
type="button"
|
||||
@click="deleteUser(user)"
|
||||
class="inline-flex items-center text-xs font-semibold text-rose-600 hover:text-rose-700 dark:text-rose-400 dark:hover:text-rose-350"
|
||||
>
|
||||
Supprimer
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
</template>
|
||||
Reference in New Issue
Block a user