diff --git a/frontend/src/i18n/fr.ts b/frontend/src/i18n/fr.ts index e8d1a19..fd823a2 100644 --- a/frontend/src/i18n/fr.ts +++ b/frontend/src/i18n/fr.ts @@ -63,6 +63,10 @@ const fr = { 'home.deleteError': 'Erreur lors de la suppression de la synthese.', 'home.generationInProgress': 'Une generation est en cours...', 'home.viewProgress': 'Voir la progression', + 'home.sortBy': 'Trier par', + 'home.sortDate': 'Date', + 'home.sortTheme': 'Theme', + 'home.deletedTheme': 'Theme supprime', // Generate 'generate.title': 'Generer la Synthese Hebdomadaire', diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index ed20b41..397a325 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,6 +1,7 @@ import { type Component, createSignal, + createMemo, onMount, onCleanup, Show, @@ -32,6 +33,7 @@ const Home: Component = () => { const [error, setError] = createSignal(null); const [deletingId, setDeletingId] = createSignal(null); const [deleteTimers, setDeleteTimers] = createSignal>>({}); + const [sortBy, setSortBy] = createSignal<'date' | 'theme'>('date'); onCleanup(() => { const timers = deleteTimers(); @@ -98,6 +100,124 @@ const Home: Component = () => { const hasInProgress = () => syntheses().some((s) => s.status === 'in_progress'); + /** Groups syntheses by theme_name, null theme goes last. Within each group: newest first. */ + const groupedByTheme = createMemo(() => { + const sorted = [...syntheses()].sort( + (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), + ); + + const map = new Map(); + for (const s of sorted) { + const key = s.theme_name ?? null; + if (!map.has(key)) map.set(key, []); + map.get(key)!.push(s); + } + + // Named themes first (sorted alphabetically), null last + const named = [...map.keys()] + .filter((k): k is string => k !== null) + .sort((a, b) => a.localeCompare(b)); + + const groups: { name: string | null; syntheses: SynthesisListItem[] }[] = named.map( + (name) => ({ name, syntheses: map.get(name)! }), + ); + + if (map.has(null)) { + groups.push({ name: null, syntheses: map.get(null)! }); + } + + return groups; + }); + + /** Renders a single synthesis card (shared between both sort views). */ + const SynthesisCard = (synth: SynthesisListItem) => ( + +
+
+
+ + {t('home.weekLabel', { week: extractWeekNumber(synth.week) })} + + + + {synth.theme_name} + + + + + {t('home.deletedTheme')} + + +
+
+
{formatDate(synth.created_at)}
+
{formatTime(synth.created_at)}
+
+
+

+ {t('home.cardTitle')} +

+
+ 0} + fallback={

{t('home.noPreview')}

} + > + + {(section) => ( +
+ {section.title} + ({section.count}) +
+ )} +
+
+
+
+
+ + {t('home.readLink')} → + +
+ {synth.job_id && ( + e.stopPropagation()} + > + + + + + )} + +
+
+ + ); + return ( }>
@@ -163,85 +283,59 @@ const Home: Component = () => {
} > -
- - {(synth) => ( - -
-
- - {t('home.weekLabel', { week: extractWeekNumber(synth.week) })} - -
-
{formatDate(synth.created_at)}
-
{formatTime(synth.created_at)}
-
-
-

- {t('home.cardTitle')} -

-
- 0} - fallback={

{t('home.noPreview')}

} - > - - {(section) => ( -
- {section.title} - ({section.count}) -
- )} -
-
-
-
-
- - {t('home.readLink')} → - -
- {synth.job_id && ( - e.stopPropagation()} - > - - - - - )} - -
+ {/* Sort toggle */} +
+ {t('home.sortBy')} : + + +
+ + {/* Date sort: flat grid, newest first */} + +
+ new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), + )}> + {(synth) => SynthesisCard(synth)} + +
+
+ + {/* Theme sort: one section per theme */} + + + {(group) => ( +
+

+ {group.name ?? t('home.deletedTheme')} +

+
+ + {(synth) => SynthesisCard(synth)} +
- +
)}
-
+