import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import '../core/theme.dart'; import '../state/compliance_controller.dart'; import '../state/profile_controller.dart'; import '../widgets/collapsing_header.dart'; import '../widgets/common.dart'; import '../widgets/krow_wordmark.dart'; class ProfileScreen extends ConsumerWidget { const ProfileScreen({super.key}); /// Avatar (84) + gap + name + role, plus 4/16 padding. static const _expandedHeader = 192.0; @override Widget build(BuildContext context, WidgetRef ref) { final worker = ref.watch(workerStatsProvider); final profile = ref.watch(profileProvider); final (done, total) = ref.watch(complianceProgressProvider); return Scaffold( body: CustomScrollView( slivers: [ KrowSliverHeader( // Must clear the open content: avatar, name and role, plus the // padding around them. The settings button overlays rather than // stacking above, which is what keeps this under 200. expandedHeight: _expandedHeader, collapsedHeight: 60, builder: (context, t) => HeaderCrossFade( t: t, expandedHeight: _expandedHeader, collapsed: Padding( padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), child: Row( children: [ Expanded( child: Text( worker.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 17, fontWeight: FontWeight.w700, letterSpacing: -0.5, color: Colors.white, ), ), ), HeaderIconButton( icon: LucideIcons.settings, tooltip: 'Settings', onPressed: () => context.push('/settings'), ), ], ), ), expanded: Padding( padding: const EdgeInsets.fromLTRB(20, 12, 20, 16), child: Stack( // Without this the unpositioned column would shrink-wrap to // the Stack's top-left corner instead of centring. alignment: Alignment.topCenter, children: [ // Overlaid rather than stacked above the avatar: as its own // row it added 46px the header could not afford. Positioned( top: 0, left: 0, right: 0, child: HeaderBrandLine( trailing: HeaderIconButton( icon: LucideIcons.settings, tooltip: 'Settings', onPressed: () => context.push('/settings'), ), ), ), Column( mainAxisSize: MainAxisSize.min, children: [ GestureDetector( onTap: () => _editName(context, ref, worker.name), child: Stack( children: [ Container( width: 84, height: 84, alignment: Alignment.center, decoration: const BoxDecoration( color: KrowPalette.yellow, shape: BoxShape.circle, ), child: Text( worker.initials, style: const TextStyle( fontSize: 28, fontWeight: FontWeight.w700, color: KrowPalette.ink, ), ), ), Positioned( right: 0, bottom: 0, child: Container( width: 28, height: 28, decoration: BoxDecoration( color: Colors.white, shape: BoxShape.circle, border: Border.all( color: KrowPalette.blue, width: 2.5, ), ), child: const Icon( LucideIcons.pencil, size: 12, color: KrowPalette.blue, ), ), ), ], ), ), const SizedBox(height: 10), Text( worker.name, maxLines: 1, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 23, fontWeight: FontWeight.w700, letterSpacing: -0.8, color: Colors.white, ), ), const SizedBox(height: 2), Text( '${worker.role} · ${worker.city}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( fontSize: 12.5, letterSpacing: 0.4, color: Colors.white.withValues(alpha: 0.6), ), ), ], ), ], ), ), ), ), SliverPadding( padding: const EdgeInsets.fromLTRB(20, 14, 20, 0), sliver: SliverList.list( children: [ StatRail( stats: [ ('Shifts', '${worker.shiftsWorked}'), ('Rating', worker.rating.toStringAsFixed(1)), ('On time', '${worker.onTimePercent}%'), ('No show', '${worker.noShows}'), ('Cancel', '${worker.cancellations}'), ], ), const SizedBox(height: 11), KrowCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'Trust index', style: TextStyle( fontSize: 14.5, fontWeight: FontWeight.w600, ), ), Text( '${worker.trustIndex}%', style: const TextStyle( fontSize: 18, fontWeight: FontWeight.w700, letterSpacing: -0.6, ), ), ], ), const SizedBox(height: 11), KrowProgressBar( worker.trustIndex / 100, color: worker.trustIndex < 45 ? const Color(0xFFA6403F) : KrowPalette.blue, ), const SizedBox(height: 9), const Text( 'Stay above 45% to keep picking up shifts. On-time ' 'arrivals raise it fastest.', style: TextStyle( fontSize: 12, height: 1.45, color: KrowPalette.mute, ), ), ], ), ), const SectionLabel('Compliance'), TileGroup( children: [ NavTile( icon: LucideIcons.shieldCheck, title: 'Compliance hub', subtitle: 'Everything a business must see', badge: StatusChip( '$done of $total', tone: done == total ? ChipTone.neutral : ChipTone.yellow, ), onTap: () => context.push('/compliance'), ), ], ), const SectionLabel('Your details'), TileGroup( children: [ NavTile( icon: LucideIcons.user, title: 'Personal info', subtitle: worker.phone, onTap: () => _editName(context, ref, worker.name), ), NavTile( icon: LucideIcons.phone, title: 'Emergency contact', subtitle: 'Who we call if something happens', badge: StatusChip( '${profile.contacts.length}', tone: ChipTone.neutral, ), onTap: () => context.push('/emergency-contacts'), ), NavTile( icon: LucideIcons.briefcase, title: 'Experience & skills', subtitle: 'Work history and trades', badge: StatusChip( '${profile.skills.length}', tone: ChipTone.neutral, ), onTap: () => context.push('/skills'), ), NavTile( icon: LucideIcons.calendar, title: 'Availability', subtitle: 'The hours you can work', onTap: () => context.push('/availability'), ), NavTile( icon: LucideIcons.gift, title: 'Benefits', subtitle: 'Policies and balance history', onTap: () => context.push('/benefits'), ), ], ), const SectionLabel('Money'), TileGroup( children: [ NavTile( icon: LucideIcons.landmark, title: 'Direct deposit', subtitle: profile.bank == null ? 'Not set up yet' : '${profile.bank!.bankName} ' '····${profile.bank!.accountLast4}', badge: profile.bank == null ? const StatusChip('Add', tone: ChipTone.yellow) : null, onTap: () => context.push('/direct-deposit'), ), NavTile( icon: LucideIcons.fileText, title: 'Timecards', subtitle: 'Hours worked and disputes', onTap: () => context.push('/timecards'), ), ], ), const SectionLabel('Support'), TileGroup( children: [ NavTile( icon: LucideIcons.lifeBuoy, title: 'Contact support', subtitle: 'Usually answered within 4 hours', onTap: () => context.push('/support'), ), NavTile( icon: LucideIcons.circleHelp, title: 'FAQs', subtitle: 'Common questions answered', onTap: () => context.push('/faqs'), ), NavTile( icon: LucideIcons.shield, title: 'Privacy & security', subtitle: 'Account and data settings', onTap: () => context.push('/privacy'), ), ], ), const SizedBox(height: 28), ], ), ), ], ), ); } Future _editName( BuildContext context, WidgetRef ref, String current, ) async { final controller = TextEditingController(text: current); final name = await showDialog( context: context, builder: (context) => AlertDialog( title: const Text('Your name'), content: TextField( controller: controller, autofocus: true, textCapitalization: TextCapitalization.words, decoration: const InputDecoration(hintText: 'Full name'), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), FilledButton( onPressed: () => Navigator.pop(context, controller.text.trim()), style: FilledButton.styleFrom(minimumSize: const Size(0, 44)), child: const Text('Save'), ), ], ), ); if (name == null || name.isEmpty) return; ref.read(profileProvider.notifier).updateName(name); } }