import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../app/providers.dart'; import '../../../core/constants/app_constants.dart'; import '../../../core/router/app_router.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_dimens.dart'; import '../../../core/utils/extensions.dart'; import '../../../core/utils/formatters.dart'; import '../../../core/widgets/glass_card.dart'; import '../../pos/providers/cart_controller.dart'; import '../widgets/welcome_illustration.dart'; /// Screen 1 — the terminal's resting state between sales. class WelcomeScreen extends ConsumerWidget { const WelcomeScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final session = ref.watch(cashierSessionProvider); final clock = ref.watch(clockProvider).value ?? DateTime.now(); return Scaffold( body: Container( decoration: const BoxDecoration(gradient: AppColors.primaryGradient), child: SafeArea( child: Column( children: [ _TopBar(cashier: session.name, now: clock), Expanded( child: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(AppSpacing.xxl), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 920), child: GlassCard( blur: 18, padding: EdgeInsets.all( context.responsive( compact: AppSpacing.xxl, expanded: AppSpacing.giant, ), ), radius: AppRadius.xxl, shadows: AppColors.shadowLg, child: context.isCompact ? const _StackedLayout() : const _SideBySideLayout(), ), ), ).animate().fadeIn(duration: 350.ms).slideY( begin: 0.04, end: 0, curve: Curves.easeOutCubic, ), ), ), const _BottomHint(), ], ), ), ), ); } } class _SideBySideLayout extends StatelessWidget { const _SideBySideLayout(); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: const [ Expanded(flex: 4, child: WelcomeIllustration(size: 260)), SizedBox(width: AppSpacing.giant), Expanded(flex: 5, child: _WelcomeContent()), ], ); } } class _StackedLayout extends StatelessWidget { const _StackedLayout(); @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: const [ WelcomeIllustration(size: 150), SizedBox(height: AppSpacing.xxl), _WelcomeContent(), ], ); } } class _WelcomeContent extends ConsumerWidget { const _WelcomeContent(); @override Widget build(BuildContext context, WidgetRef ref) { return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Welcome to', style: context.text.titleMedium?.copyWith( color: AppColors.textSecondary, letterSpacing: 1.4, ), ), const SizedBox(height: AppSpacing.xs), Text( 'Nearle POS', style: context.text.displaySmall?.copyWith( color: AppColors.primary, fontWeight: FontWeight.w800, ), ), const SizedBox(height: AppSpacing.md), Text( 'Start a new sale by identifying the shopper, ' 'or skip straight to billing.', style: context.text.bodyMedium ?.copyWith(color: AppColors.textSecondary), ), const SizedBox(height: AppSpacing.xxxl), _WelcomeAction( icon: Icons.person_add_alt_1_rounded, title: 'New Customer', subtitle: 'Register and start earning loyalty points', onTap: () => context.push(AppRoutes.registerCustomer), primary: true, ), const SizedBox(height: AppSpacing.md), _WelcomeAction( icon: Icons.badge_outlined, title: 'Existing Customer', subtitle: 'Look up by mobile number', onTap: () => context.push(AppRoutes.existingCustomer), ), const SizedBox(height: AppSpacing.md), _WelcomeAction( icon: Icons.directions_walk_rounded, title: 'Skip Customer', subtitle: 'Walk-in sale, no loyalty tracking', onTap: () { ref.read(cartControllerProvider.notifier).reset(); context.go(AppRoutes.pos); }, ), ], ); } } /// A tall, unmistakable target — the cashier taps this hundreds of times a day. class _WelcomeAction extends StatefulWidget { const _WelcomeAction({ required this.icon, required this.title, required this.subtitle, required this.onTap, this.primary = false, }); final IconData icon; final String title; final String subtitle; final VoidCallback onTap; final bool primary; @override State<_WelcomeAction> createState() => _WelcomeActionState(); } class _WelcomeActionState extends State<_WelcomeAction> { bool _hovered = false; @override Widget build(BuildContext context) { final bg = widget.primary ? AppColors.primary : (_hovered ? AppColors.primarySurface : AppColors.surface); final fg = widget.primary ? AppColors.textOnPrimary : AppColors.textPrimary; final sub = widget.primary ? AppColors.textOnPrimary.withValues(alpha: 0.78) : AppColors.textSecondary; return MouseRegion( onEnter: (_) => setState(() => _hovered = true), onExit: (_) => setState(() => _hovered = false), child: AnimatedContainer( duration: AppMotion.fast, transform: Matrix4.translationValues(0, _hovered ? -2 : 0, 0), child: Material( color: bg, borderRadius: AppRadius.brLg, child: InkWell( onTap: widget.onTap, borderRadius: AppRadius.brLg, child: Container( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xl, vertical: AppSpacing.lg, ), decoration: BoxDecoration( borderRadius: AppRadius.brLg, border: Border.all( color: widget.primary ? Colors.transparent : AppColors.border, ), boxShadow: widget.primary && _hovered ? AppColors.shadowMd : null, ), child: Row( children: [ Container( width: 46, height: 46, decoration: BoxDecoration( color: widget.primary ? Colors.white.withValues(alpha: 0.18) : AppColors.primarySurface, borderRadius: AppRadius.brMd, ), child: Icon( widget.icon, color: widget.primary ? AppColors.textOnPrimary : AppColors.primary, size: 22, ), ), const SizedBox(width: AppSpacing.lg), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.title, style: context.text.titleMedium?.copyWith(color: fg), ), const SizedBox(height: 2), Text( widget.subtitle, style: context.text.bodySmall?.copyWith(color: sub), ), ], ), ), Icon(Icons.arrow_forward_rounded, color: sub, size: 20), ], ), ), ), ), ), ); } } class _TopBar extends StatelessWidget { const _TopBar({required this.cashier, required this.now}); final String cashier; final DateTime now; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.symmetric( horizontal: AppSpacing.xxl, vertical: AppSpacing.lg, ), child: Row( children: [ const Icon(Icons.storefront_rounded, color: Colors.white, size: 26), const SizedBox(width: AppSpacing.md), Text( AppConstants.storeName, style: context.text.titleLarge?.copyWith(color: Colors.white), ), const Spacer(), Text( '${Formatters.date(now)} ${Formatters.time(now)}', style: context.text.bodyMedium ?.copyWith(color: Colors.white.withValues(alpha: 0.85)), ), const SizedBox(width: AppSpacing.xxl), CircleAvatar( radius: 16, backgroundColor: Colors.white.withValues(alpha: 0.2), child: Text( Formatters.initials(cashier), style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w700, ), ), ), const SizedBox(width: AppSpacing.sm), Text(cashier, style: context.text.bodyMedium?.copyWith(color: Colors.white)), ], ), ); } } class _BottomHint extends StatelessWidget { const _BottomHint(); @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.only(bottom: AppSpacing.xl), child: Text( 'Scan a barcode at any time to begin a walk-in sale', style: context.text.bodySmall ?.copyWith(color: Colors.white.withValues(alpha: 0.7)), ), ); } }