From 6b84c335d01ea97c70e0f2886432a642f2874ffb Mon Sep 17 00:00:00 2001 From: oabrivard Date: Thu, 26 Mar 2026 21:11:10 +0100 Subject: [PATCH] feat: improve synthesis list cards with time, all categories, and uniform height - Add generation time below date in synthesis cards - Show all categories with article count in parentheses - Use flex-col layout for uniform card height - Add sections_summary to SynthesisListItem API response - Add formatTime utility Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/src/models/synthesis.rs | 17 +++++++++++++++ frontend/src/__tests__/fixtures.ts | 4 ++++ frontend/src/pages/Home.tsx | 33 +++++++++++++++++------------- frontend/src/types.ts | 6 ++++++ frontend/src/utils/dates.ts | 11 ++++++++++ 5 files changed, 57 insertions(+), 14 deletions(-) diff --git a/backend/src/models/synthesis.rs b/backend/src/models/synthesis.rs index 5a60de7..4ce58d5 100644 --- a/backend/src/models/synthesis.rs +++ b/backend/src/models/synthesis.rs @@ -84,9 +84,17 @@ pub struct SynthesisListItem { pub created_at: DateTime, pub first_section_title: Option, pub first_section_item_count: usize, + pub sections_summary: Vec, pub job_id: Option, } +/// Summary of a section for the synthesis list view. +#[derive(Debug, Serialize)] +pub struct SectionSummary { + pub title: String, + pub count: usize, +} + impl TryFrom for SynthesisListItem { type Error = crate::errors::AppError; @@ -98,6 +106,14 @@ impl TryFrom for SynthesisListItem { let first_section_title = first.map(|sec| sec.title.clone()); let first_section_item_count = first.map(|sec| sec.items.len()).unwrap_or(0); + let sections_summary: Vec = sections + .iter() + .map(|sec| SectionSummary { + title: sec.title.clone(), + count: sec.items.len(), + }) + .collect(); + Ok(Self { id: s.id, week: s.week, @@ -105,6 +121,7 @@ impl TryFrom for SynthesisListItem { created_at: s.created_at, first_section_title, first_section_item_count, + sections_summary, job_id: s.job_id, }) } diff --git a/frontend/src/__tests__/fixtures.ts b/frontend/src/__tests__/fixtures.ts index f2c59cb..a76e43e 100644 --- a/frontend/src/__tests__/fixtures.ts +++ b/frontend/src/__tests__/fixtures.ts @@ -17,6 +17,10 @@ export const MOCK_SYNTHESIS_LIST_ITEM: SynthesisListItem = { created_at: '2026-03-21T10:00:00Z', first_section_title: 'Annonces majeures', first_section_item_count: 3, + sections_summary: [ + { title: 'Annonces majeures', count: 3 }, + { title: 'Recherche', count: 2 }, + ], job_id: 'job-test-1', }; diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 7a2d018..ed20b41 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -12,7 +12,7 @@ import { useI18n } from '~/i18n'; import { synthesesApi } from '~/api/syntheses'; import { isApiError } from '~/types'; import type { SynthesisListItem } from '~/types'; -import { extractWeekNumber, formatDate } from '~/utils/dates'; +import { extractWeekNumber, formatDate, formatTime } from '~/utils/dates'; import LoadingSpinner from '~/components/ui/LoadingSpinner'; /** @@ -168,29 +168,34 @@ const Home: Component = () => { {(synth) => ( -
+
{t('home.weekLabel', { week: extractWeekNumber(synth.week) })} - - {formatDate(synth.created_at)} - +
+
{formatDate(synth.created_at)}
+
{formatTime(synth.created_at)}
+
-

+

{t('home.cardTitle')}

-
+
{t('home.noPreview')}

} + when={synth.sections_summary && synth.sections_summary.length > 0} + fallback={

{t('home.noPreview')}

} > -

{synth.first_section_title}

-

- {t('home.previewCount', { count: String(synth.first_section_item_count) })} -

+ + {(section) => ( +
+ {section.title} + ({section.count}) +
+ )} +
diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 60d0459..d847b5b 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -133,6 +133,11 @@ export interface Synthesis { created_at: string; } +export interface SectionSummary { + title: string; + count: number; +} + export interface SynthesisListItem { id: string; week: string; @@ -140,6 +145,7 @@ export interface SynthesisListItem { created_at: string; first_section_title: string | null; first_section_item_count: number; + sections_summary: SectionSummary[]; job_id: string | null; } diff --git a/frontend/src/utils/dates.ts b/frontend/src/utils/dates.ts index 15bfd7f..40ad6ba 100644 --- a/frontend/src/utils/dates.ts +++ b/frontend/src/utils/dates.ts @@ -27,6 +27,17 @@ export function formatDate(isoDate: string): string { } } +/** + * Format an ISO date string as "10:30". + */ +export function formatTime(isoDate: string): string { + try { + return format(parseISO(isoDate), 'HH:mm', { locale: fr }); + } catch { + return ''; + } +} + /** * Format an ISO date string as "21 mars 2026" (full month name). */