refactor : merge Inventory_frontend submodule into frontend/ directory
Merges the full git history of Inventory_frontend into the monorepo under frontend/. Removes the submodule in favor of a unified repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
270
frontend/app/pages/admin/index.vue
Normal file
270
frontend/app/pages/admin/index.vue
Normal file
@@ -0,0 +1,270 @@
|
||||
<template>
|
||||
<div class="container mx-auto p-6 max-w-6xl">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Administration des profils
|
||||
</h1>
|
||||
<button class="btn btn-primary btn-sm" @click="showCreateDialog = true">
|
||||
Nouveau profil
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<DataTable
|
||||
:columns="columns"
|
||||
:rows="sortedProfiles"
|
||||
:loading="isLoading"
|
||||
:sort="sortState"
|
||||
:show-counter="false"
|
||||
table-class="table-zebra"
|
||||
empty-message="Aucun profil."
|
||||
@sort="handleSort"
|
||||
>
|
||||
<template #cell-name="{ row }">
|
||||
<span class="font-medium">{{ row.firstName }} {{ row.lastName }}</span>
|
||||
</template>
|
||||
|
||||
<template #cell-email="{ row }">
|
||||
<span class="text-sm text-base-content/70">{{ row.email || '-' }}</span>
|
||||
</template>
|
||||
|
||||
<template #cell-role="{ row }">
|
||||
<select
|
||||
class="select select-bordered select-xs"
|
||||
:value="primaryRole(row)"
|
||||
@change="handleRoleChange(row.id, $event.target.value)"
|
||||
>
|
||||
<option value="ROLE_ADMIN">Admin</option>
|
||||
<option value="ROLE_GESTIONNAIRE">Gestionnaire</option>
|
||||
<option value="ROLE_VIEWER">Viewer</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
<template #cell-password="{ row }">
|
||||
<span v-if="row.hasPassword" class="badge badge-success badge-sm">Oui</span>
|
||||
<span v-else class="badge badge-ghost badge-sm">Non</span>
|
||||
<button
|
||||
class="btn btn-ghost btn-xs ml-1"
|
||||
@click="openPasswordDialog(row.id)"
|
||||
>
|
||||
{{ row.hasPassword ? 'Changer' : 'Definir' }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template #cell-status="{ row }">
|
||||
<span
|
||||
class="badge badge-sm"
|
||||
:class="row.isActive ? 'badge-success' : 'badge-error'"
|
||||
>
|
||||
{{ row.isActive ? 'Actif' : 'Inactif' }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #cell-actions="{ row }">
|
||||
<button
|
||||
v-if="row.isActive"
|
||||
class="btn btn-ghost btn-xs text-error"
|
||||
@click="handleDeactivate(row.id)"
|
||||
>
|
||||
Desactiver
|
||||
</button>
|
||||
</template>
|
||||
</DataTable>
|
||||
|
||||
<!-- Create Profile Dialog -->
|
||||
<dialog ref="createDialog" class="modal" :open="showCreateDialog || undefined">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg mb-4">
|
||||
Nouveau profil
|
||||
</h3>
|
||||
<form @submit.prevent="handleCreate">
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Prenom</span></label>
|
||||
<input v-model="createForm.firstName" type="text" class="input input-bordered" required>
|
||||
</div>
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Nom</span></label>
|
||||
<input v-model="createForm.lastName" type="text" class="input input-bordered" required>
|
||||
</div>
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Email</span></label>
|
||||
<input v-model="createForm.email" type="email" class="input input-bordered">
|
||||
</div>
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Mot de passe</span></label>
|
||||
<input v-model="createForm.password" type="password" class="input input-bordered">
|
||||
</div>
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Role</span></label>
|
||||
<select v-model="createForm.role" class="select select-bordered">
|
||||
<option value="ROLE_ADMIN">Admin</option>
|
||||
<option value="ROLE_GESTIONNAIRE">Gestionnaire</option>
|
||||
<option value="ROLE_VIEWER">Viewer</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<button type="button" class="btn btn-ghost" @click="showCreateDialog = false">
|
||||
Annuler
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="creating">
|
||||
<span v-if="creating" class="loading loading-spinner loading-xs" />
|
||||
Creer
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button type="button" @click="showCreateDialog = false">
|
||||
close
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<!-- Set Password Dialog -->
|
||||
<dialog ref="passwordDialog" class="modal" :open="showPasswordDialog || undefined">
|
||||
<div class="modal-box">
|
||||
<h3 class="font-bold text-lg mb-4">
|
||||
Definir le mot de passe
|
||||
</h3>
|
||||
<form @submit.prevent="handleSetPassword">
|
||||
<div class="form-control mb-3">
|
||||
<label class="label"><span class="label-text">Nouveau mot de passe</span></label>
|
||||
<input v-model="newPassword" type="password" class="input input-bordered" required>
|
||||
</div>
|
||||
<div class="modal-action">
|
||||
<button type="button" class="btn btn-ghost" @click="showPasswordDialog = false">
|
||||
Annuler
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" :disabled="settingPassword">
|
||||
<span v-if="settingPassword" class="loading loading-spinner loading-xs" />
|
||||
Valider
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button type="button" @click="showPasswordDialog = false">
|
||||
close
|
||||
</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import DataTable from '~/components/common/DataTable.vue'
|
||||
import { useAdminProfiles } from '#imports'
|
||||
|
||||
const { profiles, loading, fetchAll, createProfile, updateRole, setPassword, deactivateProfile } = useAdminProfiles()
|
||||
|
||||
const loaded = ref(false)
|
||||
const isLoading = computed(() => loading.value || !loaded.value)
|
||||
|
||||
const columns = [
|
||||
{ key: 'name', label: 'Nom', sortable: true },
|
||||
{ key: 'email', label: 'Email', sortable: true },
|
||||
{ key: 'role', label: 'Role', sortable: true },
|
||||
{ key: 'password', label: 'Mot de passe' },
|
||||
{ key: 'status', label: 'Statut', sortable: true },
|
||||
{ key: 'actions', label: 'Actions' },
|
||||
]
|
||||
|
||||
const sortState = ref({ field: 'name', direction: 'asc' })
|
||||
|
||||
const handleSort = (sort) => {
|
||||
sortState.value = sort
|
||||
}
|
||||
|
||||
const sortedProfiles = computed(() => {
|
||||
const { field, direction } = sortState.value
|
||||
const dir = direction === 'desc' ? -1 : 1
|
||||
return [...profiles.value].sort((a, b) => {
|
||||
let valA, valB
|
||||
if (field === 'name') {
|
||||
valA = `${a.firstName} ${a.lastName}`.toLowerCase()
|
||||
valB = `${b.firstName} ${b.lastName}`.toLowerCase()
|
||||
}
|
||||
else if (field === 'role') {
|
||||
valA = primaryRole(a)
|
||||
valB = primaryRole(b)
|
||||
}
|
||||
else if (field === 'status') {
|
||||
valA = a.isActive ? '1' : '0'
|
||||
valB = b.isActive ? '1' : '0'
|
||||
}
|
||||
else {
|
||||
valA = (a[field] || '').toLowerCase()
|
||||
valB = (b[field] || '').toLowerCase()
|
||||
}
|
||||
return dir * valA.localeCompare(valB)
|
||||
})
|
||||
})
|
||||
|
||||
const showCreateDialog = ref(false)
|
||||
const showPasswordDialog = ref(false)
|
||||
const creating = ref(false)
|
||||
const settingPassword = ref(false)
|
||||
const passwordProfileId = ref(null)
|
||||
const newPassword = ref('')
|
||||
|
||||
const createForm = ref({
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
email: '',
|
||||
password: '',
|
||||
role: 'ROLE_VIEWER',
|
||||
})
|
||||
|
||||
const primaryRole = (profile) => {
|
||||
const roles = profile.roles || []
|
||||
if (roles.includes('ROLE_ADMIN')) { return 'ROLE_ADMIN' }
|
||||
if (roles.includes('ROLE_GESTIONNAIRE')) { return 'ROLE_GESTIONNAIRE' }
|
||||
return 'ROLE_VIEWER'
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
creating.value = true
|
||||
try {
|
||||
const data = { ...createForm.value }
|
||||
if (!data.email) { delete data.email }
|
||||
if (!data.password) { delete data.password }
|
||||
await createProfile(data)
|
||||
showCreateDialog.value = false
|
||||
createForm.value = { firstName: '', lastName: '', email: '', password: '', role: 'ROLE_VIEWER' }
|
||||
}
|
||||
finally {
|
||||
creating.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleRoleChange = async (profileId, role) => {
|
||||
await updateRole(profileId, role)
|
||||
}
|
||||
|
||||
const openPasswordDialog = (profileId) => {
|
||||
passwordProfileId.value = profileId
|
||||
newPassword.value = ''
|
||||
showPasswordDialog.value = true
|
||||
}
|
||||
|
||||
const handleSetPassword = async () => {
|
||||
if (!passwordProfileId.value) { return }
|
||||
settingPassword.value = true
|
||||
try {
|
||||
await setPassword(passwordProfileId.value, newPassword.value)
|
||||
showPasswordDialog.value = false
|
||||
}
|
||||
finally {
|
||||
settingPassword.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeactivate = async (profileId) => {
|
||||
await deactivateProfile(profileId)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await fetchAll()
|
||||
loaded.value = true
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user