User journeys · personas & testable scenarios

6 personas for QA / user testing source of truth: docs/parcours-utilisateurs.md

Presentation derived from docs/parcours-utilisateurs.md. A map of every user type and their journeys as testable scenarios (entry point → steps → expected result). Two uses: product overview, and a base for user testing. Grounded in code (File.py::Class.method, Component.vue); symbols win over line numbers.

Status legend

  • active reachable and working today.
  • dormant coded but not reachable (nav disabled, gate never true).
  • wired, no UI exists in DB + algorithm, no admin screen to drive it.

Before you test cross-cutting

1 · Anonymous visitor not logged in

Arrives via SEO / ads / social / magazine. Can browse everything, buy via affiliate, view a shared list and contribute without an account. Nothing requires login unless noted. Note: /i/marketplace/* does NOT imply login; only needsLogin gates certain routes.

1.1 Discovery / browsing active

JourneyEntryBackend + front
Home/content.py::ContentAPI.get_init_site_content · HomeAugust2023.vue (SSR, 1h cache)
Instant search (header)type in search barsearch.py::SearchAPI.instant · SearchBar.vue, InstantSearch.vue
Results + infinite scroll/i/marketplace/recherche?q=products.py::ProductsAPI.search · SearchResults.vue, FacetedProductsList.vue
Explore "Our selections"/i/marketplace/exploretags.py::TagsAPI.list(highlight=true) · ProductsSearch.vue
Selection / tag page/i/marketplace/selections/:slugtags.py::TagsAPI.get · Tag.vue
Brand editorial page/marque/:brand_slugbrands.py::BrandsAPI.get, getContent · BrandPresentationNew.vue
Brand product listing/i/marketplace/marque/:slugBrandsAPI.get + ProductsAPI.search · BrandPage.vue
Preset search/i/marketplace/produits/:slugContentAPI.get_search_preset · StaticSearch.vue
Product page/marketplace/produit/:slug/:codeproducts.py::ProductsAPI.details · ProductPage.vue

Scenario — product page

Open /marketplace/produit/<slug>/<code> → gallery + live buy box + description + "add to list" / "contribute" CTA.

QA: :code is a Sqids (not a rowid); 404 if non-catalog or no offer; the page re-elects live (not the day's display FK).

1.2 Affiliate buy click (redirect + tracking) active

Entry: "buy" button on a product page / list / selection.

Steps: full-page nav to /api/productlink/<product_id>/<from_location> (or /api/linkexternal/<hash>) → 302 to the merchant.

Expected: merchant redirect; a MerchantItemClick is logged only for an affiliate (MerchantItem) offer. Marketplace / LinkProduct offers redirect without click tracking.

views/api.py::product_link_clickget_product_redirect · link built by products.ts::getProductLink. Response is noindex,nofollow.

1.3 Editorial (WordPress magazine + CMS) active

JourneyEntryRefs
Magazine (index / paged)/magazine/:page?ContentAPI.get_last_posts · media/index.vue
Magazine category/magazine/:category_slug/:page?get_last_posts_in_category · ArticleCategoryNew.vue
Article/magazine/:category_slug/:slugContentAPI.get_post · Article.vue
Author page/auteur/:slugget_posts_from_author · Author.vue
Generic CMS page/page/:slugContentAPI.get_page · ContentPage.vue
"À découvrir" / promoted/a-decouvrir/:slugContentAPI.get_content (ns promoted.*) · Promoted.vue
City SEO/naissances/:slug, /a-faire-en-famille/:slugCMS villes.* / tourisme.*
Tools content hub/outils, /outils/:slugCMS outils.* · OutilsLayout.vue
Static pages/informations-utiles, /contactez-nous, /comment-ca-marche, /la-cagnotte-naissancefaq.vue, Contact.vue (→ SiteAPI.contact)…

Newsletter (footer) → site.py::SiteAPI.add_to_newsletter.

1.4 Tools

ToolEntryStatus
Baby clothing size guide/guide-tailles-vetements-bebeToolsAPI.birthtool_get_clothesactive
First-names hub/prenomsToolsAPI.get_top_prenomsactive
First-name search/prenoms/rechercheSearchAPI.prenomsactive
First-name detail (AI + births curve)/prenom/:slugToolsAPI.get_firstname_data · BirthTrend.vueactive
Milla, AI assistant/milla-...ai.py::AIAPI.ask · AIChat.vueactive (IP rate-limit 12/min)
Pregnancy calculator (Grossesse.vue)no route, no importorphaned
Advent calendar/i/marketplace/offres-calendrier-aventneedsLogin (not anonymous)

1.5 SEO / social landings active

1.6 View a shared list + contribute WITHOUT an account active

1.7 Account creation / login active

Entry: /mon-compte-minipouce (target of needsLogin redirects, with ?next=).

Steps: Google, email/password (register/login), forgot-password (DSO link), magic-link ?dso_token.

user.py::UserAPI.login_with_google / register_with_email / login_with_password · UserLogin.vue, GoogleSignInButton. QA: Facebook button is commented (dormant); testlogin only if backend DEBUG.

2 · Logged-in parent birth-list creator

All owner APIs are class-gated (List, Profile, StripeConnect, Lists = auth_required=True). Front guard needsLogin. A user has several lists but exactly one active (creating/restoring one archives the others).

2.1–2.4 List management active

JourneyEntryBackend
Onboarding first list/i/marketplace/inscription · UserStart.vueListAPI.create_list (⚠️ 500 on null/bad date, except christmas)
Manage lists (create/archive/restore).../parametres/listes · MyListsSettings.vuecreate_list, get_archived_user_lists, restore_and_swap_list
List settings (title/date/slug/pot params/partner).../parametres/liste · ListSettings.vueupdate_settings, set_birthdate, ProfileAPI.change_slug, invite_partner, update_many_props
Theme / cover / design.../parametres/theme · ListStylesSettingsNew.vueListAPI.set_cover, update_many_props (design.*)

QA: creating/restoring silently archives the active list; the partner is auto-invited; most pot settings are locked until the gift pot is active.

2.5 Add a product active (3 cases)

CaseEndpointNotes
CatalogListAPI.add_item_to_main(product_id)rejects non-catalog
Custom giftadd_custom_item_to_main · ProductCustomAdd.vuecreates CustomProduct; visible to guests only if the pot accepts cards
External linkadd_external_item_to_main · ProductExternalAdd.vuecreates LinkProduct, check_ugc_url_is_safe (unsafe URL → returns 0, silent fail)

2.6–2.8 Organize · favorites · mark bought active

2.9–2.10 Share · dashboard · profile active

2.11 ⭐ Gift pot & wallet: receive and WITHDRAW money active

A multi-stage Stripe Connect flow. Money actually received = contributions with state="paid_to_wallet". Model Contribution.py, wrapper Stripe.py::UserStripeAccount.

StepEntryBackend
a. Activate the pot (create the Connect account = the "wallet")/i/cagnotte/creation · CreateCagnotte.vueStripeConnectAPI.create_account_from_token
b. Identity check (KYC, required before payout)/i/cagnotte/verification-identite · CagnotteKYC.vueupdate_person_from_token (upload direct to uploads.stripe.com)
c. See received contributions/i/mon-compte/cagnotte · CagnotteOverview.vueListAPI.get_cagnotte_contributions (filters paid_to_wallet)
d. Add an IBAN/RIBin cagnotte-withdraw · AddIBAN.vueadd_iban_to_account
e. ⭐ Withdraw funds (payout)/i/cagnotte/withdraw · CagnotteWithdraw.vueget_balance + request_payout(bank_account_id)

Scenario — withdraw

CagnotteOverview → "Récupérer ma cagnotte" → (redirects to KYC if not done) → Available/Soon balance → pick/add bank → "Payout of X€" → success (24–72h).

QA: button disabled without balances.available + a selected bank; a pending_payout blocks a new request; ~5-day settlement delay after each contribution; countries FR/BE/CH/LU; single adult beneficiary.

3 · Contributor friend/relative, often not logged in

Arrives via a share link. No account required: all PublicList.* is unauthenticated. A contributor is a Contributor (email+name), not a User: they never authenticate (no "create contributor account" journey).

4 · Admin back-office

/a/admin, endpoints @api.need_admin / admin_required=True. No front route guard: only AdminBase.vue calls AdminAPI.check_access; security rests on the per-endpoint 404. Monochrome admin design system (admin.css, admin-*).

AreaEntryKey actions (all active unless noted)
Brands/a/admin/brands · EditBrand.vueset_featured_product, unindex / unindex_from_merchant, alias/consolidation (add_aliasBrandAlias), labels/logo. ⚠️ the brand editorial page is edited under Site ▸ Content (ns brand), not here.
Merchants/a/admin/merchantssettings (name/slug/weight/enable-disable, update_merchant), feed onboarding (MerchantFeedEditor.vue, AI field guess), Shopify config, link matching, stats + CSV, impersonation (impersonate_merchant).
Products (page editor)/a/admin/products · EditProduct.vueupdate_rank, set_substitue, toggle_label, AI rewrite (rewrite_description) → update_smart_description, select_cover, additional images, product tags. Election diagnostic in the header: Best (live) / Elected (daily FK read by cards) / Source (content) — explains a card↔page gap.
Editorial/a/admin/content, /tags, /menuLexical CMS (contents + templates), featured on Tag/Brand, menus, selections by tag, preset searches, theme covers. Magazine WordPress = read-only proxy (cache clear only).
Analytics/a/admin/, /analytics-debugdashboard (registrations, top products, hot users, stale feeds, pending payouts), ClickHouse debug, per-merchant/brand charts.
Mailing/a/admin/mailingLexical email editor, templates, send campaign (audience filters → send_email Celery), transactional editor (AI var detection).
Other/offers, /users, /payouts, /affiliaterulesoffers/vouchers, users (impersonation, edit), payouts (execute_payout), affiliate link rules.

4.8 Wired but NO UI (know this for testing)

LeverBackendStatus
BrandWeight (brand↔merchant buy boost)Brand.py::BrandWeight, read in electionDB-only
MerchantPriority (content source)Product.py::MerchantPriority (content_score)DB-only (#219)
Featured product on MenuMenu.py::Menu.featured_product_idDB-only
Global ProductClassification (taxonomy)Liste.py::ProductClassificationseeded, no admin CRUD
misc.vue, editorplayground.vueactiveoff-nav, direct URL only

5 · Merchant business user

Hierarchy: Merchant (feed affiliate, no login) → MerchantBusiness (login, state invited/pending/active/disabled) → MarketplaceMerchant (Shopify connector). Gate auth_merchant_required=True (JWT user_type:"merchant", decoded on POST → g.merchant).

JourneyEntryRefs + status
Shopify onboarding (OAuth, embedded)/shopify-bridge/sync?id_token=…shopify_sync.py::ShopifySyncAPI.authenticate · SyncApp.vue · active (needs a real store)
Signup wizard/i/shopify/setup · SetupAccount.vuebusiness.py::BusinessAPI.create_merchantcomplete_profile · active (lands pending, admin flips to active)
Standalone login/m/merchant/login · MerchantLogin.vuelogin_with_password / login_with_google · active
Dashboard / analytics/m/merchant · MerchantDashboard.vuemerchant_analytics.py::MerchantAnalyticsAPI.get_dashboard_kpis (ClickHouse + Postgres) · active
Browse products/m/merchant/products · MerchantProducts.vueMerchantAnalyticsAPI.get_products · active
Sales-channel curation (Shopify channel)/m/merchant/sales-channel · MerchantSalesChannel.vueMerchantSalesChannelAPI.publish_products (set_channel_membership, max 50) · active (needs Shopify + write_publications)
Storefront page + curation/m/merchant/page, /page/productsMerchantPageAPI, MerchantShopAPI (curation product-keyed, EAN-stable; entries whose offer drops from the feed are hidden, not lost) · dormant (nav enabled:false)
Public storefrontminipouce.fr/<slug> (/:slug resolver)public_slug.py::PublicSlugAPI.resolvebuild_public_page · active
Stripe Connect (merchant payouts)/m/merchant/stripeStripeConnectBusinessAPI · dormant (marketplace mode)
Affiliate feed onboardingadmin console onlyadminfeed.py::AdminFeedAPI (admin_required) · active but NOT merchant self-service
Do not confuse Two product-selection surfaces: MerchantSalesChannel (live, Shopify channel membership) vs the dormant MerchantShop storefront curation. Also minipouce.fr/<slug> (merchant storefront) is a different concept from /i/marketplace/marque/<slug> (product-brand page).

6 · Marketplace buyer future dormant

The direct-purchase flow is coded but unreachable; exercising it needs a manual seed.

Real bug (dormant) The contributor "buy a marketplace product from the list" path is doubly dead: same gate + list_prepare_contributor_order calls MarketplaceOrder.CreateContributorOrder which does not exist (AttributeError) + the contributor_order_checkout route is commented out. File an issue before ever enabling marketplace; do not spend QA cycles here.

What to test by persona

PersonaLogin?Key active zonesDo NOT test (dormant)
Anonymous visitornobrowsing, search, product page, affiliate buy, magazine/tools, view list + contribute without account, signupadvent calendar (auth), pregnancy calculator (orphaned)
Logged-in parentyeslist (create/organize), 3 add types, favorites, share, dashboard/profile, gift pot + Stripe withdrawalmarketplace buy, explore_new
Contributornoview list, contribute (card), self_bought, cancel by tokenmarketplace buy from list (dead)
Adminyes (admin)brands, merchants, product page, editorial/CMS, analytics, mailing, users/payoutsBrandWeight, MerchantPriority, featured Menu (no UI)
Merchantyes (business)Shopify onboarding, dashboard analytics, sales-channel curationstorefront (page+products), merchant Stripe, self-service feed
Marketplace buyeryeseverything (needs manual seed)