Phase 4 §2 + §1/§4: OnboardingIntent post-OTP routing + test naming + register-screen overflow
Spec §2 (flow_customer.mermaid) routes post-OTP based on user-lookup + has_transacted, but the implementation previously dumped every OTP success on /home. Introduce `OnboardingIntent` provider: set to `onboarding` by routeForVerifChoice's verified branch (the "aku mau curhat" transaction journey), set to `recover` by SHome1st's masuk → banner. Router redirect on AuthAuthenticatedData+isAuthRoute consumes it: `onboarding` → /payment/entry (dispatches S6 paywall vs PickMethod via first_session_discount.eligible); `recover` → /home. Intent is reset in /payment/entry's initState so subsequent masuk → flows don't inherit it. auth_notifier.verifyOtp uses .copyWithPrevious on AsyncError so valueOrNull retains AuthOtpSentData/AuthAnonymousData through OTP failures — required for the OTP-blocked recovery path (/onboarding/anon/method → /payment/method-pick) to clear the global redirect without bouncing to /home. Router also extends the isAuthRoute/isOnboardingFlow carve-out to AuthOtpSentData. Maestro tests adopt `ts-<app>-<NN>-<MM>-<descriptor>.yaml` convention: NN = mermaid section, MM = sub-flow index. New ts-customer-02-01..05 cover the §2 branches (verified brand-new → S6, existing-no-tx → S6, existing-tx → method-pick, OTP-blocked → method-pick, anonymous first- timer → method-pick); deferred 02-06/07/08/09 documented in README_section_02.md. TS-07 → ts-customer-02-10 (masuk → recovery); TS-01..06 → ts-customer-04-01..06 (§4 returning-user). Shared onboarding_new_user_verified.yaml subflow extracted. Register screen's body Column now uses LayoutBuilder + SingleChildScrollView + ConstrainedBox + IntrinsicHeight so the keyboard-open layout no longer overflows by 1.3 px (verified visually). Spec prose updated at flow_customer.mermaid §2 to describe the intent-driven routing + login-vs-transaction divergence. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../core/auth/auth_notifier.dart';
|
||||
import '../../core/auth/onboarding_intent_provider.dart';
|
||||
import '../../core/availability/mitra_availability_notifier.dart';
|
||||
import '../../core/chat/active_session_notifier.dart';
|
||||
import '../../core/notifications/notif_permission.dart';
|
||||
@@ -198,11 +199,11 @@ class _SHome1stView extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _LoginRecoverBanner extends StatelessWidget {
|
||||
class _LoginRecoverBanner extends ConsumerWidget {
|
||||
const _LoginRecoverBanner();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
|
||||
child: Material(
|
||||
@@ -210,7 +211,14 @@ class _LoginRecoverBanner extends StatelessWidget {
|
||||
borderRadius: HaloRadius.md,
|
||||
child: InkWell(
|
||||
borderRadius: HaloRadius.md,
|
||||
onTap: () => context.push('/auth/register'),
|
||||
onTap: () {
|
||||
// Recovery flow — post-OTP should land on /home (the user wants
|
||||
// their history), NOT /payment/entry. Defensive reset in case a
|
||||
// prior onboarding run left the intent dirty.
|
||||
ref.read(onboardingIntentProvider.notifier).state =
|
||||
OnboardingIntent.recover;
|
||||
context.push('/auth/register');
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
decoration: BoxDecoration(
|
||||
@@ -786,11 +794,13 @@ class _NotifDeniedBanner extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: HaloTokens.brandDark,
|
||||
side: const BorderSide(color: HaloTokens.brandDark, width: 1),
|
||||
shape: const StadiumBorder(),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: HaloSpacing.s8,
|
||||
horizontal: HaloSpacing.s12,
|
||||
),
|
||||
minimumSize: const Size(0, 32),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
|
||||
Reference in New Issue
Block a user