← Tous les designs

gauth-0005-dashboard-v3-reporting · frontend/src

Spec — Dashboard admin v2

Référence visuelle : Admin Dashboard v2.dc.html (ce projet). Écran cible : /account/:slug/admin/dashboard — remplace le contenu de frontend/src/pages/account/admin/dashboard/index.tsx.

Le but de ce document : décrire chaque composant à introduire, ce qui change par rapport à l'existant, et de quelles données/endpoints chacun dépend. Tout ce qui est marqué [backend] n'est pas servi aujourd'hui.


1. Ce qui change par rapport à l'existant

Existantv2
PageLayout + 4 <Section> empilées, contenu limité à ~900pxPage pleine largeur (1230px), grille de cartes
3 OverviewCard (dont une primary violette)4 MetricTile avec valeur, variation et sparkline
Graphe de participation en pleine largeur, filtres dans la barre d'ongletsGraphe dans une carte, filtres remontés dans un header de page unique
Campaigns Stats : Tabs (All/Running/Inactive) + Table avec expand des sessionsProgramsTable : une seule table, statut en Tag, opt-in en Progress, prochaine date de match
Top Randomers : Table 5 colonnes + recherche + exportTopRandomersList : liste compacte avatar / nom / équipe / compteur
FunnelCard, SeatsCard, NextMatchesCard, CategoryBreakdownTable (nouveaux)

