feat(audit) : composable useAuditLogsList (filtres brouillon/appliqué + pagination)
This commit is contained in:
@@ -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<AuditLog[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const perPage = ref(50)
|
||||
const loading = ref(false)
|
||||
const filterOpen = ref(false)
|
||||
const employeeOptions = ref<SelectOption[]>([])
|
||||
|
||||
// Applied filters (drive the fetch)
|
||||
const appliedEmployeeId = ref<number | undefined>(undefined)
|
||||
const appliedRange = ref<Range>(null)
|
||||
const appliedEntityTypes = ref<string[]>([])
|
||||
const appliedActions = ref<string[]>([])
|
||||
const appliedUsername = ref('')
|
||||
const appliedIp = ref('')
|
||||
const appliedDevice = ref('')
|
||||
|
||||
// Draft filters (edited inside the drawer)
|
||||
const draftEmployeeId = ref<number | undefined>(undefined)
|
||||
const draftRange = ref<Range>(null)
|
||||
const draftEntityTypes = ref<string[]>([])
|
||||
const draftActions = ref<string[]>([])
|
||||
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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user