flow complet · users/add/csv → audiences → campaigns
Import CSV → audience dynamique, de bout en bout
Sept écrans qui suivent le vrai parcours : le StepsLayout à deux étapes, l'écran de progression, le Result de succès, puis le modal Create new audience et la liste NamespaceTable tels qu'ils existent déjà. Les seuls ajouts sont encadrés en pointillés indigo — le reste est la copy et les composants en prod.
2a Étape 1 — Upload Upload.Dragger
1Import
2Map Columns
Click or drag file to this area to upload you list
Use a .csv file with at least first name, last name and email address like in our example file.
Next
Inchangé. Papa.parse lit le fichier côté client dès le dépôt : les colonnes et leurs valeurs sont connues avant toute requête serveur — c'est ce qui rend les suggestions des écrans suivants possibles sans backend.
2b Étape 2 — Map Columns MappingItem · Radio.Group
Import
2Map Columns
office
Paris
Lyon
Remote
Mapped Successfully
Create a new category
List of choices
Office
6 values detected. This category can become a matching rule — “Office is the same” for in-person coffees.
Suggest audiences from this column after the import
Previous Next
La case cochée ne crée rien tout de suite : elle marque la colonne comme candidate, pour que l'écran de succès n'ait pas à devenir un formulaire.
2c Traitement Progress type="circle"
8 /13
Hold on Ouadia, we’re processing...
Please don’t quit or refresh the page until the import is complete
Inchangé. L'import est découpé en chunks côté front ; c'est aussi le moment où le back crée les catégories du mapping.
2d Succès — le seul écran vraiment modifié Result · extra
Done, Ouadia!
We’ve scheduled your list for import. You’ll be notified as soon as it’s processed.
3 categories were created. Turn them into audiences to target your programs.
A dynamic audience recomputes itself at every import — you never have to reselect people by hand.
Dynamic
New joiners
Start date is within the last 90 days
86 coworkers
Dynamic
Paris office
Office is Paris
412 coworkers
Dynamic
Managers
Has at least one direct report
137 coworkers
Create 2 audiences users:audiences:created { type: 1, source: 'csv' }
Go to my upload history Resubmit a new list
Les règles sont écrites en langage de filtre RandomCoffee (« Office is Paris »), donc modifiables ensuite dans audiences/edit sans repasser par l'import. Les deux boutons existants restent, en secondaire.
2e Confirmation — modal existant, pré-rempli
Create new audience
Audience name
New joiners
Audience type
Dynamic audience
Dynamic lists automatically update over time people who match the list criteria
Static audience
Static lists don't automatically update as your users change. A static list is a snapshot of a specific moment.
Use it right away in a program — opens the targeting step next
Cancel Create audience
C'est le modal de audience-filters.tsx, avec ses RadioCards Dynamic/Static et son okText « Create audience » : nom et type arrivent pré-remplis, l'admin n'a qu'à valider.
2f Atterrissage — la liste n'est plus vide NamespaceTable · message.success
Your audience has been created successfully
Audiences
Create audience
Name
Type
People
New joiners
Dynamic
86
Paris office
Dynamic
412
Le toast et la redirection existent déjà dans onSaveAudience. Seule différence : l'admin arrive sur deux lignes au lieu de l'état vide, et le milestone 1g se coche.
2g Le payoff — ciblage de campagne AudiencesSelect · PeopleCount
Onboarding Buddies
Audience
New joiners (86 users)
86 coworkers
Matchmaking rules
Office
Is the same
Both the audience and the rule come from the columns you just imported. Nothing was typed twice.
Fin du flow : l'admin arrive au builder avec un ciblage et une règle déjà disponibles, au lieu de « All employees » et zéro règle.
RandomCoffee/randomcoffee @ master · frontend/src
Nudges audiences & catégories, dans le vrai code
Chaque bloc montre le contexte réel — fichier, composants Ant utilisés, copy gettext() existante, événement de tracking — puis la maquette du nudge, construite avec les mêmes composants Ant Design que la page qu'elle modifie. L'ajout proposé est toujours encadré en indigo.
ajout proposé
existe déjà en prod
1a
Fin d'import CSV — refermer la boucle sur les catégories créées
/admin/users/invite
FICHIER
users/add/csv/index.tsx
→ UploadStatus / extra
COMPOSANTS ANT
Result Button Alert Tag
CE QUE FAIT DÉJÀ LE CODE
getUsersUploadObj() renvoie { users, categories } : les catégories sont bien créées par l'import. onUploadOver re-dispatch categories/getCategories.
LE MANQUE
Les deux seuls boutons sont Go to my upload history et Resubmit a new list. Aucune mention des catégories qui viennent d'être créées.
TRACKING EXISTANT
popin:invite:csv:done
Done, Ouadia!
We’ve scheduled your list for import. You’ll be notified as soon as it’s processed.
3 categories were created from your file
Matching rules read categories. Turn one into an audience to target a program without picking people by hand.
Department · 11 values Office · 6 values Languages · 7 values
Create an audience from these categories tracking: invite:csv:audience:click
Go to my upload history Resubmit a new list
1b
Étape « Map Columns » — dire à quoi sert la catégorie qu'on crée
StepsLayout · step 2/2
FICHIER
users/add/csv/mapping_item.tsx
→ NewFieldsOptions
COMPOSANTS ANT
Radio.Group Alert Select List Input
COPY EXISTANTE
« Ignore this field » · « Map to existing category » · « Create a new category » · « Name of your category »
LE MANQUE
Le radio « Create a new category » ne dit jamais à quoi une catégorie sert. Deux lignes de Alert type="info" suffisent, dans un flux où l'admin est déjà en train de nommer ses attributs.
Import
2Map Columns
department
Engineering
Sales
Product
Mapped Successfully
Ignore this field
Map to existing category
Create a new category
List of choices
Department
This category can be used as a matching rule — for example “Department is different” to avoid pairing teammates.
Also create one dynamic audience per value (11 audiences)
1c
CategoriesTreeSelect — l'état vide qui n'existe pas
ciblage de campagne
FICHIER
components/categories-tree-select/index.tsx
COMPOSANTS ANT
TreeSelect Empty Spin Tooltip
COPY EXISTANTE
placeholder : « Select the categories of coworkers that will be part of the campaign »
LE MANQUE
Le TreeSelect n'a pas de notFoundContent : avec 0 catégorie, le dropdown est vide et muet. À côté, AudiencesSelect a déjà le bon pattern (Empty + Button « Create a new Audience »).
PATTERN À COPIER
audience-select/index.tsx
→ notFoundContent
AUJOURD'HUI
Select the categories of coworkers…
No Data
Un admin sans catégorie voit un menu vide. Rien ne lui dit qu'il doit d'abord en créer une, ni où.
PROPOSÉ
Select the categories of coworkers…
Categories let you categorize your coworkers by things like departments and location.
Create New Category
Copy et bouton repris mot pour mot de categories/empty.tsx, déjà traduits. Rien à écrire, rien à faire relire.
1d
Règles de matching — un Select vide au lieu d'une porte d'entrée
campaigns/rules
FICHIERS
campaigns/rules/by-category.tsx
campaigns/rules/audiences.tsx
COMPOSANTS ANT
Row / Col Select Space Flex
COPY EXISTANTE
« Choose a category » · comparateurs « Is the same » / « Is different » / « Has the most in common » · pour les audiences « Match with » / « Don't Match with »
LE MANQUE
by-category.tsx filtre les catégories par type puis les mappe : avec 0 catégorie éligible, l'admin obtient « Choose a category » sur un menu vide, sans issue. Le deuxième RuleSelector reste vide lui aussi tant que rien n'est choisi.
Matchmaking rules
Choose a category
Select
No categories for now
Custom categories such as “Team”, “Floor” or “Expertise” allow you to create matching rules for your campaigns.
Create New Category Import from a .csv
Même traitement dans rules/audiences.tsx : les deux AudiencesSelect n'affichent l'Empty avec CTA qu'après une recherche infructueuse — jamais à l'ouverture.
1e
PeopleCount — « No corresponding profiles found » devient une action
campagne · broadcast · permissions
FICHIER
categories-tree-select/index.tsx
→ export const PeopleCount
RÉUTILISÉ PAR
CategoriesTreeSelect et AudiencesSelect — donc un seul correctif couvre le ciblage de campagne, les broadcasts et les permissions.
COPY EXISTANTE
« %(count)s coworkers » · « No corresponding profiles found » · variante Free : « only the first 20 will be included »
LE MANQUE
Le composant accepte déjà une prop onClick, mais elle n'est pas passée dans le cas d'erreur. L'avertissement est un cul-de-sac alors que le hook existe.
AUJOURD'HUI
Sales Berlin
No corresponding profiles found
PROPOSÉ
Sales Berlin
No corresponding profiles found. Nobody in Sales is based in Berlin.
Keep Sales only · 64 Keep Berlin only · 96
Save this filter as an audience instead
1f
Liste des coworkers — la sélection multiple propose l'audience
/admin/users
FICHIERS
users/list/selected_users_alert.tsx
users/list/audience-filters.tsx
layouts/add-to-audience-modal.tsx
CE QUI EXISTE DÉJÀ
La barre de sélection (Text + Divider type="vertical" + ActionsDropdown), le modal Add Selected Users to Audiences, et le modal Create new audience avec ses RadioCards Dynamic/Static.
LE MANQUE
La création d'audience est enterrée dans le ActionsDropdown. Elle mérite d'être un Link visible dès la première sélection, au même niveau que « Select all coworkers ».
TRACKING EXISTANT
users:audiences:created { type }
64 out of 1,248 selected Select all coworkers Actions Save as an audience
Sarah Rousseau Sales
Thomas Meunier Sales
Create new audience
Audience name
Sales team
Audience type
Dynamic audience
Dynamic lists automatically update over time people who match the list criteria
Static audience
Static lists don't automatically update as your users change. A static list is a snapshot of a specific moment.
Cancel Create audience
1g
Onboarding admin — le 4e milestone qui manque
infra déjà câblée
FICHIERS
components/onboarding/admin-milestones.tsx
utils/onboarding.ts
CE QUE DIT LE CODE
adminCheckers = [hasCampaigns, hasCategories, hasInvitedCoworkers]
Trois milestones : users, categories, campaigns. Pas de hasAudiences.
MAIS
getOnboardingSteps() mappe déjà la route /admin/users/audiences vers ONBOARDING_MODAL_TYPES.AUDIENCES. Le canal existe, il n'a simplement pas de contenu.
TRACKING À CALQUER
onboarding:add:categories:click
→ onboarding:add:audiences:click
Finish your onboarding
2 / 4
Add your first coworkers
Add your first categories
Create your first audience
Audiences let you reuse a group of coworkers — “New joiners”, “Paris office”, “Managers” — across every program and broadcast, instead of picking people one by one.
Create an audience
Launch your first campaign
Placer l'audience avant la première campagne : c'est l'ordre dans lequel le produit s'utilise, et ça rend le ciblage disponible au moment où l'admin ouvre le builder.
1h
Page Audiences vide — proposer au lieu de constater
/admin/users/audiences
FICHIER
admin/audiences/index.tsx
→ NamespaceTable emptyOptions
COPY EXISTANTE
« You’ve got no audiences for the moment » · bouton « Create audience » · colonnes Name / Type / People avec Tag Dynamic (SyncOutlined) ou Static (FileTextOutlined)
LE BUG DE PARCOURS
onAddAudienceClick redirige vers /account/{slug}/users — l'admin est éjecté vers une liste sans explication, alors que la création se fait via le modal de audience-filters.tsx.
Audiences
Create audience
You’ve got no audiences for the moment
Start from your categories
Dynamic
New joiners
Start date · last 90 days
86 coworkers
Create
Dynamic
Paris office
Office · is Paris
412 coworkers
Create
Dynamic
Managers
Has at least one direct report
137 coworkers
Create
1i
Use Case Library — le pattern maison, décliné pour les audiences
/admin/campaigns/templates
FICHIER
campaigns/templates.tsx
→ UseCaseLibrary
LE PATTERN EXISTANT
Un Alert type="info" showIcon avec action={<CreateCampaignButton />} et une icône NotificationOutlined : « Find your new team ritual with Programs ». C'est le seul nudge produit déjà en place — donc le format de référence.
LA DÉCLINAISON
Le même composant, conditionné à !hasAudiences(state), sur la page où l'admin choisit son programme — juste avant qu'il ait à définir un ciblage.
Find your new team ritual with Programs
Start a RandomCoffee ritual using one of these programs and get set up in just 3 clicks.
Your own template
Decide who takes part before you pick a program
You have 3 categories and no audience yet. Save a group once — “New joiners”, “Paris office” — and reuse it in every program and broadcast.
Create audience
Onboarding Buddies
Pair every new joiner with a colleague from another team.
Onboarding
Cross-team Intros
Break the silos with monthly introductions across departments.
Culture
1j
Deux boutons désactivés à expliquer
liste coworkers · permissions
FICHIERS
users/list/categories-header.tsx
settings/permissions/edit.tsx
CE QUE DIT LE CODE
<Dropdown disabled={categories.length === 0}> : le bouton œil devient inerte, sans Tooltip. Côté permissions, « Only specific audiences » ouvre un AudiencesSelect qui peut n'avoir aucune option.
POURQUOI ÇA COMPTE
La page permissions est souvent le premier endroit où un admin lit le mot « audience ». Un Tooltip y fait office de définition.
LISTE DES COWORKERS
Create a category to show extra columns here — for example Department or Office.
Create New Category
PERMISSIONS ADMIN
Audiences
Can add audience:
Can see & manage audiences: Only specific audiences
Authorized audiences:
Select an Audience
You have no audiences yet. An audience is a saved group of coworkers you can scope an admin, a program or a broadcast to.
Create your first audience
1k
Sync HRIS / SCIM — un nudge unique au point de passage commun
mass_upload · create_categories
CE QUE DIT LE CODE
Toutes les sources — CSV, HRIS, SCIM — passent par la même création de catégories côté backend. Un seul point d'accroche couvre les trois.
DÉJÀ ANNONCÉ DANS L'UI
L'écran HRIS parle déjà d'ajouter les nouveaux qualifiés aux campagnes « selon des critères d'audience ». La promesse est faite, l'outil pour la tenir n'est pas proposé.
FORMAT
Un Alert type="info" sur la page d'intégration, alimenté par le diff du dernier sync — pas une notification poussée.
Workday
Connected Last sync 2 hours ago · 1,248 coworkers
This sync added 4 new values to Department and 1 to Office
Your dynamic audiences pick these up automatically. The new values are not covered by any audience yet.
Data & Analytics · 24 Legal · 9 Madrid · 31 +2
Create audiences
Fréquence à borner : une fois par sync qui introduit de nouvelles valeurs, et dismissible définitivement. Sinon la bannière devient du bruit à chaque arrivée d'employé.
Ce que ces 11 nudges ont en commun
Aucun n'introduit un concept nouveau ni un écran nouveau. Neuf réutilisent une chaîne gettext() déjà traduite et un composant déjà monté sur la page. Deux seulement demandent un champ backend : hasAudiences pour l'onboarding, et le diff de valeurs pour la bannière de sync.
6 états vides à remplir — copy déjà écrite
3 Alert info sur le pattern de templates.tsx
2 nécessitent un peu de backend