Files
Lesstime/frontend/components/admin/AdminUserTab.vue
T
Matthieu 89ce523019
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 1m18s
Pull Request — Quality gate / Frontend (build) (pull_request) Successful in 1m29s
feat(user) : UI archivage/désarchivage des utilisateurs côté admin
- badge « Archivé » et libellé barré dans la liste admin
- popup de confirmation avant archivage (rappelle que c'est réversible)
- bouton de restauration (PATCH archived:false) pour les archivés
- case « Afficher les utilisateurs archivés » (filtre ?archived=true)
- masque l'action d'archivage sur son propre compte (évite le 403)
- service users : getArchived/restore, toast remove -> users.archived
- i18n FR : clés archived/restored/badge/confirmation
2026-06-26 17:08:20 +02:00

167 lines
4.7 KiB
Vue

<template>
<div>
<div class="flex items-center justify-between">
<h2 class="text-lg font-bold text-neutral-900">Utilisateurs</h2>
<MalioButton
icon-name="mdi:plus"
icon-position="left"
button-class="w-auto px-4"
label="Ajouter un utilisateur"
@click="openCreate"
/>
</div>
<div class="mt-4">
<MalioCheckbox
v-model="showArchived"
:label="$t('users.showArchived')"
:reserve-message-space="false"
/>
</div>
<DataTable
:columns="columns"
:items="items"
:loading="isLoading"
empty-message="Aucun utilisateur trouvé."
@row-click="openEdit"
>
<template #cell-username="{ item }">
<span :class="{ 'text-neutral-400 line-through': item.archived }">
{{ item.username }}
</span>
<span
v-if="item.archived"
class="ml-2 rounded-full bg-red-100 px-2 py-0.5 text-xs font-semibold text-red-700"
>
{{ $t('users.archivedBadge') }}
</span>
</template>
<template #cell-roles="{ item }">
<span
v-for="role in item.roles"
:key="role"
class="mr-1 rounded-full bg-neutral-200 px-2 py-0.5 text-xs font-semibold text-neutral-700"
>
{{ role }}
</span>
</template>
<template #actions="{ item }">
<MalioButtonIcon
v-if="item.archived"
icon="mdi:restore"
:aria-label="$t('users.restore')"
variant="ghost"
icon-size="20"
button-class="text-neutral-400 hover:text-primary-500"
@click.stop="handleRestore(item)"
/>
<MalioButtonIcon
v-else-if="item.id !== currentUserId"
icon="mdi:delete-outline"
:aria-label="$t('users.archive')"
variant="ghost"
icon-size="20"
button-class="text-neutral-400 hover:text-red-500"
@click.stop="openArchiveConfirm(item)"
/>
</template>
</DataTable>
<UserDrawer
v-model="drawerOpen"
:item="selectedItem"
@saved="onSaved"
/>
<ConfirmArchiveUserModal
v-model="archiveConfirmOpen"
:username="userToArchive?.username ?? ''"
@confirm="confirmArchive"
/>
</div>
</template>
<script setup lang="ts">
import type { UserData } from '~/services/dto/user-data'
import { useUserService } from '~/services/users'
import { useAuthStore } from '~/shared/stores/auth'
import type { DataTableColumn } from '~/components/ui/DataTable.vue'
const columns: DataTableColumn[] = [
{ key: 'username', label: "Nom d'utilisateur", primary: true },
{ key: 'roles', label: 'Rôles' },
]
const { getAll, getArchived, remove, restore } = useUserService()
const authStore = useAuthStore()
const currentUserId = computed(() => authStore.user?.id)
const items = ref<UserData[]>([])
const isLoading = ref(true)
const drawerOpen = ref(false)
const selectedItem = ref<UserData | null>(null)
const showArchived = ref(false)
const archiveConfirmOpen = ref(false)
const userToArchive = ref<UserData | null>(null)
async function loadItems() {
isLoading.value = true
try {
if (showArchived.value) {
const [active, archived] = await Promise.all([getAll(), getArchived()])
items.value = [...active, ...archived]
} else {
items.value = await getAll()
}
} finally {
isLoading.value = false
}
}
function openCreate() {
selectedItem.value = null
drawerOpen.value = true
}
function openEdit(item: UserData) {
selectedItem.value = item
drawerOpen.value = true
}
function openArchiveConfirm(item: UserData) {
userToArchive.value = item
archiveConfirmOpen.value = true
}
async function confirmArchive() {
if (!userToArchive.value) {
return
}
await remove(userToArchive.value.id)
archiveConfirmOpen.value = false
userToArchive.value = null
await loadItems()
}
async function handleRestore(item: UserData) {
await restore(item.id)
await loadItems()
}
async function onSaved() {
await loadItems()
}
watch(showArchived, () => {
loadItems()
})
onMounted(() => {
loadItems()
})
</script>