feat(system): ajouter filtre par statut dans la page Logs
All checks were successful
Deploy / deploy (push) Successful in 2m30s

- Filtre toolbar : Échecs / Terminés / Tous (défaut : Échecs)
- Badge statut sur chaque LogItem (vert Terminé / rouge Échec)
- deleteAllLogs respecte le filtre actif
This commit is contained in:
ext.jeremy.guillot@maxicoffee.domains
2026-03-16 15:00:12 +01:00
parent 8443120c2f
commit ec4a8be934
3 changed files with 43 additions and 3 deletions

View File

@@ -3,6 +3,13 @@ import { ApiJobRepository } from '../../../activity/infrastructure/api/ApiJobRep
const jobRepository = new ApiJobRepository();
// Statuts disponibles par filtre
const STATUS_MAP = {
failed: ['failed'],
completed: ['completed'],
all: ['failed', 'completed'],
};
export const useLogsStore = defineStore('logs', {
state: () => ({
logs: [],
@@ -16,6 +23,7 @@ export const useLogsStore = defineStore('logs', {
hasPreviousPage: false,
sortBy: 'createdAt',
sortOrder: 'DESC',
statusFilter: 'failed', // 'failed' | 'completed' | 'all'
}),
getters: {
@@ -34,7 +42,7 @@ export const useLogsStore = defineStore('logs', {
limit: this.limit,
sortBy: this.sortBy,
sortOrder: this.sortOrder,
status: ['failed'],
status: STATUS_MAP[this.statusFilter],
type: 'scraping_job',
});
@@ -65,6 +73,12 @@ export const useLogsStore = defineStore('logs', {
await this.loadLogs(1);
},
async setStatusFilter(filter) {
this.statusFilter = filter;
this.currentPage = 1;
await this.loadLogs(1);
},
async deleteLog(id) {
try {
await jobRepository.deleteJob(id);
@@ -81,7 +95,10 @@ export const useLogsStore = defineStore('logs', {
this.error = null;
try {
await jobRepository.deleteJobs({ status: 'failed', type: 'scraping_job' });
await jobRepository.deleteJobs({
status: STATUS_MAP[this.statusFilter].join(','),
type: 'scraping_job',
});
await this.loadLogs(1);
} catch (error) {
this.error = error.message;

View File

@@ -1,6 +1,6 @@
<template>
<div class="border-t border-gray-200 dark:border-gray-700 py-4 px-6">
<!-- Ligne 1 : Titre manga + chapitre + date + bouton supprimer -->
<!-- Ligne 1 : Titre manga + chapitre + badge statut + date + bouton supprimer -->
<div class="flex items-start justify-between gap-4">
<div class="flex items-baseline gap-2 min-w-0">
<span class="font-semibold text-gray-900 dark:text-gray-100 truncate">
@@ -10,6 +10,15 @@
<span class="text-sm text-gray-600 dark:text-gray-400 shrink-0">
Chapitre {{ log.context?.chapterNumber ?? '?' }}
</span>
<span
:class="[
'px-1.5 py-0.5 text-xs font-medium shrink-0',
log.status === 'completed'
? 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'
: 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400'
]">
{{ log.status === 'completed' ? 'Terminé' : 'Échec' }}
</span>
</div>
<div class="flex items-center gap-3 shrink-0">
<span class="text-xs text-gray-400 dark:text-gray-500">{{ formattedDate }}</span>

View File

@@ -82,6 +82,7 @@ const {
hasPreviousPage,
sortBy,
sortOrder,
statusFilter,
} = storeToRefs(logsStore);
const hasError = computed(() => !!error.value);
@@ -100,12 +101,25 @@ function getSource(log) {
const isSortSelected = (by, order) => sortBy.value === by && sortOrder.value === order;
const STATUS_FILTERS = [
{ key: 'failed', label: 'Échecs' },
{ key: 'completed', label: 'Terminés' },
{ key: 'all', label: 'Tous' },
];
const toolbarConfig = computed(() => ({
leftSection: [
{ type: 'label', text: 'Logs', class: 'text-sm font-medium' },
{ type: 'label', text: `(${total.value})`, class: 'text-sm text-gray-400' },
],
rightSection: [
...STATUS_FILTERS.map(f => ({
type: 'button',
label: f.label,
active: statusFilter.value === f.key,
onClick: () => logsStore.setStatusFilter(f.key),
})),
{ type: 'divider' },
{
type: 'dropdown',
icon: BarsArrowDownIcon,