feat(onboarding): journey PR C — flow component + /onboarding swap behind flag (#1143)
* refactor(onboarding): extract first-year defaults + shared TIC lookup client (journey PR A)
First of four PRs replacing the onboarding wizard with the journey flow
(dev_docs/onboarding_migration_plan.md, local). No UI change.
- Move deriveFirstYearDefaults + parseStartMonthDay out of
WelcomeOnboarding into lib/company/first-year-defaults.ts and unit-test
them (11-vs-13-months boundary, UTC month seeding, malformed input).
- Add the missing computeFiscalPeriod unit tests (calendar year, brutet
ar, first year short/extended, EF calendar-year rule, period names,
BFL 3 kap. 6-18 month window errors).
- New shared fetchCompanyLookup() client: the single client path to the
Lens-backed /lookup, typed outcomes (found / not_found / disabled /
error / aborted), never throws. Fixes the 403/404 conflation: the
dispatcher's 404 ("Extension not found") and feature-flag 503
(EXTENSION_DISABLED) now degrade silently instead of rendering as
"company not found"; only the TIC handler's own 404 does.
- Step2CompanyDetails consumes the helper; identical UX otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(onboarding): journey state machine reducer with full branch coverage (journey PR B, 1/3)
Pure reducer for the journey onboarding: owns every transition and every
CompanySettings write; the component layer only renders steps, runs the
single TIC lookup, and calls the server action.
Encodes the plan's invariants: entry-snapshot history (Back rolls answers
AND stations), lookupRan gates fact-vs-question per field (BankID prefill
without lookup degrades to questions), vat_registered is never defaulted
without lookup data or an explicit answer, entity change wipes downstream,
org_number_invalid bounces to the Företaget station, station jumps rewind
to a station's first step.
34 unit tests: AB/EF found, not-found manual, ceased, BankID prefill
(found + degraded + disabled), first year, brutet år, moms nej, Back from
every step, station jumps, server-error bounces.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(onboarding): journey visual primitives + sandbox gallery (journey PR B, 2/3)
Ports the founder-approved concept (artifact c82c9358) to React:
- JourneyOrb: 320-particle canvas sphere with comet travel, check morph
and the monogram finale (glyph sampled live from --font-display). Own
component per plan, NOT thinking-orbs. rAF pauses on document.hidden;
reduced motion renders static frames.
- JourneyTrack: five stations with inked answers; completed stations are
keyboard-accessible jump-back buttons; answers mirrored to an aria-live
region.
- Question primitives: Question (ink title + "?" popover, Esc closes),
ChipRow (fly-to-orb ghost), YearBand (springy fiscal-year preview),
JourneyDatePicker (year -> month by name -> day), AddressFields
(Enter-chained, skippable).
- journey.css: concept stylesheet namespaced under .jny on app tokens,
incl. the no-scroll composition (100dvh + optical-centering balance
spacer) and the dawn layer.
- /sandbox/journey: internal primitive gallery (auth-free sandbox path),
demo data only: this page makes ZERO TIC calls.
i18n note: primitives are copy-agnostic (strings via props); the real
flow's sv/en keys land with their consumer in PR C.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: log journey reducer location decision
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(onboarding): annotate ENTITY_PICKED settings as Partial<CompanySettings>
The wipeDownstream return narrows against the inferred initializer type;
tsc strict rejects the reassignment without the explicit annotation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(onboarding): journey flow component behind NEXT_PUBLIC_ONBOARDING_JOURNEY (journey PR C)
OnboardingJourney wires the PR B reducer and primitives into the real
flow and swaps /onboarding behind the flag (wizard remains the default).
Data + error handling parity with the wizard, structurally enforced:
- identical settings payload to createCompanyFromOnboarding (incl.
ticLookup snapshot, derived vat_number, first-year fields through
computeFiscalPeriod), same /api/log error logging, same
org_number_invalid bounce (now to the Foretaget station), period
validation before submit, generic failure -> retry on the method step.
- lookup degradation: disabled surface silent, transient error shows the
advisory line; either way every fact the lookup could not provide is
asked as a question (address, F-skatt, fiscal year, VAT).
- advisory dup check rides the org submit (internal endpoint, never
blocks), rendered as a quiet fact-line note.
TIC budget: exactly ONE fetchCompanyLookup per confirmed orgnr: Enter on
the manual path, or the auto-submitted BankID deep link (which replaces
the wizard's preverified suppression per the plan addendum). No
debounce-per-keystroke; the journey strictly reduces Lens volume.
Finale per the approved concept: narrated real server steps while the
action runs, check morph, company-initial monogram, Foretagsprofil card,
conditional notes, dawn progression; sv+en strings (128 keys each).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore: trigger preview with NEXT_PUBLIC_ONBOARDING_JOURNEY=true (branch-scoped)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b771c1f923
commit
51ca574ca4
+129
-1
@@ -1297,7 +1297,135 @@
|
||||
"step4_method_cash": "Kontantmetoden",
|
||||
"step4_method_accrual_desc": "Revenue and expenses are posted when the invoice is sent or received, regardless of when payment occurs. This gives a more accurate view of the business's finances.",
|
||||
"step4_method_cash_desc": "Revenue and expenses are posted only when payment actually occurs. Simpler to manage but gives a less precise view of the business's finances at any given moment.",
|
||||
"step4_cash_limit_note": "Kontantmetoden may be used if annual net turnover is normally at most SEK 3 million (BFL 5 kap. 2 §)."
|
||||
"step4_cash_limit_note": "Kontantmetoden may be used if annual net turnover is normally at most SEK 3 million (BFL 5 kap. 2 §).",
|
||||
"journey_station_company": "The company",
|
||||
"journey_station_fiscal": "Fiscal year",
|
||||
"journey_station_vat": "VAT",
|
||||
"journey_station_method": "Bookkeeping",
|
||||
"journey_station_done": "Done",
|
||||
"journey_orb_listening": "Listening",
|
||||
"journey_orb_searching": "Searching Bolagsverket",
|
||||
"journey_orb_thinking": "Thinking",
|
||||
"journey_orb_working": "Shaping your bookkeeping",
|
||||
"journey_orb_shaping": "Shaping your bookkeeping",
|
||||
"journey_orb_check": "Done",
|
||||
"journey_press": "Press",
|
||||
"journey_orgnr_title": "What is your organisation number?",
|
||||
"journey_err_org_invalid": "The organisation number was not accepted. Check it and try again.",
|
||||
"journey_lookup_error": "Company details could not be fetched right now. Continue manually.",
|
||||
"journey_notfound_title": "I can't find a company with that number.",
|
||||
"journey_notfound_sub": "Newly registered companies can take a few days to appear at Bolagsverket.",
|
||||
"journey_notfound_continue": "Continue manually",
|
||||
"journey_notfound_edit": "Change the number",
|
||||
"journey_ceased_title": "The company is deregistered.",
|
||||
"journey_ceased_sub": "According to Bolagsverket the company is deregistered.",
|
||||
"journey_ceased_continue": "Continue anyway",
|
||||
"journey_form_title": "What type of company is it?",
|
||||
"journey_form_info": "An aktiebolag has its own organisation number from Bolagsverket. An enskild firma uses your personal identity number. The company type determines the fiscal-year and tax rules.",
|
||||
"journey_form_ab": "Aktiebolag",
|
||||
"journey_form_ef": "Enskild firma",
|
||||
"journey_name_ab_title": "What is the company called?",
|
||||
"journey_name_ab_sub": "The name as registered at Bolagsverket.",
|
||||
"journey_name_ab_placeholder": "Nordvik Bygg & Konsult AB",
|
||||
"journey_name_ef_title": "What should the business be called?",
|
||||
"journey_name_ef_sub": "Usually just your own name. You can change it later.",
|
||||
"journey_name_ef_placeholder": "Alice's Consulting",
|
||||
"journey_addr_ab_title": "What is the company's address?",
|
||||
"journey_addr_ef_title": "What is the business address?",
|
||||
"journey_addr_skip": "Add later",
|
||||
"journey_fskatt_ab_title": "Does the company have F-skatt?",
|
||||
"journey_fskatt_ef_title": "Do you have F-skatt?",
|
||||
"journey_fskatt_info": "F-skatt means you pay your own tax and contributions. Without it, your customers must withhold tax on your invoices. Apply at verksamt.se; if you answer Not yet we will remind you.",
|
||||
"journey_yes": "Yes",
|
||||
"journey_no": "No",
|
||||
"journey_fskatt_no": "Not yet",
|
||||
"journey_fy_confirm_title": "Is this fiscal year correct?",
|
||||
"journey_fy_ask_title": "Which fiscal year do you use?",
|
||||
"journey_fy_ef_title": "Is this your first fiscal year?",
|
||||
"journey_fy_confirm_sub_calendar": "According to Bolagsverket you follow the calendar year: 1 January to 31 December.",
|
||||
"journey_fy_confirm_sub_broken": "According to Bolagsverket your fiscal year runs {from} to {to}.",
|
||||
"journey_fy_ask_sub": "The calendar year is 1 January to 31 December.",
|
||||
"journey_fy_ef_sub": "An enskild firma follows the calendar year: 1 January to 31 December.",
|
||||
"journey_fy_info": "Most companies follow the calendar year. A broken fiscal year ends in a month other than December and is mostly used in groups. Newly started companies may have a first year of 6 to 18 months.",
|
||||
"journey_fy_ef_info": "The calendar year is mandatory for enskild firma (BFL ch. 3). A first year can be shorter, or up to 18 months.",
|
||||
"journey_fy_yes": "Yes, correct",
|
||||
"journey_fy_calendar": "Calendar year",
|
||||
"journey_fy_other": "Different fiscal year",
|
||||
"journey_fy_first": "This is our first year",
|
||||
"journey_fy_ef_ongoing": "No, ongoing",
|
||||
"journey_fy_ef_new": "Yes, newly started",
|
||||
"journey_suggested": "suggested",
|
||||
"journey_fymonth_title": "When does the fiscal year end?",
|
||||
"journey_fymonth_sub": "Hover to preview.",
|
||||
"journey_fymonth_info": "Most have a calendar year ending in December. A broken fiscal year ends in another month and is mostly used in groups.",
|
||||
"journey_fymonth_use": "Use this",
|
||||
"journey_band_calendar_note": "Calendar year, like most companies.",
|
||||
"journey_band_broken_note": "Broken fiscal year. Uncommon outside groups.",
|
||||
"journey_fystart_ab_title": "When did the company start?",
|
||||
"journey_fystart_ef_title": "When did the business start?",
|
||||
"journey_fystart_reg_sub": "Bolagsverket registered you on {date}.",
|
||||
"journey_fystart_ab_sub": "The registration date at Bolagsverket.",
|
||||
"journey_fystart_ef_sub": "The day you registered with Skatteverket.",
|
||||
"journey_fystart_other": "Another date …",
|
||||
"journey_fyend_title": "When should the first year end?",
|
||||
"journey_fyend_sub": "Hover an option to see the year.",
|
||||
"journey_fyend_ef_info": "An enskild firma always ends 31 December. The first year may be up to 18 months.",
|
||||
"journey_fyend_ab_info": "The first fiscal year must be 6 to 18 months under the Bookkeeping Act. To end in a month other than December, choose Another end month.",
|
||||
"journey_fyend_months": "{count} months",
|
||||
"journey_fyend_extended": "extended first year",
|
||||
"journey_fyend_short": "shortened first year",
|
||||
"journey_fyend_other_month": "Another end month …",
|
||||
"journey_fyend_none": "No valid end month from here. Try another month.",
|
||||
"journey_fyend_start_label": "Start {date}",
|
||||
"journey_momsyn_ab_title": "Is the company VAT registered?",
|
||||
"journey_momsyn_ef_title": "Are you VAT registered?",
|
||||
"journey_momsyn_info": "VAT registration is mandatory once turnover passes SEK 120,000 per year. If you answer no, we watch the threshold and let you know in good time.",
|
||||
"journey_moms_ab_title": "How often do you report VAT?",
|
||||
"journey_moms_ef_title": "How often do you report VAT?",
|
||||
"journey_moms_sub_registered": "You are VAT registered.",
|
||||
"journey_moms_info": "The period is stated in the decision from Skatteverket. Under SEK 1 million in turnover you may report yearly, up to 40 million quarterly, above that monthly.",
|
||||
"journey_moms_info_vatnr": "The VAT number {vatNumber} is derived automatically.",
|
||||
"journey_moms_quarterly": "Every quarter",
|
||||
"journey_moms_monthly": "Every month",
|
||||
"journey_moms_yearly": "Once a year",
|
||||
"journey_method_ab_title": "How do you want to keep the books?",
|
||||
"journey_method_ef_title": "How do you want to keep the books?",
|
||||
"journey_method_info": "Faktureringsmetoden records when the invoice is sent or received, the standard for aktiebolag. Kontantmetoden records only when payment happens and may be used up to SEK 3 million in turnover. You can switch before your first year-end.",
|
||||
"journey_method_accrual": "Faktureringsmetoden",
|
||||
"journey_method_cash": "Kontantmetoden",
|
||||
"journey_shaping_accounts": "Setting up the chart of accounts: BAS 2026",
|
||||
"journey_shaping_period": "Creating the fiscal year: {name}",
|
||||
"journey_shaping_vat": "Configuring VAT and deadlines",
|
||||
"journey_shaping_deadlines": "Configuring deadlines",
|
||||
"journey_err_period": "The fiscal year was not accepted. Check the dates.",
|
||||
"journey_err_generic": "Something went wrong. Try again.",
|
||||
"journey_done_title": "Welcome, {name}.",
|
||||
"journey_card_eyebrow": "Company profile",
|
||||
"journey_card_form": "Company type",
|
||||
"journey_card_orgnr": "Organisation number",
|
||||
"journey_card_persnr": "Personal identity number",
|
||||
"journey_card_seat": "Registered city",
|
||||
"journey_card_fy": "Fiscal year",
|
||||
"journey_card_fskatt": "F-skatt",
|
||||
"journey_card_vat": "VAT",
|
||||
"journey_card_method": "Bookkeeping",
|
||||
"journey_card_fskatt_yes": "Yes",
|
||||
"journey_card_fskatt_pending": "Application pending",
|
||||
"journey_note_first_year": "The first fiscal year {start} to {end} is set up.",
|
||||
"journey_note_fskatt": "Reminder created: apply for F-skatt at verksamt.se.",
|
||||
"journey_note_vat_watch": "We watch the SEK 120,000 threshold and let you know in good time.",
|
||||
"journey_note_ceased": "The company is marked as deregistered at Bolagsverket.",
|
||||
"journey_open_app": "Open Accounted",
|
||||
"journey_ans_calendar": "Calendar year",
|
||||
"journey_ans_broken": "Broken: {from} to {to}",
|
||||
"journey_ans_first": "First year: {from} to {to}",
|
||||
"journey_ans_vat_none": "Not VAT registered",
|
||||
"journey_ans_quarterly": "Quarterly",
|
||||
"journey_ans_monthly": "Monthly",
|
||||
"journey_ans_yearly": "Yearly",
|
||||
"journey_dup_note": "You already have {name} in Accounted.",
|
||||
"journey_fact_vat": "VAT registered",
|
||||
"journey_fact_ceased": "Deregistered"
|
||||
},
|
||||
"select_company": {
|
||||
"greeting_night": "Good night",
|
||||
|
||||
Reference in New Issue
Block a user