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 — UploadUpload.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 ColumnsMappingItem · 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
PreviousNext
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.
2cTraitementProgress type="circle"
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.
2dSuccè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.
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.
2eConfirmation — 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
CancelCreate 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.
2fAtterrissage — la liste n'est plus videNamespaceTable · 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.
2gLe payoff — ciblage de campagneAudiencesSelect · 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
ResultButtonAlertTag
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 valuesOffice · 6 valuesLanguages · 7 values
Create an audience from these categoriestracking: invite:csv:audience:click
Go to my upload historyResubmit 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.GroupAlertSelectListInput
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
TreeSelectEmptySpinTooltip
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
« 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 CategoryImport 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
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.
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 ».
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.
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
« 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.
<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.
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
ConnectedLast 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 · 24Legal · 9Madrid · 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.