feat(system): ajouter filtre par statut dans la page Logs
All checks were successful
Deploy / deploy (push) Successful in 2m30s
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:
parent
8443120c2f
commit
ec4a8be934
@@ -3,6 +3,13 @@ import { ApiJobRepository } from '../../../activity/infrastructure/api/ApiJobRep
|
|||||||
|
|
||||||
const jobRepository = new ApiJobRepository();
|
const jobRepository = new ApiJobRepository();
|
||||||
|
|
||||||
|
// Statuts disponibles par filtre
|
||||||
|
const STATUS_MAP = {
|
||||||
|
failed: ['failed'],
|
||||||
|
completed: ['completed'],
|
||||||
|
all: ['failed', 'completed'],
|
||||||
|
};
|
||||||
|
|
||||||
export const useLogsStore = defineStore('logs', {
|
export const useLogsStore = defineStore('logs', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
logs: [],
|
logs: [],
|
||||||
@@ -16,6 +23,7 @@ export const useLogsStore = defineStore('logs', {
|
|||||||
hasPreviousPage: false,
|
hasPreviousPage: false,
|
||||||
sortBy: 'createdAt',
|
sortBy: 'createdAt',
|
||||||
sortOrder: 'DESC',
|
sortOrder: 'DESC',
|
||||||
|
statusFilter: 'failed', // 'failed' | 'completed' | 'all'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
@@ -34,7 +42,7 @@ export const useLogsStore = defineStore('logs', {
|
|||||||
limit: this.limit,
|
limit: this.limit,
|
||||||
sortBy: this.sortBy,
|
sortBy: this.sortBy,
|
||||||
sortOrder: this.sortOrder,
|
sortOrder: this.sortOrder,
|
||||||
status: ['failed'],
|
status: STATUS_MAP[this.statusFilter],
|
||||||
type: 'scraping_job',
|
type: 'scraping_job',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -65,6 +73,12 @@ export const useLogsStore = defineStore('logs', {
|
|||||||
await this.loadLogs(1);
|
await this.loadLogs(1);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async setStatusFilter(filter) {
|
||||||
|
this.statusFilter = filter;
|
||||||
|
this.currentPage = 1;
|
||||||
|
await this.loadLogs(1);
|
||||||
|
},
|
||||||
|
|
||||||
async deleteLog(id) {
|
async deleteLog(id) {
|
||||||
try {
|
try {
|
||||||
await jobRepository.deleteJob(id);
|
await jobRepository.deleteJob(id);
|
||||||
@@ -81,7 +95,10 @@ export const useLogsStore = defineStore('logs', {
|
|||||||
this.error = null;
|
this.error = null;
|
||||||
|
|
||||||
try {
|
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);
|
await this.loadLogs(1);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.error = error.message;
|
this.error = error.message;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="border-t border-gray-200 dark:border-gray-700 py-4 px-6">
|
<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-start justify-between gap-4">
|
||||||
<div class="flex items-baseline gap-2 min-w-0">
|
<div class="flex items-baseline gap-2 min-w-0">
|
||||||
<span class="font-semibold text-gray-900 dark:text-gray-100 truncate">
|
<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">
|
<span class="text-sm text-gray-600 dark:text-gray-400 shrink-0">
|
||||||
Chapitre {{ log.context?.chapterNumber ?? '?' }}
|
Chapitre {{ log.context?.chapterNumber ?? '?' }}
|
||||||
</span>
|
</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>
|
||||||
<div class="flex items-center gap-3 shrink-0">
|
<div class="flex items-center gap-3 shrink-0">
|
||||||
<span class="text-xs text-gray-400 dark:text-gray-500">{{ formattedDate }}</span>
|
<span class="text-xs text-gray-400 dark:text-gray-500">{{ formattedDate }}</span>
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ const {
|
|||||||
hasPreviousPage,
|
hasPreviousPage,
|
||||||
sortBy,
|
sortBy,
|
||||||
sortOrder,
|
sortOrder,
|
||||||
|
statusFilter,
|
||||||
} = storeToRefs(logsStore);
|
} = storeToRefs(logsStore);
|
||||||
|
|
||||||
const hasError = computed(() => !!error.value);
|
const hasError = computed(() => !!error.value);
|
||||||
@@ -100,12 +101,25 @@ function getSource(log) {
|
|||||||
|
|
||||||
const isSortSelected = (by, order) => sortBy.value === by && sortOrder.value === order;
|
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(() => ({
|
const toolbarConfig = computed(() => ({
|
||||||
leftSection: [
|
leftSection: [
|
||||||
{ type: 'label', text: 'Logs', class: 'text-sm font-medium' },
|
{ type: 'label', text: 'Logs', class: 'text-sm font-medium' },
|
||||||
{ type: 'label', text: `(${total.value})`, class: 'text-sm text-gray-400' },
|
{ type: 'label', text: `(${total.value})`, class: 'text-sm text-gray-400' },
|
||||||
],
|
],
|
||||||
rightSection: [
|
rightSection: [
|
||||||
|
...STATUS_FILTERS.map(f => ({
|
||||||
|
type: 'button',
|
||||||
|
label: f.label,
|
||||||
|
active: statusFilter.value === f.key,
|
||||||
|
onClick: () => logsStore.setStatusFilter(f.key),
|
||||||
|
})),
|
||||||
|
{ type: 'divider' },
|
||||||
{
|
{
|
||||||
type: 'dropdown',
|
type: 'dropdown',
|
||||||
icon: BarsArrowDownIcon,
|
icon: BarsArrowDownIcon,
|
||||||
|
|||||||
Reference in New Issue
Block a user