Ce qui ne change pas : la route, le garde-fou de permission (EmptyPermission + getPermissions().hasMetricsAccessOverallPermission()), le thème antd (layouts/config.tsx, prefixCls="rc", primaire #4654f1), le modèle umi dashboard.


2. Structure de la page

<EmptyPermission hasPermission={...hasMetricsAccessOverallPermission()}>
  <PageLayout title={gettext('Dashboard')} actions={[<DashboardFilters />]}>
    <MetricsRow />                        {/* 4 colonnes */}
    <Row gutter={16}>                     {/* 16 / 8 */}
      <FunnelCard />  <SeatsCard />
    </Row>
    <ParticipationCard />                 {/* pleine largeur */}
    <Row gutter={16}>
      <ProgramsTable />  <NextMatchesCard />
    </Row>
    <Row gutter={16}>
      <TopRandomersList />  <CategoryBreakdownTable />
    </Row>
  </PageLayout>
</EmptyPermission>

Grille : Row gutter={[16, 16]}, colonnes Col span={16} / Col span={8} (soit 2fr / 1fr). Les <Section> et leurs titres Overall KPIs, Participation, Campaigns Stats, Top Randomers disparaissent : chaque carte porte son propre titre. components/section reste utilisé ailleurs, ne pas le supprimer.

Fond de page --rc-color-bg-layout (#fbfbfb), cartes en #ffffff sans ombre (boxShadow: 'none').


3. Composants

3.1 DashboardFilters

pages/account/admin/dashboard/filters.tsx (nouveau) — remplace l'usage de graphs/filters.tsx dans la barre d'onglets.

Contenu : Segmented (30D / 3M / 6M / 12M) + RangePicker + FiltersDropdown (réutiliser tel quel depuis dashboard/graphs/filters.tsx).

Props : aucune (branché sur le modèle dashboard).

3.2 MetricTile + MetricsRow

components/metric-tile/index.tsx (nouveau, à exporter depuis components/index.ts).

type MetricTileProps = {
  label: string;          // ex. gettext('Members')
  value: React.ReactNode; // déjà formaté (espace insécable comme séparateur de milliers)
  delta?: number;         // variation en % ou en points
  deltaUnit?: '%' | 'pts';
  trend?: number[];       // série pour la sparkline, ordre chronologique
  loading?: boolean;      // -> <Skeleton active paragraph={false} />
};

Rendu : Card (body padding: 20), label 13px colorTextTertiary, valeur 28px / 600 / letter-spacing: -0.02em, delta 13px coloré par le signe (colorSuccess / colorError), sparkline 34px de haut en dessous.

Sparkline : <Line> bizcharts sans axes ni légende (padding={0}, <Axis visible={false} />) — c'est la lib déjà utilisée dans le projet, ne pas en introduire une seconde. Le rendu SVG du fichier de design n'est qu'une maquette.

Les 4 tuiles :

TuileValeurSource
Membersnombre de membreshigh_level_stats.engaged_users_count (existant) ou reportingaverage_members_count
Unique members invitedmembres uniques invitésreportingoptins_sent_count
Opt-in ratetaux d'opt-inhigh_level_stats.optin_conversion (existant)
Meetings scheduledRandomCoffees planifiésreportingmeetings_scheduled_count

[backend] delta et trend : aucun endpoint ne renvoie de comparaison période / période. Deux options, à trancher avant implémentation :

  1. le front appelle reporting deux fois (période courante + période précédente de même durée) et calcule la variation ;
  2. high_level_stats renvoie previous_* et une série mensuelle.

L'option 1 est faisable sans backend : POST reporting accepte déjà { reports, campaigns, start, end }, et randomcoffee_funnel_trend renvoie déjà une série mensuelle (label: 'YYYY-MM') qui alimente directement les sparklines. C'est le chemin recommandé : un seul appel reporting avec reports: ['randomcoffee_funnel_trend'] fournit les 4 séries et les valeurs courantes.

3.3 FunnelCard

pages/account/admin/dashboard/funnel.tsx (nouveau).

4 étapes en barres horizontales empilées verticalement, chaque étape affichant sa valeur et son taux de conversion par rapport à l'étape précédente :

ÉtapeChampTaux affiché
Members in platformaverage_members_count— (référence, barre 100%)
Unique members invitedoptins_sent_countinvitation_rate = invités / membres
Unique members matchedunique_members_matched_countmatch_rate = matchés / invités
Meetings scheduledmeetings_scheduled_countscheduling_rate = planifiés / matchés

Les trois taux sont déjà calculés dans reporting/randomcoffee_data_per_category.tsx : extraire cette logique dans utils/reporting.ts (computeRates(row)) et l'appeler des deux côtés plutôt que de la dupliquer.

Largeur de barre = value / members * 100, dégradé de teinte du primaire du plus clair au plus foncé (#e8eafd, #c3c8f9, #8e97f5, #4654f1). Hauteur de barre 28px, borderRadius: 6.

Source : POST reporting avec reports: ['randomcoffee_funnel_trend'], en sommant/prenant le dernier point de la série selon la métrique. Aucun nouvel endpoint.

3.4 SeatsCard

pages/account/admin/dashboard/seats.tsx (nouveau).

Progress type="dashboard" (140px) + Tag du plan + ligne X of Y seats used + lien « Manage » vers la page de facturation.

3.5 ParticipationCard

Reprend dashboard/graphs/participation.tsx sans changement de données : même OptinsRate (série total = Users reached, accepted = Participants), même endpoint participations_stats via dashboard/updateParticipations.

Changements :

3.6 ProgramsTable

Remplace campaigns-following/index.tsx. Même source : dashboard/getCampaignsStatscampaigns_stats.

Colonnes :

ColonneContenuChamp
Programnom en lien + sous-ligne X reached · Y coffeesname, active_users_count, randomcoffees_count
StatusTag bordered={false} vert « Running » / neutre « Inactive »status
Opt-in rateProgress size="small" + valeuroptin_conversion
Next matchdate du prochain envoi[backend]

Ce qui est retiré, à confirmer avant de coder :

[backend] campaigns_stats ne renvoie pas de date de prochain match. Elle est dérivable côté front depuis la campagne (classes/campaign.ts, getClosestDate(refDate, date, frequency) dans utils/dates) si la fréquence et la date de référence sont dans le payload ; sinon l'ajouter à campaigns_stats.

3.7 NextMatchesCard

pages/account/admin/dashboard/next-matches.tsx (nouveau).

3 prochains envois : pastille date (mois + jour), nom de campagne, sous-ligne X reached · fréquence · canal. Pied de carte : total des invitations partant dans la semaine.

[backend] même dépendance que la colonne Next match. Si la date de prochain match n'est pas disponible, ne pas livrer cette carte dans la v1 et passer ProgramsTable en pleine largeur.

3.8 TopRandomersList

Remplace top-randomers/randomers.tsx. Même source : dashboard/getTopRandomerstop_randomers.

Liste de 5 lignes : Avatar (initiales, couleur dérivée de l'index), nom + équipe, compteur à droite. Séparateur 1px solid #f0f0f0 entre les lignes.

3.9 CategoryBreakdownTable

Réutilise le rapport randomcoffee_data_per_category (déjà écrit, page /admin/reporting).


4. Données — récapitulatif

Endpoints déjà disponibles, aucun à créer pour le cœur de la page :

EndpointViaAlimente
companies/:id/high_level_stats/dashboard/stats/index.tsxMembers, Opt-in rate
participations_statsdashboard/updateParticipationsParticipationCard
engagement_statsdashboard/updateEngagementParticipationCard (By category)
campaigns_statsdashboard/getCampaignsStatsProgramsTable
top_randomersdashboard/getTopRandomersTopRandomersList
reporting (POST {reports, campaigns, start, end})pages/account/admin/reportingMetricTiles, FunnelCard, SeatsCard, CategoryBreakdownTable

À ajouter au modèle models/dashboard.ts : un effect updateReporting({ start, end, campaigns, reports }) + un reducer setReporting, sur le même patron que updateParticipations (setLoadingcallset*). Le service correspondant va dans services/dashboard.tsx à côté de getParticipations, en réutilisant getUrl('reporting').

Manques côté backend, par ordre d'impact :

  1. date du prochain match par campagne (bloque NextMatchesCard et la colonne Next match) ;
  2. comparaison période précédente pour les deltas (contournable par un second appel reporting) ;
  3. équipe/département dans top_randomers (contournable : afficher l'email).

5. Thème et détails d'implémentation


6. Fichiers

Nouveaux :

pages/account/admin/dashboard/filters.tsx
pages/account/admin/dashboard/funnel.tsx
pages/account/admin/dashboard/seats.tsx
pages/account/admin/dashboard/next-matches.tsx
pages/account/admin/dashboard/metrics.tsx
components/metric-tile/index.tsx
utils/reporting.ts

Modifiés :

pages/account/admin/dashboard/index.tsx        nouvelle composition
pages/account/admin/dashboard/graphs/index.tsx Segmented au lieu des Tabs
pages/account/admin/dashboard/campaigns-following/index.tsx  -> ProgramsTable
pages/account/admin/dashboard/top-randomers/randomers.tsx    -> liste
pages/account/admin/models/dashboard.ts        effect reporting
services/dashboard.tsx                         getReporting
components/index.ts                            export MetricTile

Inchangés et réutilisés : dashboard/graphs/filters.tsx (FiltersDropdown, CategoriesDropdown), dashboard/graphs/participation.tsx (OptinsRate), dashboard/graphs/engagement.tsx, reporting/randomcoffee_data_per_category.tsx, utils/dates.ts, components/section.


7. À trancher avant de coder

  1. Les onglets All / Running / Inactive et l'expand des sessions sont-ils utilisés ? Si oui, ils reviennent dans ProgramsTable.
  2. La recherche coworker des Top Randomers est-elle utilisée ?
  3. Les deltas : second appel reporting (front seul) ou évolution de high_level_stats (backend) ?
  4. La date de prochain match est-elle dérivable du payload actuel de campaigns_stats ?