From 32b1af23774093ec385910a36d6c6cf1562e54a4 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 24 Jun 2026 11:22:53 +0200 Subject: [PATCH] =?UTF-8?q?feat(audit)=20:=20composable=20useAuditLogsList?= =?UTF-8?q?=20(filtres=20brouillon/appliqu=C3=A9=20+=20pagination)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/composables/useAuditLogsList.ts | 156 +++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 frontend/composables/useAuditLogsList.ts diff --git a/frontend/composables/useAuditLogsList.ts b/frontend/composables/useAuditLogsList.ts new file mode 100644 index 0000000..58a4f2d --- /dev/null +++ b/frontend/composables/useAuditLogsList.ts @@ -0,0 +1,156 @@ +import { ref, computed } from 'vue' +import type { AuditLog } from '~/services/dto/audit-log' +import { fetchAuditLogs, type AuditLogFilters } from '~/services/audit-logs' +import { listEmployees } from '~/services/employees' + +type Range = { start: string, end: string } | null +type SelectOption = { value: number, text: string } + +export const useAuditLogsList = () => { + const items = ref([]) + const total = ref(0) + const page = ref(1) + const perPage = ref(50) + const loading = ref(false) + const filterOpen = ref(false) + const employeeOptions = ref([]) + + // Applied filters (drive the fetch) + const appliedEmployeeId = ref(undefined) + const appliedRange = ref(null) + const appliedEntityTypes = ref([]) + const appliedActions = ref([]) + const appliedUsername = ref('') + const appliedIp = ref('') + const appliedDevice = ref('') + + // Draft filters (edited inside the drawer) + const draftEmployeeId = ref(undefined) + const draftRange = ref(null) + const draftEntityTypes = ref([]) + const draftActions = ref([]) + const draftUsername = ref('') + const draftIp = ref('') + const draftDevice = ref('') + + const activeFilterCount = computed(() => { + let n = 0 + if (appliedEmployeeId.value !== undefined) n++ + if (appliedRange.value?.start || appliedRange.value?.end) n++ + if (appliedEntityTypes.value.length > 0) n++ + if (appliedActions.value.length > 0) n++ + if (appliedUsername.value.trim() !== '') n++ + if (appliedIp.value.trim() !== '') n++ + if (appliedDevice.value.trim() !== '') n++ + return n + }) + + const buildFilters = (): AuditLogFilters => ({ + employeeId: appliedEmployeeId.value, + from: appliedRange.value?.start || undefined, + to: appliedRange.value?.end || undefined, + entityType: appliedEntityTypes.value.length > 0 ? [...appliedEntityTypes.value] : undefined, + action: appliedActions.value.length > 0 ? [...appliedActions.value] : undefined, + username: appliedUsername.value.trim() || undefined, + ip: appliedIp.value.trim() || undefined, + device: appliedDevice.value.trim() || undefined, + page: page.value, + perPage: perPage.value, + }) + + // Race guard: only the latest request may commit its result. + let requestSeq = 0 + const load = async () => { + const seq = ++requestSeq + loading.value = true + try { + const result = await fetchAuditLogs(buildFilters()) + if (seq !== requestSeq) return + items.value = result.items + total.value = result.total + page.value = result.page + perPage.value = result.perPage + } finally { + if (seq === requestSeq) loading.value = false + } + } + + const init = async () => { + try { + const employees = await listEmployees() + employeeOptions.value = employees.map(e => ({ + value: e.id, + text: `${e.lastName} ${e.firstName}`, + })) + } catch { + employeeOptions.value = [] + } + await load() + } + + const goToPage = (n: number) => { + page.value = n + load() + } + + const setPerPage = (n: number) => { + perPage.value = n + page.value = 1 + load() + } + + const openFilters = () => { + draftEmployeeId.value = appliedEmployeeId.value + draftRange.value = appliedRange.value ? { ...appliedRange.value } : null + draftEntityTypes.value = [...appliedEntityTypes.value] + draftActions.value = [...appliedActions.value] + draftUsername.value = appliedUsername.value + draftIp.value = appliedIp.value + draftDevice.value = appliedDevice.value + filterOpen.value = true + } + + const applyFilters = () => { + appliedEmployeeId.value = draftEmployeeId.value + appliedRange.value = draftRange.value ? { ...draftRange.value } : null + appliedEntityTypes.value = [...draftEntityTypes.value] + appliedActions.value = [...draftActions.value] + appliedUsername.value = draftUsername.value + appliedIp.value = draftIp.value + appliedDevice.value = draftDevice.value + page.value = 1 + filterOpen.value = false + load() + } + + const resetFilters = () => { + draftEmployeeId.value = undefined + draftRange.value = null + draftEntityTypes.value = [] + draftActions.value = [] + draftUsername.value = '' + draftIp.value = '' + draftDevice.value = '' + appliedEmployeeId.value = undefined + appliedRange.value = null + appliedEntityTypes.value = [] + appliedActions.value = [] + appliedUsername.value = '' + appliedIp.value = '' + appliedDevice.value = '' + page.value = 1 + load() // drawer stays open + } + + const toggle = (arr: typeof draftEntityTypes, value: string, selected: boolean) => { + arr.value = selected ? [...arr.value, value] : arr.value.filter(v => v !== value) + } + const toggleEntityType = (value: string, selected: boolean) => toggle(draftEntityTypes, value, selected) + const toggleAction = (value: string, selected: boolean) => toggle(draftActions, value, selected) + + return { + items, total, page, perPage, loading, filterOpen, employeeOptions, activeFilterCount, + draftEmployeeId, draftRange, draftEntityTypes, draftActions, draftUsername, draftIp, draftDevice, + init, goToPage, setPerPage, openFilters, applyFilters, resetFilters, toggleEntityType, toggleAction, + } +}