Standardize UI to design system tokens
Refactor multiple UI components to use design system tokens and primitives. Added new UiIcons (coffee, wifi, xCircle, ban) and typography color getters (primary, accent). Replaced hardcoded paddings, spacings, radii, borderRadius, and icon imports (lucide_icons -> UiIcons) with UiConstants, UiColors, UiTypography and UiIcons, and switched to UiColors.withValues for opacity. Changes apply across authentication, availability, clock_in (and its widgets), commute tracker, lunch break modal, location map placeholder, attendance card, date selector, and related presentation files to improve visual consistency.
This commit is contained in:
@@ -237,4 +237,16 @@ class UiIcons {
|
|||||||
|
|
||||||
/// Timer icon
|
/// Timer icon
|
||||||
static const IconData timer = _IconLib.timer;
|
static const IconData timer = _IconLib.timer;
|
||||||
|
|
||||||
|
/// Coffee icon for breaks
|
||||||
|
static const IconData coffee = _IconLib.coffee;
|
||||||
|
|
||||||
|
/// Wifi icon for NFC check-in
|
||||||
|
static const IconData wifi = _IconLib.wifi;
|
||||||
|
|
||||||
|
/// X Circle icon for no-shows
|
||||||
|
static const IconData xCircle = _IconLib.xCircle;
|
||||||
|
|
||||||
|
/// Ban icon for cancellations
|
||||||
|
static const IconData ban = _IconLib.ban;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -579,4 +579,10 @@ extension TypographyColors on TextStyle {
|
|||||||
|
|
||||||
/// Active content color
|
/// Active content color
|
||||||
TextStyle get activeContentColor => copyWith(color: UiColors.textPrimary);
|
TextStyle get activeContentColor => copyWith(color: UiColors.textPrimary);
|
||||||
|
|
||||||
|
/// Primary color
|
||||||
|
TextStyle get primary => copyWith(color: UiColors.primary);
|
||||||
|
|
||||||
|
/// Accent color
|
||||||
|
TextStyle get accent => copyWith(color: UiColors.accent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
|
||||||
@@ -36,7 +37,7 @@ class GetStartedPage extends StatelessWidget {
|
|||||||
|
|
||||||
// Content Overlay
|
// Content Overlay
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
@@ -44,7 +45,7 @@ class GetStartedPage extends StatelessWidget {
|
|||||||
// Main text and actions
|
// Main text and actions
|
||||||
const GetStartedHeader(),
|
const GetStartedHeader(),
|
||||||
|
|
||||||
const SizedBox(height: 48),
|
const SizedBox(height: UiConstants.space10),
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
GetStartedActions(
|
GetStartedActions(
|
||||||
@@ -52,7 +53,7 @@ class GetStartedPage extends StatelessWidget {
|
|||||||
onLoginPressed: onLoginPressed,
|
onLoginPressed: onLoginPressed,
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class GetStartedBackground extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
// Logo
|
// Logo
|
||||||
Image.asset(UiImageAssets.logoBlue, height: 40),
|
Image.asset(UiImageAssets.logoBlue, height: 40),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -18,9 +18,9 @@ class GetStartedBackground extends StatelessWidget {
|
|||||||
height: 288,
|
height: 288,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: const Color(0xFF3A4A5A).withOpacity(0.05),
|
color: UiColors.bgSecondary.withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(UiConstants.space2),
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
'https://images.unsplash.com/photo-1577219491135-ce391730fb2c?w=400&h=400&fit=crop&crop=faces',
|
'https://images.unsplash.com/photo-1577219491135-ce391730fb2c?w=400&h=400&fit=crop&crop=faces',
|
||||||
@@ -50,7 +50,7 @@ class GetStartedBackground extends StatelessWidget {
|
|||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.2),
|
color: UiColors.primary.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -59,7 +59,7 @@ class GetStartedBackground extends StatelessWidget {
|
|||||||
width: 8,
|
width: 8,
|
||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.2),
|
color: UiColors.primary.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -79,10 +79,12 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
spacing: 24,
|
spacing: UiConstants.space6,
|
||||||
children: [
|
children: [
|
||||||
_buildQuickSet(context),
|
_buildQuickSet(context),
|
||||||
_buildWeekNavigation(context, state),
|
_buildWeekNavigation(context, state),
|
||||||
@@ -119,10 +121,10 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
|
|
||||||
Widget _buildQuickSet(BuildContext context) {
|
Widget _buildQuickSet(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withValues(alpha: 0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -131,19 +133,19 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
'Quick Set Availability',
|
'Quick Set Availability',
|
||||||
style: UiTypography.body2b,
|
style: UiTypography.body2b,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: _buildQuickSetButton(context, 'All Week', 'all')),
|
Expanded(child: _buildQuickSetButton(context, 'All Week', 'all')),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildQuickSetButton(context, 'Weekdays', 'weekdays'),
|
child: _buildQuickSetButton(context, 'Weekdays', 'weekdays'),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildQuickSetButton(context, 'Weekends', 'weekends'),
|
child: _buildQuickSetButton(context, 'Weekends', 'weekends'),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildQuickSetButton(
|
child: _buildQuickSetButton(
|
||||||
context,
|
context,
|
||||||
@@ -179,7 +181,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
),
|
),
|
||||||
backgroundColor: UiColors.transparent,
|
backgroundColor: UiColors.transparent,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
foregroundColor: isDestructive
|
foregroundColor: isDestructive
|
||||||
? UiColors.destructive
|
? UiColors.destructive
|
||||||
@@ -201,17 +203,17 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
final monthYear = DateFormat('MMMM yyyy').format(middleDate);
|
final monthYear = DateFormat('MMMM yyyy').format(middleDate);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.cardViewBackground,
|
color: UiColors.cardViewBackground,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Nav Header
|
// Nav Header
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -275,12 +277,12 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
onTap: () => context.read<AvailabilityBloc>().add(SelectDate(day.date)),
|
onTap: () => context.read<AvailabilityBloc>().add(SelectDate(day.date)),
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 2),
|
margin: const EdgeInsets.symmetric(horizontal: 2),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? UiColors.primary
|
? UiColors.primary
|
||||||
: (isAvailable ? UiColors.tagSuccess : UiColors.bgSecondary),
|
: (isAvailable ? UiColors.tagSuccess : UiColors.bgSecondary),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? UiColors.primary
|
? UiColors.primary
|
||||||
@@ -346,10 +348,10 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
final isAvailable = day.isAvailable;
|
final isAvailable = day.isAvailable;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.cardViewBackground,
|
color: UiColors.cardViewBackground,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -380,7 +382,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Time Slots (only from Domain)
|
// Time Slots (only from Domain)
|
||||||
...day.slots.map((slot) {
|
...day.slots.map((slot) {
|
||||||
@@ -464,11 +466,11 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
: null,
|
: null,
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: bgColor,
|
color: bgColor,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: borderColor, width: 2),
|
border: Border.all(color: borderColor, width: 2),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -479,7 +481,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: uiConfig['bg'],
|
color: uiConfig['bg'],
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
uiConfig['icon'],
|
uiConfig['icon'],
|
||||||
@@ -487,7 +489,7 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
// Text
|
// Text
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -539,20 +541,20 @@ class _AvailabilityPageState extends State<AvailabilityPage> {
|
|||||||
|
|
||||||
Widget _buildInfoCard() {
|
Widget _buildInfoCard() {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withValues(alpha: 0.05),
|
color: UiColors.primary.withValues(alpha: 0.05),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 12,
|
spacing: UiConstants.space3,
|
||||||
children: [
|
children: [
|
||||||
const Icon(UiIcons.clock, size: 20, color: UiColors.primary),
|
const Icon(UiIcons.clock, size: 20, color: UiColors.primary),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 4,
|
spacing: UiConstants.space1,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Auto-Match uses your availability',
|
'Auto-Match uses your availability',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
import '../bloc/clock_in_bloc.dart';
|
import '../bloc/clock_in_bloc.dart';
|
||||||
import '../bloc/clock_in_event.dart';
|
import '../bloc/clock_in_event.dart';
|
||||||
@@ -84,7 +83,9 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -103,12 +104,13 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
// Date Selector
|
// Date Selector
|
||||||
DateSelector(
|
DateSelector(
|
||||||
selectedDate: state.selectedDate,
|
selectedDate: state.selectedDate,
|
||||||
onSelect: (DateTime date) => _bloc.add(DateSelected(date)),
|
onSelect: (DateTime date) =>
|
||||||
|
_bloc.add(DateSelected(date)),
|
||||||
shiftDates: <String>[
|
shiftDates: <String>[
|
||||||
DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
DateFormat('yyyy-MM-dd').format(DateTime.now()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: UiConstants.space5),
|
||||||
|
|
||||||
// Your Activity Header
|
// Your Activity Header
|
||||||
Text(
|
Text(
|
||||||
@@ -117,7 +119,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
),
|
),
|
||||||
|
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Selected Shift Info Card
|
// Selected Shift Info Card
|
||||||
if (todayShifts.isNotEmpty)
|
if (todayShifts.isNotEmpty)
|
||||||
@@ -128,14 +130,16 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
onTap: () =>
|
onTap: () =>
|
||||||
_bloc.add(ShiftSelected(shift)),
|
_bloc.add(ShiftSelected(shift)),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(
|
||||||
margin:
|
UiConstants.space3,
|
||||||
const EdgeInsets.only(bottom: 12),
|
),
|
||||||
|
margin: const EdgeInsets.only(
|
||||||
|
bottom: UiConstants.space3,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius:
|
||||||
12,
|
UiConstants.radiusLg,
|
||||||
),
|
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: shift.id ==
|
color: shift.id ==
|
||||||
selectedShift?.id
|
selectedShift?.id
|
||||||
@@ -211,39 +215,41 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Swipe To Check In / Checked Out State / No Shift State
|
// Swipe To Check In / Checked Out State / No Shift State
|
||||||
if (selectedShift != null && checkOutTime == null) ...<Widget>[
|
if (selectedShift != null &&
|
||||||
if (!isCheckedIn &&
|
checkOutTime == null) ...<Widget>[
|
||||||
!_isCheckInAllowed(selectedShift))
|
if (!isCheckedIn &&
|
||||||
Container(
|
!_isCheckInAllowed(selectedShift))
|
||||||
width: double.infinity,
|
Container(
|
||||||
padding: const EdgeInsets.all(24),
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
padding:
|
||||||
color: UiColors.bgSecondary,
|
const EdgeInsets.all(UiConstants.space6),
|
||||||
borderRadius: BorderRadius.circular(16),
|
decoration: BoxDecoration(
|
||||||
),
|
color: UiColors.bgSecondary,
|
||||||
child: Column(
|
borderRadius: UiConstants.radiusLg,
|
||||||
children: <Widget>[
|
),
|
||||||
const Icon(
|
child: Column(
|
||||||
LucideIcons.clock,
|
children: <Widget>[
|
||||||
size: 48,
|
const Icon(
|
||||||
color: UiColors.iconThird,
|
UiIcons.clock,
|
||||||
),
|
size: 48,
|
||||||
const SizedBox(height: 16),
|
color: UiColors.iconThird,
|
||||||
Text(
|
),
|
||||||
"You're early!",
|
const SizedBox(height: UiConstants.space4),
|
||||||
style: UiTypography.body1m.textSecondary,
|
Text(
|
||||||
),
|
"You're early!",
|
||||||
const SizedBox(height: 4),
|
style: UiTypography.body1m.textSecondary,
|
||||||
Text(
|
),
|
||||||
"Check-in available at ${_getCheckInAvailabilityTime(selectedShift)}",
|
const SizedBox(height: UiConstants.space1),
|
||||||
style: UiTypography.body2r.textSecondary,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
"Check-in available at ${_getCheckInAvailabilityTime(selectedShift)}",
|
||||||
),
|
style: UiTypography.body2r.textSecondary,
|
||||||
],
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
)
|
],
|
||||||
else
|
),
|
||||||
SwipeToCheckIn(
|
)
|
||||||
|
else
|
||||||
|
SwipeToCheckIn(
|
||||||
isCheckedIn: isCheckedIn,
|
isCheckedIn: isCheckedIn,
|
||||||
mode: state.checkInMode,
|
mode: state.checkInMode,
|
||||||
isLoading:
|
isLoading:
|
||||||
@@ -264,14 +270,17 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
onCheckOut: () {
|
onCheckOut: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) => LunchBreakDialog(
|
builder: (BuildContext context) =>
|
||||||
onComplete: () {
|
LunchBreakDialog(
|
||||||
Navigator.of(
|
onComplete: () {
|
||||||
context,
|
Navigator.of(
|
||||||
).pop(); // Close dialog first
|
context,
|
||||||
_bloc.add(const CheckOutRequested());
|
).pop(); // Close dialog first
|
||||||
},
|
_bloc.add(
|
||||||
),
|
const CheckOutRequested(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -279,12 +288,14 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
checkOutTime != null) ...<Widget>[
|
checkOutTime != null) ...<Widget>[
|
||||||
// Shift Completed State
|
// Shift Completed State
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.tagSuccess,
|
color: UiColors.tagSuccess,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.success.withValues(alpha: 0.3),
|
color: UiColors.success.withValues(
|
||||||
|
alpha: 0.3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -297,17 +308,17 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.check,
|
UiIcons.check,
|
||||||
color: UiColors.textSuccess,
|
color: UiColors.textSuccess,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Text(
|
Text(
|
||||||
"Shift Completed!",
|
"Shift Completed!",
|
||||||
style: UiTypography.body1b.textSuccess,
|
style: UiTypography.body1b.textSuccess,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
"Great work today",
|
"Great work today",
|
||||||
style: UiTypography.body2r.textSuccess,
|
style: UiTypography.body2r.textSuccess,
|
||||||
@@ -319,10 +330,10 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
// No Shift State
|
// No Shift State
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgSecondary,
|
color: UiColors.bgSecondary,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -331,7 +342,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
style: UiTypography.body1m.textSecondary,
|
style: UiTypography.body1m.textSecondary,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
"Accept a shift to clock in",
|
"Accept a shift to clock in",
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
@@ -344,14 +355,16 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
|
|
||||||
// Checked In Banner
|
// Checked In Banner
|
||||||
if (isCheckedIn && checkInTime != null) ...<Widget>[
|
if (isCheckedIn && checkInTime != null) ...<Widget>[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.tagSuccess,
|
color: UiColors.tagSuccess,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.success.withValues(alpha: 0.3),
|
color: UiColors.success.withValues(
|
||||||
|
alpha: 0.3,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -382,7 +395,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.check,
|
UiIcons.check,
|
||||||
color: UiColors.textSuccess,
|
color: UiColors.textSuccess,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -419,10 +432,10 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _bloc.add(CheckInModeChanged(value)),
|
onTap: () => _bloc.add(CheckInModeChanged(value)),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? UiColors.white : UiColors.transparent,
|
color: isSelected ? UiColors.white : UiColors.transparent,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: UiConstants.radiusMd,
|
||||||
boxShadow: isSelected
|
boxShadow: isSelected
|
||||||
? <BoxShadow>[
|
? <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -476,21 +489,23 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
width: 96,
|
width: 96,
|
||||||
height: 96,
|
height: 96,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: scanned ? UiColors.tagSuccess : UiColors.tagInProgress,
|
color: scanned
|
||||||
|
? UiColors.tagSuccess
|
||||||
|
: UiColors.tagInProgress,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
scanned ? LucideIcons.check : LucideIcons.nfc,
|
scanned ? UiIcons.check : UiIcons.nfc,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: scanned ? UiColors.textSuccess : UiColors.primary,
|
color: scanned ? UiColors.textSuccess : UiColors.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
scanned ? 'Processing check-in...' : 'Ready to scan',
|
scanned ? 'Processing check-in...' : 'Ready to scan',
|
||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
scanned
|
scanned
|
||||||
? 'Please wait...'
|
? 'Please wait...'
|
||||||
@@ -499,7 +514,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
if (!scanned) ...<Widget>[
|
if (!scanned) ...<Widget>[
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 56,
|
height: 56,
|
||||||
@@ -520,7 +535,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
// But this dialog is just a function call.
|
// But this dialog is just a function call.
|
||||||
// It's safer to just return a result
|
// It's safer to just return a result
|
||||||
},
|
},
|
||||||
icon: const Icon(LucideIcons.nfc, size: 24),
|
icon: const Icon(UiIcons.nfc, size: 24),
|
||||||
label: Text(
|
label: Text(
|
||||||
'Tap to Scan',
|
'Tap to Scan',
|
||||||
style: UiTypography.headline4m.white,
|
style: UiTypography.headline4m.white,
|
||||||
@@ -529,7 +544,7 @@ class _ClockInPageState extends State<ClockInPage> {
|
|||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
foregroundColor: UiColors.white,
|
foregroundColor: UiColors.white,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
enum AttendanceType { checkin, checkout, breaks, days }
|
enum AttendanceType { checkin, checkout, breaks, days }
|
||||||
|
|
||||||
@@ -24,10 +23,10 @@ class AttendanceCard extends StatelessWidget {
|
|||||||
final _AttendanceStyle styles = _getStyles(type);
|
final _AttendanceStyle styles = _getStyles(type);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.bgSecondary),
|
border: Border.all(color: UiColors.bgSecondary),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -46,11 +45,11 @@ class AttendanceCard extends StatelessWidget {
|
|||||||
height: 32,
|
height: 32,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: styles.bgColor,
|
color: styles.bgColor,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: UiConstants.radiusMd,
|
||||||
),
|
),
|
||||||
child: Icon(styles.icon, size: 16, color: styles.iconColor),
|
child: Icon(styles.icon, size: 16, color: styles.iconColor),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: UiTypography.titleUppercase4m.textSecondary,
|
style: UiTypography.titleUppercase4m.textSecondary,
|
||||||
@@ -86,25 +85,25 @@ class AttendanceCard extends StatelessWidget {
|
|||||||
switch (type) {
|
switch (type) {
|
||||||
case AttendanceType.checkin:
|
case AttendanceType.checkin:
|
||||||
return _AttendanceStyle(
|
return _AttendanceStyle(
|
||||||
icon: LucideIcons.logIn,
|
icon: UiIcons.logIn,
|
||||||
bgColor: UiColors.primary.withValues(alpha: 0.1),
|
bgColor: UiColors.primary.withValues(alpha: 0.1),
|
||||||
iconColor: UiColors.primary,
|
iconColor: UiColors.primary,
|
||||||
);
|
);
|
||||||
case AttendanceType.checkout:
|
case AttendanceType.checkout:
|
||||||
return _AttendanceStyle(
|
return _AttendanceStyle(
|
||||||
icon: LucideIcons.logOut,
|
icon: UiIcons.logOut,
|
||||||
bgColor: UiColors.foreground.withValues(alpha: 0.1),
|
bgColor: UiColors.foreground.withValues(alpha: 0.1),
|
||||||
iconColor: UiColors.foreground,
|
iconColor: UiColors.foreground,
|
||||||
);
|
);
|
||||||
case AttendanceType.breaks:
|
case AttendanceType.breaks:
|
||||||
return _AttendanceStyle(
|
return _AttendanceStyle(
|
||||||
icon: LucideIcons.coffee,
|
icon: UiIcons.coffee,
|
||||||
bgColor: UiColors.accent.withValues(alpha: 0.2),
|
bgColor: UiColors.accent.withValues(alpha: 0.2),
|
||||||
iconColor: UiColors.accentForeground,
|
iconColor: UiColors.accentForeground,
|
||||||
);
|
);
|
||||||
case AttendanceType.days:
|
case AttendanceType.days:
|
||||||
return _AttendanceStyle(
|
return _AttendanceStyle(
|
||||||
icon: LucideIcons.calendar,
|
icon: UiIcons.calendar,
|
||||||
bgColor: UiColors.success.withValues(alpha: 0.1),
|
bgColor: UiColors.success.withValues(alpha: 0.1),
|
||||||
iconColor: UiColors.textSuccess,
|
iconColor: UiColors.textSuccess,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
enum CommuteMode {
|
enum CommuteMode {
|
||||||
lockedNoShift,
|
lockedNoShift,
|
||||||
@@ -158,8 +157,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
|
|
||||||
Widget _buildConsentCard() {
|
Widget _buildConsentCard() {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
@@ -169,7 +168,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
UiColors.primary.withValues(alpha: 0.1),
|
UiColors.primary.withValues(alpha: 0.1),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
@@ -192,12 +191,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -206,7 +205,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
'Enable Commute Tracking?',
|
'Enable Commute Tracking?',
|
||||||
style: UiTypography.body2m.textPrimary,
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'Share location 1hr before shift so your manager can see you\'re on the way.',
|
'Share location 1hr before shift so your manager can see you\'re on the way.',
|
||||||
style: UiTypography.body4r.textSecondary,
|
style: UiTypography.body4r.textSecondary,
|
||||||
@@ -216,7 +215,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -225,13 +224,15 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
setState(() => _localHasConsent = false);
|
setState(() => _localHasConsent = false);
|
||||||
},
|
},
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space2,
|
||||||
|
),
|
||||||
side: const BorderSide(color: UiColors.border),
|
side: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Text('Not Now', style: UiTypography.footnote1m),
|
child: Text('Not Now', style: UiTypography.footnote1m),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -239,7 +240,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space2,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Enable',
|
'Enable',
|
||||||
@@ -256,11 +259,11 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
|
|
||||||
Widget _buildPreShiftCard() {
|
Widget _buildPreShiftCard() {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
@@ -279,12 +282,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.navigation,
|
UiIcons.navigation,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: UiColors.textSecondary,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -295,11 +298,11 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
'On My Way',
|
'On My Way',
|
||||||
style: UiTypography.body2m.textPrimary,
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.clock,
|
UiIcons.clock,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: UiColors.textInactive,
|
color: UiColors.textInactive,
|
||||||
),
|
),
|
||||||
@@ -351,7 +354,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -370,7 +373,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.navigation,
|
UiIcons.navigation,
|
||||||
size: 48,
|
size: 48,
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
),
|
),
|
||||||
@@ -382,12 +385,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
'On My Way',
|
'On My Way',
|
||||||
style: UiTypography.displayMb.white,
|
style: UiTypography.displayMb.white,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
'Your manager can see you\'re heading to the site',
|
'Your manager can see you\'re heading to the site',
|
||||||
style: UiTypography.body2r.copyWith(
|
style: UiTypography.body2r.copyWith(
|
||||||
@@ -395,15 +398,15 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
),
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
if (widget.distanceMeters != null) ...<Widget>[
|
if (widget.distanceMeters != null) ...<Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
constraints: const BoxConstraints(maxWidth: 300),
|
constraints: const BoxConstraints(maxWidth: 300),
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white.withValues(alpha: 0.1),
|
color: UiColors.white.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.white.withValues(alpha: 0.2),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
),
|
),
|
||||||
@@ -416,7 +419,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
_formatDistance(widget.distanceMeters!),
|
_formatDistance(widget.distanceMeters!),
|
||||||
style: UiTypography.displayM.white,
|
style: UiTypography.displayM.white,
|
||||||
@@ -425,14 +428,14 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (widget.etaMinutes != null) ...<Widget>[
|
if (widget.etaMinutes != null) ...<Widget>[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
constraints: const BoxConstraints(maxWidth: 300),
|
constraints: const BoxConstraints(maxWidth: 300),
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white.withValues(alpha: 0.1),
|
color: UiColors.white.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.white.withValues(alpha: 0.2),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
),
|
),
|
||||||
@@ -445,7 +448,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'${widget.etaMinutes} min',
|
'${widget.etaMinutes} min',
|
||||||
style: UiTypography.headline1m.white,
|
style: UiTypography.headline1m.white,
|
||||||
@@ -455,7 +458,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Text(
|
Text(
|
||||||
'Most app features are locked while commute mode is on. You\'ll be able to clock in once you arrive.',
|
'Most app features are locked while commute mode is on. You\'ll be able to clock in once you arrive.',
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.copyWith(
|
||||||
@@ -469,7 +472,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _localIsCommuteOn = false);
|
setState(() => _localIsCommuteOn = false);
|
||||||
@@ -477,7 +480,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
foregroundColor: UiColors.white,
|
foregroundColor: UiColors.white,
|
||||||
side: BorderSide(color: UiColors.white.withValues(alpha: 0.3)),
|
side: BorderSide(color: UiColors.white.withValues(alpha: 0.3)),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
minimumSize: const Size(double.infinity, 48),
|
minimumSize: const Size(double.infinity, 48),
|
||||||
),
|
),
|
||||||
child: Text('Turn Off Commute Mode', style: UiTypography.buttonL),
|
child: Text('Turn Off Commute Mode', style: UiTypography.buttonL),
|
||||||
@@ -491,8 +496,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
|
|
||||||
Widget _buildArrivedCard() {
|
Widget _buildArrivedCard() {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 20),
|
margin: const EdgeInsets.only(bottom: UiConstants.space5),
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
@@ -502,7 +507,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
UiColors.tagActive,
|
UiColors.tagActive,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.1),
|
color: UiColors.black.withValues(alpha: 0.1),
|
||||||
@@ -521,17 +526,17 @@ class _CommuteTrackerState extends State<CommuteTracker> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.checkCircle,
|
UiIcons.check,
|
||||||
size: 32,
|
size: 32,
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
'You\'ve Arrived! 🎉',
|
'You\'ve Arrived! 🎉',
|
||||||
style: UiTypography.headline3m.textPrimary,
|
style: UiTypography.headline3m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
'You\'re at the shift location. Ready to clock in?',
|
'You\'re at the shift location. Ready to clock in?',
|
||||||
style: UiTypography.body2r.textSecondary,
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
|||||||
@@ -34,10 +34,12 @@ class DateSelector extends StatelessWidget {
|
|||||||
onTap: () => onSelect(date),
|
onTap: () => onSelect(date),
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 4),
|
margin: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space1,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? UiColors.primary : UiColors.white,
|
color: isSelected ? UiColors.primary : UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: isSelected
|
boxShadow: isSelected
|
||||||
? <BoxShadow>[
|
? <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
@@ -55,7 +57,8 @@ class DateSelector extends StatelessWidget {
|
|||||||
DateFormat('d').format(date),
|
DateFormat('d').format(date),
|
||||||
style: UiTypography.title1m.copyWith(
|
style: UiTypography.title1m.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: isSelected ? UiColors.white : UiColors.foreground,
|
color:
|
||||||
|
isSelected ? UiColors.white : UiColors.foreground,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
@@ -67,7 +70,7 @@ class DateSelector extends StatelessWidget {
|
|||||||
: UiColors.textInactive,
|
: UiColors.textInactive,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
if (hasShift)
|
if (hasShift)
|
||||||
Container(
|
Container(
|
||||||
width: 6,
|
width: 6,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class LocationMapPlaceholder extends StatelessWidget {
|
class LocationMapPlaceholder extends StatelessWidget {
|
||||||
|
|
||||||
@@ -19,7 +18,7 @@ class LocationMapPlaceholder extends StatelessWidget {
|
|||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.border,
|
color: UiColors.border,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
image: DecorationImage(
|
image: DecorationImage(
|
||||||
image: const NetworkImage(
|
image: const NetworkImage(
|
||||||
'https://maps.googleapis.com/maps/api/staticmap?center=40.7128,-74.0060&zoom=15&size=600x300&maptype=roadmap&markers=color:red%7C40.7128,-74.0060&key=YOUR_API_KEY',
|
'https://maps.googleapis.com/maps/api/staticmap?center=40.7128,-74.0060&zoom=15&size=600x300&maptype=roadmap&markers=color:red%7C40.7128,-74.0060&key=YOUR_API_KEY',
|
||||||
@@ -37,9 +36,12 @@ class LocationMapPlaceholder extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(LucideIcons.mapPin,
|
const Icon(
|
||||||
size: 48, color: UiColors.iconSecondary),
|
UiIcons.mapPin,
|
||||||
const SizedBox(height: 8),
|
size: 48,
|
||||||
|
color: UiColors.iconSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text('Map View (GPS)', style: UiTypography.body2r.textSecondary),
|
Text('Map View (GPS)', style: UiTypography.body2r.textSecondary),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -47,14 +49,17 @@ class LocationMapPlaceholder extends StatelessWidget {
|
|||||||
|
|
||||||
// Status Overlay
|
// Status Overlay
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 16,
|
bottom: UiConstants.space4,
|
||||||
left: 16,
|
left: UiConstants.space4,
|
||||||
right: 16,
|
right: UiConstants.space4,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space3,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.1),
|
color: UiColors.black.withValues(alpha: 0.1),
|
||||||
@@ -66,15 +71,13 @@ class LocationMapPlaceholder extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
isVerified
|
isVerified ? UiIcons.checkCircle : UiIcons.warning,
|
||||||
? LucideIcons.checkCircle
|
|
||||||
: LucideIcons.alertCircle,
|
|
||||||
color: isVerified
|
color: isVerified
|
||||||
? UiColors.textSuccess
|
? UiColors.textSuccess
|
||||||
: UiColors.destructive,
|
: UiColors.destructive,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class LunchBreakDialog extends StatefulWidget {
|
class LunchBreakDialog extends StatefulWidget {
|
||||||
const LunchBreakDialog({super.key, required this.onComplete});
|
const LunchBreakDialog({super.key, required this.onComplete});
|
||||||
@@ -48,7 +47,9 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Dialog(
|
return Dialog(
|
||||||
backgroundColor: UiColors.white,
|
backgroundColor: UiColors.white,
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.space6),
|
||||||
|
),
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
child: _buildCurrentStep(),
|
child: _buildCurrentStep(),
|
||||||
@@ -75,7 +76,7 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
|
|
||||||
Widget _buildStep1() {
|
Widget _buildStep1() {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -87,18 +88,18 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.coffee,
|
UiIcons.coffee,
|
||||||
size: 40,
|
size: 40,
|
||||||
color: UiColors.iconSecondary,
|
color: UiColors.iconSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
"Did You Take\na Lunch?",
|
"Did You Take\na Lunch?",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: UiTypography.headline1m.textPrimary,
|
style: UiTypography.headline1m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -110,10 +111,12 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
color: UiColors.transparent,
|
color: UiColors.transparent,
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
@@ -124,7 +127,7 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -135,9 +138,11 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -156,169 +161,183 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
|
|||||||
Widget _buildStep2() {
|
Widget _buildStep2() {
|
||||||
// Time input
|
// Time input
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"When did you take lunch?",
|
"When did you take lunch?",
|
||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
// Mock Inputs
|
// Mock Inputs
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
value: _breakStart,
|
value: _breakStart,
|
||||||
items: _timeOptions
|
items: _timeOptions
|
||||||
.map((String t) => DropdownMenuItem(
|
.map(
|
||||||
value: t,
|
(String t) => DropdownMenuItem(
|
||||||
child: Text(t, style: UiTypography.body3r)))
|
value: t,
|
||||||
.toList(),
|
child: Text(t, style: UiTypography.body3r),
|
||||||
onChanged: (String? v) => setState(() => _breakStart = v),
|
),
|
||||||
decoration: const InputDecoration(
|
)
|
||||||
labelText: 'Start',
|
.toList(),
|
||||||
contentPadding:
|
onChanged: (String? v) => setState(() => _breakStart = v),
|
||||||
EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Start',
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 8,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
|
||||||
Expanded(
|
|
||||||
child: DropdownButtonFormField<String>(
|
|
||||||
isExpanded: true,
|
|
||||||
value: _breakEnd,
|
|
||||||
items: _timeOptions
|
|
||||||
.map((String t) => DropdownMenuItem(
|
|
||||||
value: t,
|
|
||||||
child: Text(t, style: UiTypography.body3r)))
|
|
||||||
.toList(),
|
|
||||||
onChanged: (String? v) => setState(() => _breakEnd = v),
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'End',
|
|
||||||
contentPadding:
|
|
||||||
EdgeInsets.symmetric(horizontal: 10, vertical: 8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => _step = 3);
|
|
||||||
},
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: UiColors.primary,
|
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
),
|
),
|
||||||
child: Text("Next", style: UiTypography.body1m.white),
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: DropdownButtonFormField<String>(
|
||||||
|
isExpanded: true,
|
||||||
|
value: _breakEnd,
|
||||||
|
items: _timeOptions
|
||||||
|
.map(
|
||||||
|
(String t) => DropdownMenuItem(
|
||||||
|
value: t,
|
||||||
|
child: Text(t, style: UiTypography.body3r),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onChanged: (String? v) => setState(() => _breakEnd = v),
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'End',
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => _step = 3);
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.primary,
|
||||||
|
minimumSize: const Size(double.infinity, 48),
|
||||||
),
|
),
|
||||||
],
|
child: Text("Next", style: UiTypography.body1m.white),
|
||||||
));
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStep2b() {
|
Widget _buildStep2b() {
|
||||||
// No lunch reason
|
// No lunch reason
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"Why didn't you take lunch?",
|
"Why didn't you take lunch?",
|
||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
..._noLunchReasons.map(
|
||||||
|
(String reason) => RadioListTile<String>(
|
||||||
|
title: Text(reason, style: UiTypography.body2r),
|
||||||
|
value: reason,
|
||||||
|
groupValue: _noLunchReason,
|
||||||
|
onChanged: (String? val) => setState(() => _noLunchReason = val),
|
||||||
|
activeColor: UiColors.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
),
|
||||||
..._noLunchReasons.map((String reason) => RadioListTile<String>(
|
|
||||||
title: Text(reason, style: UiTypography.body2r),
|
|
||||||
value: reason,
|
|
||||||
groupValue: _noLunchReason,
|
|
||||||
onChanged: (String? val) =>
|
|
||||||
setState(() => _noLunchReason = val),
|
|
||||||
activeColor: UiColors.primary,
|
|
||||||
)),
|
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _step = 3);
|
setState(() => _step = 3);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
minimumSize: const Size(double.infinity, 48),
|
minimumSize: const Size(double.infinity, 48),
|
||||||
),
|
|
||||||
child: Text("Next", style: UiTypography.body1m.white),
|
|
||||||
),
|
),
|
||||||
],
|
child: Text("Next", style: UiTypography.body1m.white),
|
||||||
));
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStep3() {
|
Widget _buildStep3() {
|
||||||
// Additional Notes
|
// Additional Notes
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"Additional Notes",
|
"Additional Notes",
|
||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
TextField(
|
TextField(
|
||||||
onChanged: (String v) => _additionalNotes = v,
|
onChanged: (String v) => _additionalNotes = v,
|
||||||
style: UiTypography.body2r,
|
style: UiTypography.body2r,
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
hintText: 'Add any details...',
|
hintText: 'Add any details...',
|
||||||
border: OutlineInputBorder(),
|
border: OutlineInputBorder(),
|
||||||
),
|
|
||||||
maxLines: 3,
|
|
||||||
),
|
),
|
||||||
|
maxLines: 3,
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
setState(() => _step = 4);
|
setState(() => _step = 4);
|
||||||
},
|
},
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
minimumSize: const Size(double.infinity, 48),
|
minimumSize: const Size(double.infinity, 48),
|
||||||
),
|
|
||||||
child: Text("Submit", style: UiTypography.body1m.white),
|
|
||||||
),
|
),
|
||||||
],
|
child: Text("Submit", style: UiTypography.body1m.white),
|
||||||
));
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStep4() {
|
Widget _buildStep4() {
|
||||||
// Success
|
// Success
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(LucideIcons.checkCircle,
|
const Icon(UiIcons.checkCircle, size: 64, color: UiColors.success),
|
||||||
size: 64, color: UiColors.success),
|
const SizedBox(height: UiConstants.space6),
|
||||||
const SizedBox(height: 24),
|
Text(
|
||||||
Text(
|
"Break Logged!",
|
||||||
"Break Logged!",
|
style: UiTypography.headline1m,
|
||||||
style: UiTypography.headline1m,
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: widget.onComplete,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.primary,
|
||||||
|
minimumSize: const Size(double.infinity, 48),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
child: Text("Close", style: UiTypography.body1m.white),
|
||||||
ElevatedButton(
|
),
|
||||||
onPressed: widget.onComplete,
|
],
|
||||||
style: ElevatedButton.styleFrom(
|
),
|
||||||
backgroundColor: UiColors.primary,
|
);
|
||||||
minimumSize: const Size(double.infinity, 48),
|
|
||||||
),
|
|
||||||
child: Text("Close", style: UiTypography.body1m.white),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class SwipeToCheckIn extends StatefulWidget {
|
class SwipeToCheckIn extends StatefulWidget {
|
||||||
|
|
||||||
const SwipeToCheckIn({
|
const SwipeToCheckIn({
|
||||||
super.key,
|
super.key,
|
||||||
this.onCheckIn,
|
this.onCheckIn,
|
||||||
@@ -73,8 +71,9 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Color baseColor =
|
final Color baseColor = widget.isCheckedIn
|
||||||
widget.isCheckedIn ? UiColors.success : UiColors.primary;
|
? UiColors.success
|
||||||
|
: UiColors.primary;
|
||||||
|
|
||||||
if (widget.mode == 'nfc') {
|
if (widget.mode == 'nfc') {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@@ -93,7 +92,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
height: 56,
|
height: 56,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: baseColor,
|
color: baseColor,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: baseColor.withValues(alpha: 0.4),
|
color: baseColor.withValues(alpha: 0.4),
|
||||||
@@ -106,8 +105,8 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(LucideIcons.wifi, color: UiColors.white),
|
const Icon(UiIcons.wifi, color: UiColors.white),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Text(
|
Text(
|
||||||
widget.isLoading
|
widget.isLoading
|
||||||
? (widget.isCheckedIn
|
? (widget.isCheckedIn
|
||||||
@@ -129,10 +128,12 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
|
|
||||||
// Calculate background color based on drag
|
// Calculate background color based on drag
|
||||||
final double progress = _dragValue / maxDrag;
|
final double progress = _dragValue / maxDrag;
|
||||||
final Color startColor =
|
final Color startColor = widget.isCheckedIn
|
||||||
widget.isCheckedIn ? UiColors.success : UiColors.primary;
|
? UiColors.success
|
||||||
final Color endColor =
|
: UiColors.primary;
|
||||||
widget.isCheckedIn ? UiColors.primary : UiColors.success;
|
final Color endColor = widget.isCheckedIn
|
||||||
|
? UiColors.primary
|
||||||
|
: UiColors.success;
|
||||||
final Color currentColor =
|
final Color currentColor =
|
||||||
Color.lerp(startColor, endColor, progress) ?? startColor;
|
Color.lerp(startColor, endColor, progress) ?? startColor;
|
||||||
|
|
||||||
@@ -140,7 +141,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
height: 56,
|
height: 56,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: currentColor,
|
color: currentColor,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.1),
|
color: UiColors.black.withValues(alpha: 0.1),
|
||||||
@@ -173,14 +174,16 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
left: 4 + _dragValue,
|
left: 4 + _dragValue,
|
||||||
top: 4,
|
top: 4,
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onHorizontalDragUpdate: (DragUpdateDetails d) => _onDragUpdate(d, maxWidth),
|
onHorizontalDragUpdate: (DragUpdateDetails d) =>
|
||||||
onHorizontalDragEnd: (DragEndDetails d) => _onDragEnd(d, maxWidth),
|
_onDragUpdate(d, maxWidth),
|
||||||
|
onHorizontalDragEnd: (DragEndDetails d) =>
|
||||||
|
_onDragEnd(d, maxWidth),
|
||||||
child: Container(
|
child: Container(
|
||||||
width: _handleSize,
|
width: _handleSize,
|
||||||
height: _handleSize,
|
height: _handleSize,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withValues(alpha: 0.1),
|
color: UiColors.black.withValues(alpha: 0.1),
|
||||||
@@ -191,9 +194,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
|||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
_isComplete
|
_isComplete ? UiIcons.check : UiIcons.arrowRight,
|
||||||
? LucideIcons.check
|
|
||||||
: LucideIcons.arrowRight,
|
|
||||||
color: startColor,
|
color: startColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import '../blocs/payments/payments_bloc.dart';
|
import '../blocs/payments/payments_bloc.dart';
|
||||||
@@ -33,12 +33,22 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
return BlocProvider<PaymentsBloc>.value(
|
return BlocProvider<PaymentsBloc>.value(
|
||||||
value: _bloc,
|
value: _bloc,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
backgroundColor: UiColors.background,
|
||||||
body: BlocBuilder<PaymentsBloc, PaymentsState>(
|
body: BlocBuilder<PaymentsBloc, PaymentsState>(
|
||||||
builder: (BuildContext context, PaymentsState state) {
|
builder: (BuildContext context, PaymentsState state) {
|
||||||
if (state is PaymentsLoading) {
|
if (state is PaymentsLoading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
color: UiColors.primary,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else if (state is PaymentsError) {
|
} else if (state is PaymentsError) {
|
||||||
return Center(child: Text('Error: ${state.message}'));
|
return Center(
|
||||||
|
child: Text(
|
||||||
|
'Error: ${state.message}',
|
||||||
|
style: UiTypography.body2r.textError,
|
||||||
|
),
|
||||||
|
);
|
||||||
} else if (state is PaymentsLoaded) {
|
} else if (state is PaymentsLoaded) {
|
||||||
return _buildContent(context, state);
|
return _buildContent(context, state);
|
||||||
}
|
}
|
||||||
@@ -55,63 +65,57 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
// Header Section with Gradient
|
// Header Section with Gradient
|
||||||
Container(
|
Container(
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: <Color>[Color(0xFF0032A0), Color(0xFF333F48)],
|
colors: <Color>[
|
||||||
|
UiColors.primary,
|
||||||
|
UiColors.primary.withValues(alpha: 0.8),
|
||||||
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
20,
|
UiConstants.space5,
|
||||||
MediaQuery.of(context).padding.top + 24,
|
MediaQuery.of(context).padding.top + UiConstants.space6,
|
||||||
20,
|
UiConstants.space5,
|
||||||
32,
|
UiConstants.space8,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Text(
|
Text(
|
||||||
"Earnings",
|
"Earnings",
|
||||||
style: TextStyle(
|
style: UiTypography.displayMb.white,
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Main Balance
|
// Main Balance
|
||||||
Center(
|
Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Text(
|
Text(
|
||||||
"Total Earnings",
|
"Total Earnings",
|
||||||
style: TextStyle(
|
style: UiTypography.body2r.copyWith(
|
||||||
color: Color(0xFFF8E08E),
|
color: UiColors.accent,
|
||||||
fontSize: 14,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
"\$${state.summary.totalEarnings.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}",
|
"\$${state.summary.totalEarnings.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]},')}",
|
||||||
style: const TextStyle(
|
style: UiTypography.displayL.white,
|
||||||
fontSize: 36,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Period Tabs
|
// Period Tabs
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(4),
|
padding: const EdgeInsets.all(UiConstants.space1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.2),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -127,9 +131,9 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
|
|
||||||
// Main Content - Offset upwards
|
// Main Content - Offset upwards
|
||||||
Transform.translate(
|
Transform.translate(
|
||||||
offset: const Offset(0, -16),
|
offset: const Offset(0, -UiConstants.space4),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -138,76 +142,75 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
payments: state.history,
|
payments: state.history,
|
||||||
period: state.activePeriod,
|
period: state.activePeriod,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
// Quick Stats
|
// Quick Stats
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PaymentStatsCard(
|
child: PaymentStatsCard(
|
||||||
icon: LucideIcons.trendingUp,
|
icon: UiIcons.chart,
|
||||||
iconColor: const Color(0xFF059669),
|
iconColor: UiColors.success,
|
||||||
label: "This Week",
|
label: "This Week",
|
||||||
amount: "\$${state.summary.weeklyEarnings}",
|
amount: "\$${state.summary.weeklyEarnings}",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PaymentStatsCard(
|
child: PaymentStatsCard(
|
||||||
icon: LucideIcons.calendar,
|
icon: UiIcons.calendar,
|
||||||
iconColor: const Color(0xFF2563EB),
|
iconColor: UiColors.primary,
|
||||||
label: "This Month",
|
label: "This Month",
|
||||||
amount: "\$${state.summary.monthlyEarnings.toStringAsFixed(0)}",
|
amount: "\$${state.summary.monthlyEarnings.toStringAsFixed(0)}",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
// Pending Pay
|
// Pending Pay
|
||||||
if(state.summary.pendingEarnings > 0) PendingPayCard(
|
if (state.summary.pendingEarnings > 0)
|
||||||
amount: state.summary.pendingEarnings,
|
PendingPayCard(
|
||||||
onCashOut: () {
|
amount: state.summary.pendingEarnings,
|
||||||
Modular.to.pushNamed('/early-pay');
|
onCashOut: () {
|
||||||
},
|
Modular.to.pushNamed('/early-pay');
|
||||||
),
|
},
|
||||||
const SizedBox(height: 24),
|
),
|
||||||
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
|
|
||||||
// Recent Payments
|
// Recent Payments
|
||||||
if (state.history.isNotEmpty) Column(
|
if (state.history.isNotEmpty)
|
||||||
children: <Widget>[
|
Column(
|
||||||
const Text(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
"Recent Payments",
|
children: <Widget>[
|
||||||
style: TextStyle(
|
Text(
|
||||||
fontSize: 14,
|
"Recent Payments",
|
||||||
fontWeight: FontWeight.w600,
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: UiConstants.space3),
|
||||||
const SizedBox(height: 12),
|
Column(
|
||||||
Column(
|
children: state.history.map((StaffPayment payment) {
|
||||||
children: state.history.map((StaffPayment payment) {
|
return Padding(
|
||||||
return Padding(
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
bottom: UiConstants.space2),
|
||||||
child: PaymentHistoryItem(
|
child: PaymentHistoryItem(
|
||||||
amount: payment.amount,
|
amount: payment.amount,
|
||||||
title: "Shift Payment",
|
title: "Shift Payment",
|
||||||
location: "Varies",
|
location: "Varies",
|
||||||
address: "Payment ID: ${payment.id}",
|
address: "Payment ID: ${payment.id}",
|
||||||
date: payment.paidAt != null
|
date: payment.paidAt != null
|
||||||
? DateFormat('E, MMM d').format(payment.paidAt!)
|
? DateFormat('E, MMM d')
|
||||||
: 'Pending',
|
.format(payment.paidAt!)
|
||||||
workedTime: "Completed",
|
: 'Pending',
|
||||||
hours: 0,
|
workedTime: "Completed",
|
||||||
rate: 0.0,
|
hours: 0,
|
||||||
status: payment.status.name.toUpperCase(),
|
rate: 0.0,
|
||||||
),
|
status: payment.status.name.toUpperCase(),
|
||||||
);
|
),
|
||||||
}).toList(),
|
);
|
||||||
),
|
}).toList(),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
|
),
|
||||||
|
|
||||||
const SizedBox(height: 100),
|
const SizedBox(height: 100),
|
||||||
],
|
],
|
||||||
@@ -225,19 +228,17 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
|||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => _bloc.add(ChangePeriodEvent(value)),
|
onTap: () => _bloc.add(ChangePeriodEvent(value)),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? Colors.white : Colors.transparent,
|
color: isSelected ? UiColors.white : Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: isSelected
|
||||||
fontSize: 14,
|
? UiTypography.body2m.copyWith(color: UiColors.primary)
|
||||||
fontWeight: FontWeight.w500,
|
: UiTypography.body2m.white,
|
||||||
color: isSelected ? const Color(0xFF0032A0) : Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:fl_chart/fl_chart.dart';
|
import 'package:fl_chart/fl_chart.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
@@ -25,26 +26,30 @@ class EarningsGraph extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
height: 200,
|
height: 200,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
"No sufficient data for graph",
|
||||||
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: const Center(child: Text("No sufficient data for graph")),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<FlSpot> spots = _generateSpots(validPayments);
|
final List<FlSpot> spots = _generateSpots(validPayments);
|
||||||
final double maxX = spots.isNotEmpty ? spots.last.x : 0.0;
|
|
||||||
final double maxY = spots.isNotEmpty ? spots.map((FlSpot s) => s.y).reduce((double a, double b) => a > b ? a : b) : 0.0;
|
final double maxY = spots.isNotEmpty ? spots.map((FlSpot s) => s.y).reduce((double a, double b) => a > b ? a : b) : 0.0;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
height: 220,
|
height: 220,
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
blurRadius: 12,
|
blurRadius: 12,
|
||||||
),
|
),
|
||||||
@@ -53,15 +58,11 @@ class EarningsGraph extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Text(
|
Text(
|
||||||
"Earnings Trend",
|
"Earnings Trend",
|
||||||
style: TextStyle(
|
style: UiTypography.body2b.textPrimary,
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: LineChart(
|
child: LineChart(
|
||||||
LineChartData(
|
LineChartData(
|
||||||
@@ -79,7 +80,7 @@ class EarningsGraph extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(top: 8.0),
|
padding: const EdgeInsets.only(top: 8.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
DateFormat('d').format(validPayments[index].paidAt!),
|
DateFormat('d').format(validPayments[index].paidAt!),
|
||||||
style: const TextStyle(fontSize: 10, color: Colors.grey),
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -96,13 +97,13 @@ class EarningsGraph extends StatelessWidget {
|
|||||||
LineChartBarData(
|
LineChartBarData(
|
||||||
spots: spots,
|
spots: spots,
|
||||||
isCurved: true,
|
isCurved: true,
|
||||||
color: const Color(0xFF0032A0),
|
color: UiColors.primary,
|
||||||
barWidth: 3,
|
barWidth: 3,
|
||||||
isStrokeCapRound: true,
|
isStrokeCapRound: true,
|
||||||
dotData: const FlDotData(show: false),
|
dotData: const FlDotData(show: false),
|
||||||
belowBarData: BarAreaData(
|
belowBarData: BarAreaData(
|
||||||
show: true,
|
show: true,
|
||||||
color: const Color(0xFF0032A0).withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -121,7 +122,7 @@ class EarningsGraph extends StatelessWidget {
|
|||||||
List<FlSpot> _generateSpots(List<StaffPayment> data) {
|
List<FlSpot> _generateSpots(List<StaffPayment> data) {
|
||||||
// Generate spots based on index in the list for simplicity in this demo
|
// Generate spots based on index in the list for simplicity in this demo
|
||||||
// Real implementation would map to actual dates on X-axis
|
// Real implementation would map to actual dates on X-axis
|
||||||
return List.generate(data.length, (int index) {
|
return List<FlSpot>.generate(data.length, (int index) {
|
||||||
return FlSpot(index.toDouble(), data[index].amount);
|
return FlSpot(index.toDouble(), data[index].amount);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class PaymentHistoryItem extends StatelessWidget {
|
class PaymentHistoryItem extends StatelessWidget {
|
||||||
|
|
||||||
@@ -28,13 +28,13 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -50,23 +50,20 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
width: 6,
|
width: 6,
|
||||||
height: 6,
|
height: 6,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Color(0xFF3B82F6), // blue-500
|
color: UiColors.primary,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
const Text(
|
Text(
|
||||||
"PAID",
|
"PAID",
|
||||||
style: TextStyle(
|
style: UiTypography.titleUppercase4b.copyWith(
|
||||||
fontSize: 10,
|
color: UiColors.primary,
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: Color(0xFF2563EB), // blue-600
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -76,16 +73,16 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
width: 44,
|
width: 44,
|
||||||
height: 44,
|
height: 44,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF1F5F9), // slate-100
|
color: UiColors.secondary,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.dollarSign,
|
UiIcons.chart,
|
||||||
color: Color(0xFF334155), // slate-700
|
color: UiColors.mutedForeground,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
|
||||||
// Content
|
// Content
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -101,18 +98,11 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: const TextStyle(
|
style: UiTypography.body2b.textPrimary,
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
location,
|
location,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF475569), // slate-600
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -122,75 +112,59 @@ class PaymentHistoryItem extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"\$${amount.toStringAsFixed(0)}",
|
"\$${amount.toStringAsFixed(0)}",
|
||||||
style: const TextStyle(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\$${rate.toStringAsFixed(0)}/hr · ${hours}h",
|
"\$${rate.toStringAsFixed(0)}/hr · ${hours}h",
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
|
||||||
// Date and Time
|
// Date and Time
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.calendar,
|
UiIcons.calendar,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: Color(0xFF64748B),
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
date,
|
date,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.clock,
|
UiIcons.clock,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: Color(0xFF64748B),
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
workedTime,
|
workedTime,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 1),
|
||||||
|
|
||||||
// Address
|
// Address
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: Color(0xFF64748B),
|
color: UiColors.mutedForeground,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
address,
|
address,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class PaymentStatsCard extends StatelessWidget {
|
class PaymentStatsCard extends StatelessWidget {
|
||||||
|
|
||||||
const PaymentStatsCard({
|
const PaymentStatsCard({
|
||||||
super.key,
|
super.key,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
@@ -9,6 +9,7 @@ class PaymentStatsCard extends StatelessWidget {
|
|||||||
required this.label,
|
required this.label,
|
||||||
required this.amount,
|
required this.amount,
|
||||||
});
|
});
|
||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final Color iconColor;
|
final Color iconColor;
|
||||||
final String label;
|
final String label;
|
||||||
@@ -17,13 +18,13 @@ class PaymentStatsCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -35,24 +36,17 @@ class PaymentStatsCard extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(icon, size: 16, color: iconColor),
|
Icon(icon, size: 16, color: iconColor),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: UiTypography.body3r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF64748B), // slate-500
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
amount,
|
amount,
|
||||||
style: const TextStyle(
|
style: UiTypography.headline1m.textPrimary,
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
|
|
||||||
class PendingPayCard extends StatelessWidget {
|
class PendingPayCard extends StatelessWidget {
|
||||||
|
|
||||||
@@ -14,17 +14,13 @@ class PendingPayCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(14),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: const LinearGradient(
|
color: UiColors.tagInProgress,
|
||||||
colors: <Color>[Color(0xFFEFF6FF), Color(0xFFEFF6FF)], // blue-50 to blue-50
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(16),
|
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -36,63 +32,34 @@ class PendingPayCard extends StatelessWidget {
|
|||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
width: 40,
|
width: UiConstants.space10,
|
||||||
height: 40,
|
height: UiConstants.space10,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFE8F0FF),
|
color: UiColors.white.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
LucideIcons.dollarSign,
|
UiIcons.chart,
|
||||||
color: Color(0xFF0047FF),
|
color: UiColors.primary,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Text(
|
Text(
|
||||||
"Pending",
|
"Pending",
|
||||||
style: TextStyle(
|
style: UiTypography.body2b.textPrimary,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFF0F172A), // slate-900
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\$${amount.toStringAsFixed(0)} available",
|
"\$${amount.toStringAsFixed(0)} available",
|
||||||
style: const TextStyle(
|
style: UiTypography.body3m.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: Color(0xFF475569), // slate-600
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
/*
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: onCashOut,
|
|
||||||
icon: const Icon(LucideIcons.zap, size: 14),
|
|
||||||
label: const Text("Early Pay"),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFF0047FF),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(8),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
|
|
||||||
elevation: 4,
|
|
||||||
shadowColor: Colors.black.withOpacity(0.2),
|
|
||||||
textStyle: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart' hide ReadContext;
|
import 'package:flutter_bloc/flutter_bloc.dart' hide ReadContext;
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:krow_core/core.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import '../blocs/profile_cubit.dart';
|
import '../blocs/profile_cubit.dart';
|
||||||
import '../blocs/profile_state.dart';
|
import '../blocs/profile_state.dart';
|
||||||
import 'package:krow_core/core.dart';
|
import '../widgets/language_selector_bottom_sheet.dart';
|
||||||
import '../widgets/logout_button.dart';
|
import '../widgets/logout_button.dart';
|
||||||
|
import '../widgets/profile_header.dart';
|
||||||
import '../widgets/profile_menu_grid.dart';
|
import '../widgets/profile_menu_grid.dart';
|
||||||
import '../widgets/profile_menu_item.dart';
|
import '../widgets/profile_menu_item.dart';
|
||||||
import '../widgets/profile_header.dart';
|
|
||||||
import '../widgets/reliability_score_bar.dart';
|
import '../widgets/reliability_score_bar.dart';
|
||||||
import '../widgets/reliability_stats_card.dart';
|
import '../widgets/reliability_stats_card.dart';
|
||||||
import '../widgets/reliability_stats_card.dart';
|
|
||||||
import '../widgets/section_title.dart';
|
import '../widgets/section_title.dart';
|
||||||
import '../widgets/language_selector_bottom_sheet.dart';
|
|
||||||
|
|
||||||
/// The main Staff Profile page.
|
/// The main Staff Profile page.
|
||||||
///
|
///
|
||||||
@@ -68,17 +67,15 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
// Show loading spinner if status is loading
|
// Show loading spinner if status is loading
|
||||||
if (state.status == ProfileStatus.loading) {
|
if (state.status == ProfileStatus.loading) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.status == ProfileStatus.error) {
|
if (state.status == ProfileStatus.error) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
state.errorMessage ?? 'An error occurred',
|
state.errorMessage ?? 'An error occurred',
|
||||||
style: UiTypography.body1r.copyWith(
|
style: UiTypography.body1r.textError,
|
||||||
color: UiColors.destructive,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -121,7 +118,6 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
SectionTitle(i18n.sections.onboarding),
|
SectionTitle(i18n.sections.onboarding),
|
||||||
ProfileMenuGrid(
|
ProfileMenuGrid(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
icon: UiIcons.user,
|
icon: UiIcons.user,
|
||||||
@@ -181,18 +177,23 @@ class StaffProfilePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
SectionTitle(
|
SectionTitle(
|
||||||
i18n.header.title.contains("Perfil") ? "Ajustes" : "Settings",
|
i18n.header.title.contains("Perfil")
|
||||||
|
? "Ajustes"
|
||||||
|
: "Settings",
|
||||||
),
|
),
|
||||||
ProfileMenuGrid(
|
ProfileMenuGrid(
|
||||||
crossAxisCount: 3,
|
crossAxisCount: 3,
|
||||||
children: [
|
children: [
|
||||||
ProfileMenuItem(
|
ProfileMenuItem(
|
||||||
icon: UiIcons.globe,
|
icon: UiIcons.globe,
|
||||||
label: i18n.header.title.contains("Perfil") ? "Idioma" : "Language",
|
label: i18n.header.title.contains("Perfil")
|
||||||
|
? "Idioma"
|
||||||
|
: "Language",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => const LanguageSelectorBottomSheet(),
|
builder: (context) =>
|
||||||
|
const LanguageSelectorBottomSheet(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
|
|
||||||
/// A bottom sheet that allows the user to select their preferred language.
|
/// A bottom sheet that allows the user to select their preferred language.
|
||||||
@@ -15,10 +14,12 @@ class LanguageSelectorBottomSheet extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space6),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.background,
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.vertical(top: Radius.circular(UiConstants.radiusBase)),
|
borderRadius: BorderRadius.vertical(
|
||||||
|
top: Radius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -29,19 +30,19 @@ class LanguageSelectorBottomSheet extends StatelessWidget {
|
|||||||
style: UiTypography.headline4m,
|
style: UiTypography.headline4m,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
_buildLanguageOption(
|
_buildLanguageOption(
|
||||||
context,
|
context,
|
||||||
label: 'English',
|
label: 'English',
|
||||||
locale: AppLocale.en,
|
locale: AppLocale.en,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
_buildLanguageOption(
|
_buildLanguageOption(
|
||||||
context,
|
context,
|
||||||
label: 'Español',
|
label: 'Español',
|
||||||
locale: AppLocale.es,
|
locale: AppLocale.es,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -62,21 +63,23 @@ class LanguageSelectorBottomSheet extends StatelessWidget {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
// Dispatch the ChangeLocale event to the LocaleBloc
|
// Dispatch the ChangeLocale event to the LocaleBloc
|
||||||
Modular.get<LocaleBloc>().add(ChangeLocale(locale.flutterLocale));
|
Modular.get<LocaleBloc>().add(ChangeLocale(locale.flutterLocale));
|
||||||
|
|
||||||
// Close the bottom sheet
|
// Close the bottom sheet
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
// Force a rebuild of the entire app to reflect locale change instantly if not handled by root widget
|
// Force a rebuild of the entire app to reflect locale change instantly if not handled by root widget
|
||||||
// (Usually handled by BlocBuilder at the root, but this ensures settings are updated)
|
// (Usually handled by BlocBuilder at the root, but this ensures settings are updated)
|
||||||
},
|
},
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: UiConstants.space4,
|
vertical: UiConstants.space4,
|
||||||
horizontal: UiConstants.space4,
|
horizontal: UiConstants.space4,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? UiColors.primary.withValues(alpha: 0.1) : UiColors.background,
|
color: isSelected
|
||||||
|
? UiColors.primary.withValues(alpha: 0.1)
|
||||||
|
: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? UiColors.primary : UiColors.border,
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
@@ -88,12 +91,10 @@ class LanguageSelectorBottomSheet extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: isSelected
|
style: isSelected ? UiTypography.body1b.primary : UiTypography.body1r,
|
||||||
? UiTypography.body1b.copyWith(color: UiColors.primary)
|
|
||||||
: UiTypography.body1r,
|
|
||||||
),
|
),
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
Icon(
|
const Icon(
|
||||||
UiIcons.check,
|
UiIcons.check,
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
size: 24.0,
|
size: 24.0,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// The sign-out button widget.
|
/// The sign-out button widget.
|
||||||
///
|
///
|
||||||
@@ -14,31 +13,33 @@ class LogoutButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final i18n = t.staff.profile.header;
|
final i18n = t.staff.profile.header;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: const Color(0x00000000),
|
color: const Color(0x00000000),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: UiConstants.space4),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(LucideIcons.logOut, color: UiColors.destructive, size: 20),
|
const Icon(
|
||||||
SizedBox(width: UiConstants.space2),
|
UiIcons.logOut,
|
||||||
|
color: UiColors.destructive,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
i18n.sign_out,
|
i18n.sign_out,
|
||||||
style: UiTypography.body1m.copyWith(
|
style: UiTypography.body1m.textError,
|
||||||
color: UiColors.destructive,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -34,13 +34,13 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
UiConstants.space5,
|
UiConstants.space5,
|
||||||
UiConstants.space16,
|
UiConstants.space16,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
borderRadius: BorderRadius.vertical(
|
borderRadius: BorderRadius.vertical(
|
||||||
bottom: Radius.circular(UiConstants.space6),
|
bottom: Radius.circular(UiConstants.space6),
|
||||||
@@ -56,22 +56,20 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textSecondary,
|
||||||
color: UiColors.primaryForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: onSignOutTap,
|
onTap: onSignOutTap,
|
||||||
child: Text(
|
child: Text(
|
||||||
i18n.sign_out,
|
i18n.sign_out,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.copyWith(
|
||||||
color: UiColors.primaryForeground.withOpacity(0.8),
|
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space8),
|
const SizedBox(height: UiConstants.space8),
|
||||||
// Avatar Section
|
// Avatar Section
|
||||||
Stack(
|
Stack(
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
@@ -79,7 +77,7 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
width: 112,
|
width: 112,
|
||||||
height: 112,
|
height: 112,
|
||||||
padding: EdgeInsets.all(UiConstants.space1),
|
padding: const EdgeInsets.all(UiConstants.space1),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
@@ -87,13 +85,13 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: [
|
||||||
UiColors.accent,
|
UiColors.accent,
|
||||||
UiColors.accent.withOpacity(0.5),
|
UiColors.accent.withValues(alpha: 0.5),
|
||||||
UiColors.primaryForeground,
|
UiColors.primaryForeground,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.foreground.withOpacity(0.2),
|
color: UiColors.foreground.withValues(alpha: 0.2),
|
||||||
blurRadius: 10,
|
blurRadius: 10,
|
||||||
offset: const Offset(0, 4),
|
offset: const Offset(0, 4),
|
||||||
),
|
),
|
||||||
@@ -103,7 +101,7 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.primaryForeground.withOpacity(0.2),
|
color: UiColors.primaryForeground.withValues(alpha: 0.2),
|
||||||
width: 4,
|
width: 4,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -123,16 +121,16 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: [
|
colors: [
|
||||||
UiColors.accent,
|
UiColors.accent,
|
||||||
UiColors.accent.withOpacity(0.7),
|
UiColors.accent.withValues(alpha: 0.7),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Text(
|
child: Text(
|
||||||
fullName.isNotEmpty ? fullName[0].toUpperCase() : 'K',
|
fullName.isNotEmpty
|
||||||
style: UiTypography.displayM.copyWith(
|
? fullName[0].toUpperCase()
|
||||||
color: UiColors.primary,
|
: 'K',
|
||||||
),
|
style: UiTypography.displayM.primary,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@@ -148,7 +146,7 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
border: Border.all(color: UiColors.primary, width: 2),
|
border: Border.all(color: UiColors.primary, width: 2),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.foreground.withOpacity(0.1),
|
color: UiColors.foreground.withValues(alpha: 0.1),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -161,28 +159,24 @@ class ProfileHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
fullName,
|
fullName,
|
||||||
style: UiTypography.headline3m.copyWith(
|
style: UiTypography.headline3m.textPlaceholder,
|
||||||
color: UiColors.primaryForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space3,
|
horizontal: UiConstants.space3,
|
||||||
vertical: UiConstants.space1,
|
vertical: UiConstants.space1,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.accent.withOpacity(0.2),
|
color: UiColors.accent.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.space5),
|
borderRadius: BorderRadius.circular(UiConstants.space5),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
level,
|
level,
|
||||||
style: UiTypography.footnote1b.copyWith(
|
style: UiTypography.footnote1b.accent,
|
||||||
color: UiColors.accent,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -25,10 +25,10 @@ class ProfileMenuItem extends StatelessWidget {
|
|||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.all(UiConstants.space2),
|
padding: const EdgeInsets.all(UiConstants.space2),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1.0,
|
aspectRatio: 1.0,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
@@ -42,24 +42,23 @@ class ProfileMenuItem extends StatelessWidget {
|
|||||||
width: 36,
|
width: 36,
|
||||||
height: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.08),
|
color: UiColors.primary.withValues(alpha: 0.08),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Icon(icon, color: UiColors.primary, size: 20),
|
child: Icon(icon, color: UiColors.primary, size: 20),
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: UiConstants.space1),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space1,
|
||||||
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: UiTypography.footnote1m.copyWith(
|
style: UiTypography.footnote1m.textSecondary,
|
||||||
color: UiColors.foreground,
|
|
||||||
height: 1.2,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -76,16 +75,18 @@ class ProfileMenuItem extends StatelessWidget {
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: completed!
|
color: completed!
|
||||||
? UiColors.primary
|
? UiColors.primary
|
||||||
: UiColors.primary.withOpacity(0.1),
|
: UiColors.primary.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: completed!
|
child: completed!
|
||||||
? const Icon(Icons.check, size: 10, color: UiColors.primaryForeground)
|
? const Icon(
|
||||||
|
UiIcons.check,
|
||||||
|
size: 10,
|
||||||
|
color: UiColors.primaryForeground,
|
||||||
|
)
|
||||||
: Text(
|
: Text(
|
||||||
"!",
|
"!",
|
||||||
style: UiTypography.footnote2b.copyWith(
|
style: UiTypography.footnote2b.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ class ReliabilityScoreBar extends StatelessWidget {
|
|||||||
final score = (reliabilityScore ?? 0) / 100;
|
final score = (reliabilityScore ?? 0) / 100;
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -32,19 +32,15 @@ class ReliabilityScoreBar extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${reliabilityScore ?? 0}%",
|
"${reliabilityScore ?? 0}%",
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
ClipRRect(
|
ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(UiConstants.space1),
|
borderRadius: BorderRadius.circular(UiConstants.space1),
|
||||||
child: LinearProgressIndicator(
|
child: LinearProgressIndicator(
|
||||||
@@ -55,12 +51,10 @@ class ReliabilityScoreBar extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: UiConstants.space2),
|
padding: const EdgeInsets.only(top: UiConstants.space2),
|
||||||
child: Text(
|
child: Text(
|
||||||
i18n.description,
|
i18n.description,
|
||||||
style: UiTypography.footnote2r.copyWith(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Displays the staff member's reliability statistics (Shifts, Rating, On Time, etc.).
|
/// Displays the staff member's reliability statistics (Shifts, Rating, On Time, etc.).
|
||||||
///
|
///
|
||||||
@@ -24,14 +23,14 @@ class ReliabilityStatsCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.foreground.withOpacity(0.05),
|
color: UiColors.foreground.withValues(alpha: 0.05),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -42,31 +41,31 @@ class ReliabilityStatsCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
_buildStatItem(
|
_buildStatItem(
|
||||||
context,
|
context,
|
||||||
LucideIcons.briefcase,
|
UiIcons.briefcase,
|
||||||
"${totalShifts ?? 0}",
|
"${totalShifts ?? 0}",
|
||||||
"Shifts",
|
"Shifts",
|
||||||
),
|
),
|
||||||
_buildStatItem(
|
_buildStatItem(
|
||||||
context,
|
context,
|
||||||
LucideIcons.star,
|
UiIcons.star,
|
||||||
(averageRating ?? 0.0).toStringAsFixed(1),
|
(averageRating ?? 0.0).toStringAsFixed(1),
|
||||||
"Rating",
|
"Rating",
|
||||||
),
|
),
|
||||||
_buildStatItem(
|
_buildStatItem(
|
||||||
context,
|
context,
|
||||||
LucideIcons.clock,
|
UiIcons.clock,
|
||||||
"${onTimeRate ?? 0}%",
|
"${onTimeRate ?? 0}%",
|
||||||
"On Time",
|
"On Time",
|
||||||
),
|
),
|
||||||
_buildStatItem(
|
_buildStatItem(
|
||||||
context,
|
context,
|
||||||
LucideIcons.xCircle,
|
UiIcons.xCircle,
|
||||||
"${noShowCount ?? 0}",
|
"${noShowCount ?? 0}",
|
||||||
"No Shows",
|
"No Shows",
|
||||||
),
|
),
|
||||||
_buildStatItem(
|
_buildStatItem(
|
||||||
context,
|
context,
|
||||||
LucideIcons.ban,
|
UiIcons.ban,
|
||||||
"${cancellationCount ?? 0}",
|
"${cancellationCount ?? 0}",
|
||||||
"Cancel.",
|
"Cancel.",
|
||||||
),
|
),
|
||||||
@@ -88,26 +87,22 @@ class ReliabilityStatsCard extends StatelessWidget {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: Icon(icon, size: 20, color: UiColors.primary),
|
child: Icon(icon, size: 20, color: UiColors.primary),
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.body1b.copyWith(
|
style: UiTypography.body1b.textSecondary,
|
||||||
color: UiColors.foreground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
FittedBox(
|
FittedBox(
|
||||||
fit: BoxFit.scaleDown,
|
fit: BoxFit.scaleDown,
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.footnote2r.copyWith(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -13,14 +13,11 @@ class SectionTitle extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: EdgeInsets.only(left: UiConstants.space1),
|
padding: const EdgeInsets.only(left: UiConstants.space1),
|
||||||
margin: EdgeInsets.only(bottom: UiConstants.space3),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: Text(
|
child: Text(
|
||||||
title.toUpperCase(),
|
title.toUpperCase(),
|
||||||
style: UiTypography.footnote1b.copyWith(
|
style: UiTypography.footnote1b.textSecondary,
|
||||||
color: UiColors.mutedForeground,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class CertificatesPage extends StatelessWidget {
|
|||||||
Transform.translate(
|
Transform.translate(
|
||||||
offset: const Offset(0, -48),
|
offset: const Offset(0, -48),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
...documents.map((StaffDocument doc) => CertificateCard(
|
...documents.map((StaffDocument doc) => CertificateCard(
|
||||||
@@ -70,11 +70,11 @@ class CertificatesPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
AddCertificateCard(
|
AddCertificateCard(
|
||||||
onTap: () => _showUploadModal(context, null),
|
onTap: () => _showUploadModal(context, null),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,37 +12,36 @@ class AddCertificateCard extends StatelessWidget {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: <Color>[Colors.grey[50]!, Colors.grey[100]!], // Keep prototype style
|
colors: <Color>[
|
||||||
|
UiColors.bgSecondary.withValues(alpha: 0.5),
|
||||||
|
UiColors.bgSecondary,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: Colors.grey[300]!,
|
color: UiColors.border,
|
||||||
style: BorderStyle.solid,
|
style: BorderStyle.solid,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(UiIcons.add, color: UiColors.primary, size: 24),
|
const Icon(UiIcons.add, color: UiColors.primary, size: 24),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.add_more.title,
|
t.staff_certificates.add_more.title,
|
||||||
style: UiTypography.body1b.copyWith( // 16px Bold
|
style: UiTypography.body1b.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.add_more.subtitle,
|
t.staff_certificates.add_more.subtitle,
|
||||||
style: UiTypography.body3r.copyWith( // 12px Regular
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
|
||||||
|
|
||||||
class CertificateCard extends StatelessWidget {
|
class CertificateCard extends StatelessWidget {
|
||||||
final StaffDocument document;
|
final StaffDocument document;
|
||||||
@@ -39,13 +39,13 @@ class CertificateCard extends StatelessWidget {
|
|||||||
final _CertificateUiProps uiProps = _getUiProps(document.documentId);
|
final _CertificateUiProps uiProps = _getUiProps(document.documentId);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.space4),
|
borderRadius: UiConstants.radiusLg,
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 4,
|
blurRadius: 4,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -57,11 +57,14 @@ class CertificateCard extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
if (isExpiring || isExpired)
|
if (isExpiring || isExpired)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF9E547).withOpacity(0.2), // Yellow tint
|
color: UiColors.accent.withValues(alpha: 0.2), // Yellow tint
|
||||||
border: const Border(
|
border: Border(
|
||||||
bottom: BorderSide(color: Color(0x66F9E547)),
|
bottom: BorderSide(color: UiColors.accent.withValues(alpha: 0.4)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -76,16 +79,14 @@ class CertificateCard extends StatelessWidget {
|
|||||||
isExpired
|
isExpired
|
||||||
? t.staff_certificates.card.expired
|
? t.staff_certificates.card.expired
|
||||||
: t.staff_certificates.card.expires_in_days(days: _daysUntilExpiry(document.expiryDate)),
|
: t.staff_certificates.card.expires_in_days(days: _daysUntilExpiry(document.expiryDate)),
|
||||||
style: UiTypography.body3m.copyWith( // 12px Medium
|
style: UiTypography.body3m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -96,8 +97,8 @@ class CertificateCard extends StatelessWidget {
|
|||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: uiProps.color.withOpacity(0.1),
|
color: uiProps.color.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
@@ -137,7 +138,7 @@ class CertificateCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -151,16 +152,12 @@ class CertificateCard extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
document.name,
|
document.name,
|
||||||
style: UiTypography.body1m.copyWith( // 16px Medium
|
style: UiTypography.body1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
document.description ?? '', // Optional description
|
document.description ?? '', // Optional description
|
||||||
style: UiTypography.body3r.copyWith( // 12px Regular
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -172,7 +169,7 @@ class CertificateCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
|
|
||||||
if (showComplete) _buildCompleteStatus(document.expiryDate),
|
if (showComplete) _buildCompleteStatus(document.expiryDate),
|
||||||
|
|
||||||
@@ -186,9 +183,11 @@ class CertificateCard extends StatelessWidget {
|
|||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space3,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -202,9 +201,7 @@ class CertificateCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.card.upload_button,
|
t.staff_certificates.card.upload_button,
|
||||||
style: UiTypography.body2m.copyWith( // 14px Medium
|
style: UiTypography.body2m.white,
|
||||||
color: UiColors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -212,7 +209,7 @@ class CertificateCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
if (showComplete || isExpiring || isExpired) ...<Widget>[
|
if (showComplete || isExpiring || isExpired) ...<Widget>[
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: OutlinedButton.icon(
|
child: OutlinedButton.icon(
|
||||||
@@ -223,13 +220,15 @@ class CertificateCard extends StatelessWidget {
|
|||||||
foregroundColor: UiColors.textPrimary,
|
foregroundColor: UiColors.textPrimary,
|
||||||
side: const BorderSide(color: UiColors.border),
|
side: const BorderSide(color: UiColors.border),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space3,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
@@ -238,9 +237,11 @@ class CertificateCard extends StatelessWidget {
|
|||||||
label: Text(t.staff_certificates.card.remove),
|
label: Text(t.staff_certificates.card.remove),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: UiColors.destructive,
|
foregroundColor: UiColors.destructive,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space3,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -274,16 +275,14 @@ class CertificateCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.card.verified,
|
t.staff_certificates.card.verified,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (expiryDate != null)
|
if (expiryDate != null)
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.card.exp(date: DateFormat('MMM d, yyyy').format(expiryDate)),
|
t.staff_certificates.card.exp(date: DateFormat('MMM d, yyyy').format(expiryDate)),
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import 'package:flutter/material.dart';
|
|||||||
class CertificateUploadModal extends StatelessWidget {
|
class CertificateUploadModal extends StatelessWidget {
|
||||||
/// The document being edited, or null for a new upload.
|
/// The document being edited, or null for a new upload.
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
final dynamic document; // Using dynamic for now as we don't import domain here to avoid direct coupling if possible, but actually we should import domain.
|
final dynamic
|
||||||
|
document; // Using dynamic for now as we don't import domain here to avoid direct coupling if possible, but actually we should import domain.
|
||||||
// Ideally, widgets should be dumb. Let's import domain.
|
// Ideally, widgets should be dumb. Let's import domain.
|
||||||
|
|
||||||
final VoidCallback onSave;
|
final VoidCallback onSave;
|
||||||
@@ -24,13 +25,13 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
height: MediaQuery.of(context).size.height * 0.75,
|
height: MediaQuery.of(context).size.height * 0.75,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: BorderRadius.only(
|
||||||
topLeft: Radius.circular(24),
|
topLeft: Radius.circular(UiConstants.radiusBase),
|
||||||
topRight: Radius.circular(24),
|
topRight: Radius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.all(24),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -39,7 +40,7 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.title,
|
t.staff_certificates.upload_modal.title,
|
||||||
style: UiTypography.headline3m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.headline3m.textPrimary,
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: onCancel,
|
onPressed: onCancel,
|
||||||
@@ -47,35 +48,42 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.expiry_label,
|
t.staff_certificates.upload_modal.expiry_label,
|
||||||
style: UiTypography.body1m,
|
style: UiTypography.body1m,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space3,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(UiIcons.calendar, size: 20, color: UiColors.textSecondary),
|
const Icon(
|
||||||
const SizedBox(width: 12),
|
UiIcons.calendar,
|
||||||
|
size: 20,
|
||||||
|
color: UiColors.textSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.select_date,
|
t.staff_certificates.upload_modal.select_date,
|
||||||
style: UiTypography.body1m.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body1m.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.upload_file,
|
t.staff_certificates.upload_modal.upload_file,
|
||||||
style: UiTypography.body1m,
|
style: UiTypography.body1m,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
@@ -84,16 +92,16 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
color: UiColors.border,
|
color: UiColors.border,
|
||||||
style: BorderStyle.solid,
|
style: BorderStyle.solid,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
color: UiColors.background,
|
color: UiColors.background,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color(0xFFEFF6FF), // Light blue
|
color: UiColors.tagActive,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@@ -102,7 +110,7 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.drag_drop,
|
t.staff_certificates.upload_modal.drag_drop,
|
||||||
style: UiTypography.body1m,
|
style: UiTypography.body1m,
|
||||||
@@ -110,43 +118,51 @@ class CertificateUploadModal extends StatelessWidget {
|
|||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.upload_modal.supported_formats,
|
t.staff_certificates.upload_modal.supported_formats,
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: onCancel,
|
onPressed: onCancel,
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
side: const BorderSide(color: UiColors.border),
|
side: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Text(t.staff_certificates.upload_modal.cancel,
|
child: Text(
|
||||||
style: UiTypography.body1m.copyWith(color: UiColors.textPrimary)),
|
t.staff_certificates.upload_modal.cancel,
|
||||||
|
style: UiTypography.body1m.textPrimary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: onSave,
|
onPressed: onSave,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
child: Text(t.staff_certificates.upload_modal.save,
|
child: Text(
|
||||||
style: UiTypography.body1m.copyWith(color: Colors.white)),
|
t.staff_certificates.upload_modal.save,
|
||||||
|
style: UiTypography.body1m.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -20,13 +20,21 @@ class CertificatesHeader extends StatelessWidget {
|
|||||||
final int progressPercent = totalCount == 0 ? 0 : (progressValue * 100).round();
|
final int progressPercent = totalCount == 0 ? 0 : (progressValue * 100).round();
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.fromLTRB(20, 60, 20, 80),
|
padding: const EdgeInsets.fromLTRB(
|
||||||
|
UiConstants.space5,
|
||||||
|
60,
|
||||||
|
UiConstants.space5,
|
||||||
|
80,
|
||||||
|
),
|
||||||
// Keeping gradient as per prototype layout requirement
|
// Keeping gradient as per prototype layout requirement
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
colors: <Color>[UiColors.primary, Color(0xFF1E40AF)], // Using Primary and a darker shade
|
colors: <Color>[
|
||||||
|
UiColors.primary,
|
||||||
|
Color(0xFF1E40AF),
|
||||||
|
], // Using Primary and a darker shade
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -39,7 +47,7 @@ class CertificatesHeader extends StatelessWidget {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white.withOpacity(0.1),
|
color: UiColors.white.withValues(alpha: 0.1),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
@@ -49,16 +57,14 @@ class CertificatesHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.title,
|
t.staff_certificates.title,
|
||||||
style: UiTypography.headline3m.copyWith( // 18px Bold
|
style: UiTypography.headline3m.white,
|
||||||
color: UiColors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: UiConstants.space8),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -70,53 +76,48 @@ class CertificatesHeader extends StatelessWidget {
|
|||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
value: progressValue,
|
value: progressValue,
|
||||||
strokeWidth: 8,
|
strokeWidth: 8,
|
||||||
backgroundColor: UiColors.white.withOpacity(0.2),
|
backgroundColor: UiColors.white.withValues(alpha: 0.2),
|
||||||
valueColor: const AlwaysStoppedAnimation<Color>(
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||||
Color(0xFFF9E547), // Yellow from prototype
|
UiColors.accent, // Yellow from prototype
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Center(
|
Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'$progressPercent%',
|
'$progressPercent%',
|
||||||
style: UiTypography.display1b.copyWith( // 26px Bold
|
style: UiTypography.display1b.white,
|
||||||
color: UiColors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 24),
|
const SizedBox(width: UiConstants.space6),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.progress.title,
|
t.staff_certificates.progress.title,
|
||||||
style: UiTypography.body1b.copyWith( // 16px Bold
|
style: UiTypography.body1b.white,
|
||||||
color: UiColors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.progress.verified_count(completed: completedCount, total: totalCount),
|
t.staff_certificates.progress.verified_count(
|
||||||
style: UiTypography.body3r.copyWith( // 12px Regular
|
completed: completedCount, total: totalCount),
|
||||||
color: UiColors.white.withOpacity(0.7),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.shield,
|
UiIcons.shield,
|
||||||
color: Color(0xFFF9E547),
|
color: UiColors.accent,
|
||||||
size: 16,
|
size: 16,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
t.staff_certificates.progress.active,
|
t.staff_certificates.progress.active,
|
||||||
style: UiTypography.body3m.copyWith( // 12px Medium
|
style: UiTypography.body3m.accent,
|
||||||
color: const Color(0xFFF9E547),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -25,15 +25,14 @@ class DocumentsPage extends StatelessWidget {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
|
backgroundColor: UiColors.bgPopup,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(UiIcons.arrowLeft, color: UiColors.iconSecondary),
|
icon: const Icon(UiIcons.arrowLeft, color: UiColors.iconSecondary),
|
||||||
onPressed: () => Modular.to.pop(),
|
onPressed: () => Modular.to.pop(),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
t.staff_documents.title,
|
t.staff_documents.title,
|
||||||
style: UiTypography.headline3m.copyWith(
|
style: UiTypography.headline3m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
@@ -56,7 +55,7 @@ class DocumentsPage extends StatelessWidget {
|
|||||||
t.staff_documents.list.error(
|
t.staff_documents.list.error(
|
||||||
message: state.errorMessage ?? 'Unknown',
|
message: state.errorMessage ?? 'Unknown',
|
||||||
),
|
),
|
||||||
style: UiTypography.body1m.copyWith(color: UiColors.textError),
|
style: UiTypography.body1m.textError,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -64,20 +63,23 @@ class DocumentsPage extends StatelessWidget {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
t.staff_documents.list.empty,
|
t.staff_documents.list.empty,
|
||||||
style: UiTypography.body1m.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body1m.textSecondary,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ListView(
|
return ListView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
vertical: UiConstants.space6,
|
||||||
|
),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
DocumentsProgressCard(
|
DocumentsProgressCard(
|
||||||
completedCount: state.completedCount,
|
completedCount: state.completedCount,
|
||||||
totalCount: state.totalCount,
|
totalCount: state.totalCount,
|
||||||
progress: state.progress,
|
progress: state.progress,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
...state.documents.map(
|
...state.documents.map(
|
||||||
(StaffDocument doc) => DocumentCard(
|
(StaffDocument doc) => DocumentCard(
|
||||||
document: doc,
|
document: doc,
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
// ignore: depend_on_referenced_packages
|
// ignore: depend_on_referenced_packages
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
|
|
||||||
@@ -18,11 +17,11 @@ class DocumentCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -32,7 +31,7 @@ class DocumentCard extends StatelessWidget {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
@@ -43,7 +42,7 @@ class DocumentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -53,9 +52,7 @@ class DocumentCard extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
document.name,
|
document.name,
|
||||||
style: UiTypography.body1m.copyWith(
|
style: UiTypography.body1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_getStatusIcon(document.status),
|
_getStatusIcon(document.status),
|
||||||
],
|
],
|
||||||
@@ -64,15 +61,13 @@ class DocumentCard extends StatelessWidget {
|
|||||||
if (document.description != null)
|
if (document.description != null)
|
||||||
Text(
|
Text(
|
||||||
document.description!,
|
document.description!,
|
||||||
style: UiTypography.body2r.copyWith(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildStatusBadge(document.status),
|
_buildStatusBadge(document.status),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildActionButton(document.status),
|
_buildActionButton(document.status),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -114,27 +109,27 @@ class DocumentCard extends StatelessWidget {
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case DocumentStatus.verified:
|
case DocumentStatus.verified:
|
||||||
bg = UiColors.textSuccess.withOpacity(0.2);
|
bg = UiColors.tagSuccess;
|
||||||
text = UiColors.textSuccess;
|
text = UiColors.textSuccess;
|
||||||
label = t.staff_documents.card.verified;
|
label = t.staff_documents.card.verified;
|
||||||
break;
|
break;
|
||||||
case DocumentStatus.pending:
|
case DocumentStatus.pending:
|
||||||
bg = UiColors.textWarning.withOpacity(0.2);
|
bg = UiColors.tagPending;
|
||||||
text = UiColors.textWarning;
|
text = UiColors.textWarning;
|
||||||
label = t.staff_documents.card.pending;
|
label = t.staff_documents.card.pending;
|
||||||
break;
|
break;
|
||||||
case DocumentStatus.missing:
|
case DocumentStatus.missing:
|
||||||
bg = UiColors.textError.withOpacity(0.2);
|
bg = UiColors.textError.withValues(alpha: 0.1);
|
||||||
text = UiColors.textError;
|
text = UiColors.textError;
|
||||||
label = t.staff_documents.card.missing;
|
label = t.staff_documents.card.missing;
|
||||||
break;
|
break;
|
||||||
case DocumentStatus.rejected:
|
case DocumentStatus.rejected:
|
||||||
bg = UiColors.textError.withOpacity(0.2);
|
bg = UiColors.textError.withValues(alpha: 0.1);
|
||||||
text = UiColors.textError;
|
text = UiColors.textError;
|
||||||
label = t.staff_documents.card.rejected;
|
label = t.staff_documents.card.rejected;
|
||||||
break;
|
break;
|
||||||
case DocumentStatus.expired:
|
case DocumentStatus.expired:
|
||||||
bg = UiColors.textError.withOpacity(0.2);
|
bg = UiColors.textError.withValues(alpha: 0.1);
|
||||||
text = UiColors.textError;
|
text = UiColors.textError;
|
||||||
label = t.staff_documents.card.rejected; // Or define "Expired" string
|
label = t.staff_documents.card.rejected; // Or define "Expired" string
|
||||||
break;
|
break;
|
||||||
@@ -165,7 +160,7 @@ class DocumentCard extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
Icon(
|
||||||
isVerified ? UiIcons.eye : LucideIcons.upload,
|
isVerified ? UiIcons.eye : UiIcons.upload,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
@@ -174,9 +169,7 @@ class DocumentCard extends StatelessWidget {
|
|||||||
isVerified
|
isVerified
|
||||||
? t.staff_documents.card.view
|
? t.staff_documents.card.view
|
||||||
: t.staff_documents.card.upload,
|
: t.staff_documents.card.upload,
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ class DocumentsProgressCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -37,16 +37,14 @@ class DocumentsProgressCard extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_documents.verification_card.title,
|
t.staff_documents.verification_card.title,
|
||||||
style: UiTypography.body1m.copyWith(
|
style: UiTypography.body1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
t.staff_documents.verification_card.progress(
|
t.staff_documents.verification_card.progress(
|
||||||
completed: completedCount,
|
completed: completedCount,
|
||||||
total: totalCount,
|
total: totalCount,
|
||||||
),
|
),
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.primary),
|
style: UiTypography.body2r.primary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -83,10 +83,13 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
if (state.status == FormI9Status.success) {
|
if (state.status == FormI9Status.success) {
|
||||||
// Success view is handled by state check in build or we can navigate
|
// Success view is handled by state check in build or we can navigate
|
||||||
} else if (state.status == FormI9Status.failure) {
|
} else if (state.status == FormI9Status.failure) {
|
||||||
final ScaffoldMessengerState messenger = ScaffoldMessenger.of(context);
|
final ScaffoldMessengerState messenger =
|
||||||
|
ScaffoldMessenger.of(context);
|
||||||
messenger.hideCurrentSnackBar();
|
messenger.hideCurrentSnackBar();
|
||||||
messenger.showSnackBar(
|
messenger.showSnackBar(
|
||||||
SnackBar(content: Text(state.errorMessage ?? 'An error occurred')),
|
SnackBar(
|
||||||
|
content: Text(state.errorMessage ?? 'An error occurred'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -100,7 +103,10 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
_buildHeader(context, state),
|
_buildHeader(context, state),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
vertical: UiConstants.space6,
|
||||||
|
),
|
||||||
child: _buildCurrentStep(context, state),
|
child: _buildCurrentStep(context, state),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -118,9 +124,9 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
backgroundColor: UiColors.background,
|
backgroundColor: UiColors.background,
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(32),
|
padding: const EdgeInsets.all(UiConstants.space8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
@@ -132,40 +138,40 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
Container(
|
Container(
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color(0xFFDCFCE7),
|
color: UiColors.tagSuccess,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.success,
|
UiIcons.success,
|
||||||
color: Color(0xFF16A34A),
|
color: UiColors.textSuccess,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
'Form I-9 Submitted!',
|
'Form I-9 Submitted!',
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
'Your employment eligibility verification has been submitted.',
|
'Your employment eligibility verification has been submitted.',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => Modular.to.pop(true),
|
onPressed: () => Modular.to.pop(true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
foregroundColor: UiColors.bgPopup,
|
foregroundColor: UiColors.white,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@@ -183,7 +189,12 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
Widget _buildHeader(BuildContext context, FormI9State state) {
|
Widget _buildHeader(BuildContext context, FormI9State state) {
|
||||||
return Container(
|
return Container(
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
padding: const EdgeInsets.only(top: 60, bottom: 24, left: 20, right: 20),
|
padding: const EdgeInsets.only(
|
||||||
|
top: 60,
|
||||||
|
bottom: UiConstants.space6,
|
||||||
|
left: UiConstants.space5,
|
||||||
|
right: UiConstants.space5,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -193,31 +204,34 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
onTap: () => Modular.to.pop(),
|
onTap: () => Modular.to.pop(),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.arrowLeft,
|
UiIcons.arrowLeft,
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.white,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Form I-9',
|
'Form I-9',
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.white,
|
||||||
color: UiColors.bgPopup,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Employment Eligibility Verification',
|
'Employment Eligibility Verification',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Row(
|
Row(
|
||||||
children: _steps.asMap().entries.map((MapEntry<int, Map<String, String>> entry) {
|
children: _steps
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.map((MapEntry<int, Map<String, String>> entry) {
|
||||||
final int idx = entry.key;
|
final int idx = entry.key;
|
||||||
final bool isLast = idx == _steps.length - 1;
|
final bool isLast = idx == _steps.length - 1;
|
||||||
return Expanded(
|
return Expanded(
|
||||||
@@ -228,8 +242,8 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
height: 4,
|
height: 4,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: idx <= state.currentStep
|
color: idx <= state.currentStep
|
||||||
? UiColors.bgPopup
|
? UiColors.white
|
||||||
: UiColors.bgPopup.withOpacity(0.3),
|
: UiColors.white.withValues(alpha: 0.3),
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -240,20 +254,21 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Step ${state.currentStep + 1} of ${_steps.length}',
|
'Step ${state.currentStep + 1} of ${_steps.length}',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
_steps[state.currentStep]['title']!,
|
_steps[state.currentStep]['title']!,
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.white.copyWith(
|
||||||
color: UiColors.bgPopup,
|
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -292,8 +307,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary.copyWith(
|
||||||
color: UiColors.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -305,26 +319,26 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
),
|
),
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: placeholder,
|
hintText: placeholder,
|
||||||
hintStyle: TextStyle(color: Colors.grey[400]),
|
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: UiConstants.space4,
|
||||||
vertical: 16,
|
vertical: UiConstants.space4,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.primary),
|
borderSide: const BorderSide(color: UiColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -455,15 +469,15 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'State *',
|
'State *',
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary.copyWith(
|
||||||
color: UiColors.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
DropdownButtonFormField<String>(
|
DropdownButtonFormField<String>(
|
||||||
value: state.state.isEmpty ? null : state.state,
|
value: state.state.isEmpty ? null : state.state,
|
||||||
onChanged: (String? val) => context.read<FormI9Cubit>().stateChanged(val ?? ''),
|
onChanged: (String? val) =>
|
||||||
|
context.read<FormI9Cubit>().stateChanged(val ?? ''),
|
||||||
items: _usStates.map((String stateAbbr) {
|
items: _usStates.map((String stateAbbr) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: stateAbbr,
|
value: stateAbbr,
|
||||||
@@ -473,13 +487,15 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16),
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -507,9 +523,9 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'I attest, under penalty of perjury, that I am (check one of the following boxes):',
|
'I attest, under penalty of perjury, that I am (check one of the following boxes):',
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
_buildRadioOption(
|
_buildRadioOption(
|
||||||
context,
|
context,
|
||||||
state,
|
state,
|
||||||
@@ -578,15 +594,21 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRadioOption(BuildContext context, FormI9State state, String value, String label, {Widget? child}) {
|
Widget _buildRadioOption(
|
||||||
|
BuildContext context,
|
||||||
|
FormI9State state,
|
||||||
|
String value,
|
||||||
|
String label, {
|
||||||
|
Widget? child,
|
||||||
|
}) {
|
||||||
final bool isSelected = state.citizenshipStatus == value;
|
final bool isSelected = state.citizenshipStatus == value;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.read<FormI9Cubit>().citizenshipStatusChanged(value),
|
onTap: () => context.read<FormI9Cubit>().citizenshipStatusChanged(value),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? UiColors.primary : UiColors.border,
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
width: isSelected ? 2 : 1,
|
width: isSelected ? 2 : 1,
|
||||||
@@ -602,18 +624,16 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? UiColors.primary : Colors.grey,
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
width: isSelected ? 6 : 2,
|
width: isSelected ? 6 : 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -630,10 +650,10 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -643,15 +663,21 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
'Summary',
|
'Summary',
|
||||||
style: UiTypography.headline4m.copyWith(fontSize: 14),
|
style: UiTypography.headline4m.copyWith(fontSize: 14),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
_buildSummaryRow('Name', '${state.firstName} ${state.lastName}'),
|
_buildSummaryRow('Name', '${state.firstName} ${state.lastName}'),
|
||||||
_buildSummaryRow('Address', '${state.address}, ${state.city}'),
|
_buildSummaryRow('Address', '${state.address}, ${state.city}'),
|
||||||
_buildSummaryRow('SSN', '***-**-${state.ssn.length >= 4 ? state.ssn.substring(state.ssn.length - 4) : '****'}'),
|
_buildSummaryRow(
|
||||||
_buildSummaryRow('Citizenship', _getReadableCitizenship(state.citizenshipStatus)),
|
'SSN',
|
||||||
|
'***-**-${state.ssn.length >= 4 ? state.ssn.substring(state.ssn.length - 4) : '****'}',
|
||||||
|
),
|
||||||
|
_buildSummaryRow(
|
||||||
|
'Citizenship',
|
||||||
|
_getReadableCitizenship(state.citizenshipStatus),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
CheckboxListTile(
|
CheckboxListTile(
|
||||||
value: state.preparerUsed,
|
value: state.preparerUsed,
|
||||||
onChanged: (bool? val) {
|
onChanged: (bool? val) {
|
||||||
@@ -660,29 +686,27 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(
|
title: Text(
|
||||||
'I used a preparer or translator',
|
'I used a preparer or translator',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
controlAffinity: ListTileControlAffinity.leading,
|
controlAffinity: ListTileControlAffinity.leading,
|
||||||
activeColor: UiColors.primary,
|
activeColor: UiColors.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.amber[50],
|
color: UiColors.accent.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'I am aware that federal law provides for imprisonment and/or fines for false statements or use of false documents in connection with the completion of this form.',
|
'I am aware that federal law provides for imprisonment and/or fines for false statements or use of false documents in connection with the completion of this form.',
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFFB45309)),
|
style: UiTypography.body3r.textWarning.copyWith(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
'Signature (type your full name) *',
|
'Signature (type your full name) *',
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
TextField(
|
TextField(
|
||||||
@@ -690,44 +714,46 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
..selection = TextSelection.fromPosition(
|
..selection = TextSelection.fromPosition(
|
||||||
TextPosition(offset: state.signature.length),
|
TextPosition(offset: state.signature.length),
|
||||||
),
|
),
|
||||||
onChanged: (String val) => context.read<FormI9Cubit>().signatureChanged(val),
|
onChanged: (String val) =>
|
||||||
|
context.read<FormI9Cubit>().signatureChanged(val),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Type your full name',
|
hintText: 'Type your full name',
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: UiConstants.space4,
|
||||||
vertical: 16,
|
vertical: UiConstants.space4,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.primary),
|
borderSide: const BorderSide(color: UiColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
|
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
'Date',
|
'Date',
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF3F4F6),
|
color: UiColors.bgSecondary,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -747,15 +773,13 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
value,
|
value,
|
||||||
textAlign: TextAlign.end,
|
textAlign: TextAlign.end,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -780,7 +804,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
|
|
||||||
Widget _buildFooter(BuildContext context, FormI9State state) {
|
Widget _buildFooter(BuildContext context, FormI9State state) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
@@ -791,24 +815,30 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
if (state.currentStep > 0)
|
if (state.currentStep > 0)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 12),
|
padding: const EdgeInsets.only(right: UiConstants.space3),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () => _handleBack(context),
|
onPressed: () => _handleBack(context),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
side: const BorderSide(color: UiColors.border),
|
side: const BorderSide(color: UiColors.border),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(UiIcons.arrowLeft, size: 16, color: UiColors.textPrimary),
|
const Icon(
|
||||||
|
UiIcons.arrowLeft,
|
||||||
|
size: 16,
|
||||||
|
color: UiColors.textPrimary,
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'Back',
|
'Back',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -818,16 +848,20 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: (_canProceed(state) && state.status != FormI9Status.submitting)
|
onPressed: (
|
||||||
|
_canProceed(state) &&
|
||||||
|
state.status != FormI9Status.submitting)
|
||||||
? () => _handleNext(context, state.currentStep)
|
? () => _handleNext(context, state.currentStep)
|
||||||
: null,
|
: null,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
disabledBackgroundColor: Colors.grey[300],
|
disabledBackgroundColor: UiColors.bgSecondary,
|
||||||
foregroundColor: UiColors.bgPopup,
|
foregroundColor: UiColors.white,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@@ -836,7 +870,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.white,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -850,7 +884,7 @@ class _FormI9PageState extends State<FormI9Page> {
|
|||||||
),
|
),
|
||||||
if (state.currentStep < _steps.length - 1) ...<Widget>[
|
if (state.currentStep < _steps.length - 1) ...<Widget>[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.bgPopup),
|
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.white),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -146,7 +146,10 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
_buildHeader(context, state),
|
_buildHeader(context, state),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 24),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
vertical: UiConstants.space6,
|
||||||
|
),
|
||||||
child: _buildCurrentStep(context, state),
|
child: _buildCurrentStep(context, state),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -164,9 +167,9 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
backgroundColor: UiColors.background,
|
backgroundColor: UiColors.background,
|
||||||
body: Center(
|
body: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(32),
|
padding: const EdgeInsets.all(UiConstants.space8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(24),
|
borderRadius: BorderRadius.circular(24),
|
||||||
@@ -178,40 +181,40 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
Container(
|
Container(
|
||||||
width: 64,
|
width: 64,
|
||||||
height: 64,
|
height: 64,
|
||||||
decoration: const BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color(0xFFDCFCE7),
|
color: UiColors.tagSuccess,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.success,
|
UiIcons.success,
|
||||||
color: Color(0xFF16A34A),
|
color: UiColors.textSuccess,
|
||||||
size: 32,
|
size: 32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
'Form W-4 Submitted!',
|
'Form W-4 Submitted!',
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
'Your withholding certificate has been submitted to your employer.',
|
'Your withholding certificate has been submitted to your employer.',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: () => Modular.to.pop(true),
|
onPressed: () => Modular.to.pop(true),
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
foregroundColor: UiColors.bgPopup,
|
foregroundColor: UiColors.white,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@@ -229,7 +232,12 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
Widget _buildHeader(BuildContext context, FormW4State state) {
|
Widget _buildHeader(BuildContext context, FormW4State state) {
|
||||||
return Container(
|
return Container(
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
padding: const EdgeInsets.only(top: 60, bottom: 24, left: 20, right: 20),
|
padding: const EdgeInsets.only(
|
||||||
|
top: 60,
|
||||||
|
bottom: UiConstants.space6,
|
||||||
|
left: UiConstants.space5,
|
||||||
|
right: UiConstants.space5,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -239,31 +247,34 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
onTap: () => Modular.to.pop(),
|
onTap: () => Modular.to.pop(),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.arrowLeft,
|
UiIcons.arrowLeft,
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.white,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Form W-4',
|
'Form W-4',
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.white,
|
||||||
color: UiColors.bgPopup,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Employee\'s Withholding Certificate',
|
'Employee\'s Withholding Certificate',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Row(
|
Row(
|
||||||
children: _steps.asMap().entries.map((MapEntry<int, Map<String, String>> entry) {
|
children: _steps
|
||||||
|
.asMap()
|
||||||
|
.entries
|
||||||
|
.map((MapEntry<int, Map<String, String>> entry) {
|
||||||
final int idx = entry.key;
|
final int idx = entry.key;
|
||||||
final bool isLast = idx == _steps.length - 1;
|
final bool isLast = idx == _steps.length - 1;
|
||||||
return Expanded(
|
return Expanded(
|
||||||
@@ -274,8 +285,8 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
height: 4,
|
height: 4,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: idx <= state.currentStep
|
color: idx <= state.currentStep
|
||||||
? UiColors.bgPopup
|
? UiColors.white
|
||||||
: UiColors.bgPopup.withOpacity(0.3),
|
: UiColors.white.withValues(alpha: 0.3),
|
||||||
borderRadius: BorderRadius.circular(2),
|
borderRadius: BorderRadius.circular(2),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -286,18 +297,19 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Step ${state.currentStep + 1} of ${_steps.length}',
|
'Step ${state.currentStep + 1} of ${_steps.length}',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.bgPopup.withOpacity(0.7)),
|
style: UiTypography.body3r.copyWith(
|
||||||
|
color: UiColors.white.withValues(alpha: 0.7),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
_steps[state.currentStep]['title']!,
|
_steps[state.currentStep]['title']!,
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.white.copyWith(
|
||||||
color: UiColors.bgPopup,
|
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -339,8 +351,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary.copyWith(
|
||||||
color: UiColors.textSecondary,
|
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -352,26 +363,26 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
),
|
),
|
||||||
onChanged: onChanged,
|
onChanged: onChanged,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: placeholder,
|
hintText: placeholder,
|
||||||
hintStyle: TextStyle(color: Colors.grey[400]),
|
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: UiConstants.space4,
|
||||||
vertical: 16,
|
vertical: UiConstants.space4,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.primary),
|
borderSide: const BorderSide(color: UiColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -438,25 +449,25 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue[50],
|
color: UiColors.tagActive,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: const <Widget>[
|
children: <Widget>[
|
||||||
Icon(UiIcons.info, color: Color(0xFF2563EB), size: 20),
|
const Icon(UiIcons.info, color: UiColors.primary, size: 20),
|
||||||
SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Your filing status determines your standard deduction and tax rates.',
|
'Your filing status determines your standard deduction and tax rates.',
|
||||||
style: TextStyle(fontSize: 14, color: Color(0xFF1D4ED8)),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
_buildRadioOption(
|
_buildRadioOption(
|
||||||
context,
|
context,
|
||||||
state,
|
state,
|
||||||
@@ -484,15 +495,21 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildRadioOption(BuildContext context, FormW4State state, String value, String label, String? subLabel) {
|
Widget _buildRadioOption(
|
||||||
|
BuildContext context,
|
||||||
|
FormW4State state,
|
||||||
|
String value,
|
||||||
|
String label,
|
||||||
|
String? subLabel,
|
||||||
|
) {
|
||||||
final bool isSelected = state.filingStatus == value;
|
final bool isSelected = state.filingStatus == value;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => context.read<FormW4Cubit>().filingStatusChanged(value),
|
onTap: () => context.read<FormW4Cubit>().filingStatusChanged(value),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? UiColors.primary : UiColors.border,
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
width: isSelected ? 2 : 1,
|
width: isSelected ? 2 : 1,
|
||||||
@@ -508,29 +525,25 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? UiColors.primary : Colors.grey,
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
width: isSelected ? 6 : 2,
|
width: isSelected ? 6 : 2,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (subLabel != null) ...<Widget>[
|
if (subLabel != null) ...<Widget>[
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
subLabel,
|
subLabel,
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
@@ -546,36 +559,32 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.amber[50],
|
color: UiColors.accent.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: const Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(
|
const Icon(
|
||||||
UiIcons.help,
|
UiIcons.help,
|
||||||
color: Color(0xFFD97706),
|
color: UiColors.accent,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'When to complete this step?',
|
'When to complete this step?',
|
||||||
style: TextStyle(
|
style: UiTypography.body2m.accent,
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Color(0xFF92400E),
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'Complete this step only if you hold more than one job at a time, or are married filing jointly and your spouse also works.',
|
'Complete this step only if you hold more than one job at a time, or are married filing jointly and your spouse also works.',
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFFB45309)),
|
style: UiTypography.body3r.accent,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -583,18 +592,17 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => context.read<FormW4Cubit>().multipleJobsChanged(!state.multipleJobs),
|
onTap: () =>
|
||||||
|
context.read<FormW4Cubit>().multipleJobsChanged(!state.multipleJobs),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: state.multipleJobs
|
color: state.multipleJobs ? UiColors.primary : UiColors.border,
|
||||||
? UiColors.primary
|
|
||||||
: UiColors.border,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -604,20 +612,16 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: state.multipleJobs
|
color: state.multipleJobs ? UiColors.primary : UiColors.bgPopup,
|
||||||
? UiColors.primary
|
|
||||||
: UiColors.bgPopup,
|
|
||||||
borderRadius: BorderRadius.circular(6),
|
borderRadius: BorderRadius.circular(6),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: state.multipleJobs
|
color: state.multipleJobs ? UiColors.primary : UiColors.border,
|
||||||
? UiColors.primary
|
|
||||||
: Colors.grey,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: state.multipleJobs
|
child: state.multipleJobs
|
||||||
? const Icon(
|
? const Icon(
|
||||||
UiIcons.check,
|
UiIcons.check,
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.white,
|
||||||
size: 16,
|
size: 16,
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@@ -629,16 +633,12 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'I have multiple jobs or my spouse works',
|
'I have multiple jobs or my spouse works',
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(
|
Text(
|
||||||
'Check this box if there are only two jobs total',
|
'Check this box if there are only two jobs total',
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -651,7 +651,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
Text(
|
Text(
|
||||||
'If this does not apply, you can continue to the next step',
|
'If this does not apply, you can continue to the next step',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -661,30 +661,30 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.blue[50], // Same note about blue migration
|
color: UiColors.tagActive,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: const Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(UiIcons.info, color: Color(0xFF2563EB), size: 20),
|
const Icon(UiIcons.info, color: UiColors.primary, size: 20),
|
||||||
SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
'If your total income will be \$200,000 or less (\$400,000 if married filing jointly), you may claim credits for dependents.',
|
'If your total income will be \$200,000 or less (\$400,000 if married filing jointly), you may claim credits for dependents.',
|
||||||
style: TextStyle(fontSize: 14, color: Color(0xFF1D4ED8)),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -715,10 +715,10 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
if (_totalCredits(state) > 0) ...<Widget>[
|
if (_totalCredits(state) > 0) ...<Widget>[
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFDCFCE7),
|
color: const Color(0xFFDCFCE7),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@@ -770,16 +770,12 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFDCFCE7),
|
color: UiColors.tagSuccess,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
badge,
|
badge,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote2b.textSuccess,
|
||||||
fontSize: 10,
|
|
||||||
color: Color(0xFF15803D),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -834,7 +830,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'These adjustments are optional. You can skip them if they don\'t apply.',
|
'These adjustments are optional. You can skip them if they don\'t apply.',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
_buildTextField(
|
_buildTextField(
|
||||||
@@ -848,7 +844,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Include interest, dividends, retirement income',
|
'Include interest, dividends, retirement income',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -863,7 +859,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
'If you expect to claim deductions other than the standard deduction',
|
'If you expect to claim deductions other than the standard deduction',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@@ -878,7 +874,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
padding: const EdgeInsets.only(top: 4, bottom: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Any additional tax you want withheld each pay period',
|
'Any additional tax you want withheld each pay period',
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -890,10 +886,10 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -927,22 +923,20 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.amber[50],
|
color: UiColors.accent.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: const Text(
|
child: Text(
|
||||||
'Under penalties of perjury, I declare that this certificate, to the best of my knowledge and belief, is true, correct, and complete.',
|
'Under penalties of perjury, I declare that this certificate, to the best of my knowledge and belief, is true, correct, and complete.',
|
||||||
style: TextStyle(fontSize: 12, color: Color(0xFFB45309)),
|
style: UiTypography.body3r.textWarning.copyWith(fontSize: 12),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
Text(
|
Text(
|
||||||
'Signature (type your full name) *',
|
'Signature (type your full name) *',
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
TextField(
|
TextField(
|
||||||
@@ -950,44 +944,46 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
..selection = TextSelection.fromPosition(
|
..selection = TextSelection.fromPosition(
|
||||||
TextPosition(offset: state.signature.length),
|
TextPosition(offset: state.signature.length),
|
||||||
),
|
),
|
||||||
onChanged: (String val) => context.read<FormW4Cubit>().signatureChanged(val),
|
onChanged: (String val) =>
|
||||||
|
context.read<FormW4Cubit>().signatureChanged(val),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Type your full name',
|
hintText: 'Type your full name',
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: 16,
|
horizontal: UiConstants.space4,
|
||||||
vertical: 16,
|
vertical: UiConstants.space4,
|
||||||
),
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
enabledBorder: OutlineInputBorder(
|
enabledBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.border),
|
borderSide: const BorderSide(color: UiColors.border),
|
||||||
),
|
),
|
||||||
focusedBorder: OutlineInputBorder(
|
focusedBorder: OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: const BorderSide(color: UiColors.primary),
|
borderSide: const BorderSide(color: UiColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
|
style: const TextStyle(fontFamily: 'Cursive', fontSize: 18),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
'Date',
|
'Date',
|
||||||
style: UiTypography.body3m.copyWith(
|
style: UiTypography.body3m.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF3F4F6),
|
color: UiColors.bgSecondary,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -1007,7 +1003,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
@@ -1035,7 +1031,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
|
|
||||||
Widget _buildFooter(BuildContext context, FormW4State state) {
|
Widget _buildFooter(BuildContext context, FormW4State state) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
@@ -1046,24 +1042,30 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
if (state.currentStep > 0)
|
if (state.currentStep > 0)
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 12),
|
padding: const EdgeInsets.only(right: UiConstants.space3),
|
||||||
child: OutlinedButton(
|
child: OutlinedButton(
|
||||||
onPressed: () => _handleBack(context),
|
onPressed: () => _handleBack(context),
|
||||||
style: OutlinedButton.styleFrom(
|
style: OutlinedButton.styleFrom(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
side: const BorderSide(color: UiColors.border),
|
side: const BorderSide(color: UiColors.border),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
const Icon(UiIcons.arrowLeft, size: 16, color: UiColors.textPrimary),
|
const Icon(
|
||||||
|
UiIcons.arrowLeft,
|
||||||
|
size: 16,
|
||||||
|
color: UiColors.textPrimary,
|
||||||
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Text(
|
Text(
|
||||||
'Back',
|
'Back',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -1073,16 +1075,20 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: (_canProceed(state) && state.status != FormW4Status.submitting)
|
onPressed: (
|
||||||
|
_canProceed(state) &&
|
||||||
|
state.status != FormW4Status.submitting)
|
||||||
? () => _handleNext(context, state.currentStep)
|
? () => _handleNext(context, state.currentStep)
|
||||||
: null,
|
: null,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
disabledBackgroundColor: Colors.grey[300],
|
disabledBackgroundColor: UiColors.bgSecondary,
|
||||||
foregroundColor: UiColors.bgPopup,
|
foregroundColor: UiColors.white,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
),
|
),
|
||||||
@@ -1091,7 +1097,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
width: 20,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.white,
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -1105,7 +1111,7 @@ class _FormW4PageState extends State<FormW4Page> {
|
|||||||
),
|
),
|
||||||
if (state.currentStep < _steps.length - 1) ...<Widget>[
|
if (state.currentStep < _steps.length - 1) ...<Widget>[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.bgPopup),
|
const Icon(UiIcons.arrowRight, size: 16, color: UiColors.white),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Tax Documents',
|
'Tax Documents',
|
||||||
style: UiTypography.headline3m.copyWith(color: UiColors.bgPopup),
|
style: UiTypography.headline3m.textSecondary,
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(24),
|
preferredSize: const Size.fromHeight(24),
|
||||||
@@ -37,7 +37,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'Complete required forms to start working',
|
'Complete required forms to start working',
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.copyWith(
|
||||||
color: UiColors.bgPopup.withOpacity(0.8),
|
color: UiColors.primaryForeground.withValues(alpha: 0.8),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -111,15 +111,11 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Document Progress',
|
'Document Progress',
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'$completedCount/$totalCount',
|
'$completedCount/$totalCount',
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -171,7 +167,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Center(child: Text(icon, style: UiTypography.headline1m)),
|
child: Center(child: Text(icon, style: UiTypography.headline1m)),
|
||||||
@@ -186,9 +182,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
form.title,
|
form.title,
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_buildStatusBadge(form.status),
|
_buildStatusBadge(form.status),
|
||||||
],
|
],
|
||||||
@@ -196,17 +190,14 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
form.subtitle ?? '',
|
form.subtitle ?? '',
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textSecondary.copyWith(
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
form.description ?? '',
|
form.description ?? '',
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -247,9 +238,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
const SizedBox(width: UiConstants.space1),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'Completed',
|
'Completed',
|
||||||
style: UiTypography.footnote2b.copyWith(
|
style: UiTypography.footnote2b.textSuccess,
|
||||||
color: UiColors.textSuccess,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -271,9 +260,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
const SizedBox(width: UiConstants.space1),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'In Progress',
|
'In Progress',
|
||||||
style: UiTypography.footnote2b.copyWith(
|
style: UiTypography.footnote2b.textWarning,
|
||||||
color: UiColors.textWarning,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -290,9 +277,7 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Not Started',
|
'Not Started',
|
||||||
style: UiTypography.footnote2b.copyWith(
|
style: UiTypography.footnote2b.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -316,16 +301,12 @@ class TaxFormsPage extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'Why are these needed?',
|
'Why are these needed?',
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
'I-9 and W-4 forms are required by federal law to verify your employment eligibility and set up correct tax withholding.',
|
'I-9 and W-4 forms are required by federal law to verify your employment eligibility and set up correct tax withholding.',
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
strings.title,
|
strings.title,
|
||||||
style: UiTypography.headline3m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.headline3m.textPrimary,
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
@@ -143,7 +143,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.08),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -157,15 +157,12 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
strings.secure_title,
|
strings.secure_title,
|
||||||
style: UiTypography.body2r.copyWith( // Was body2
|
style: UiTypography.body2m.textPrimary,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
strings.secure_subtitle,
|
strings.secure_subtitle,
|
||||||
style: UiTypography.body3r.copyWith(color: UiColors.textSecondary), // Was bodySmall
|
style: UiTypography.body3r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -199,7 +196,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
width: 48,
|
width: 48,
|
||||||
height: 48,
|
height: 48,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: primaryColor.withOpacity(0.1),
|
color: primaryColor.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
@@ -216,10 +213,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
account.bankName,
|
account.bankName,
|
||||||
style: UiTypography.body2r.copyWith( // Was body2
|
style: UiTypography.body2m.textPrimary,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
strings.account_ending(
|
strings.account_ending(
|
||||||
@@ -227,9 +221,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
? account.last4!
|
? account.last4!
|
||||||
: '----',
|
: '----',
|
||||||
),
|
),
|
||||||
style: UiTypography.body2r.copyWith( // Was body2
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -239,7 +231,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: primaryColor.withOpacity(0.15),
|
color: primaryColor.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -248,10 +240,7 @@ class BankAccountPage extends StatelessWidget {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
strings.primary,
|
strings.primary,
|
||||||
style: UiTypography.body3r.copyWith( // Was bodySmall
|
style: UiTypography.body3m.primary,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: primaryColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class _AddAccountFormState extends State<AddAccountForm> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
widget.strings.add_new_account,
|
widget.strings.add_new_account,
|
||||||
style: UiTypography.headline4m.copyWith(color: UiColors.textPrimary), // Was header4
|
style: UiTypography.headline4m.textPrimary, // Was header4
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
UiTextField(
|
UiTextField(
|
||||||
@@ -71,8 +71,7 @@ class _AddAccountFormState extends State<AddAccountForm> {
|
|||||||
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
||||||
child: Text(
|
child: Text(
|
||||||
widget.strings.account_type,
|
widget.strings.account_type,
|
||||||
style: UiTypography.body2r.copyWith( // Was body2
|
style: UiTypography.body2m.textSecondary,
|
||||||
color: UiColors.textSecondary, fontWeight: FontWeight.w500),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@@ -122,7 +121,7 @@ class _AddAccountFormState extends State<AddAccountForm> {
|
|||||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? UiColors.primary.withOpacity(0.05)
|
? UiColors.primary.withValues(alpha: 0.05)
|
||||||
: UiColors.bgPopup, // Was surface
|
: UiColors.bgPopup, // Was surface
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
@@ -133,8 +132,7 @@ class _AddAccountFormState extends State<AddAccountForm> {
|
|||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.body2r.copyWith( // Was body2
|
style: UiTypography.body2b.copyWith(
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: isSelected ? UiColors.primary : UiColors.textSecondary,
|
color: isSelected ? UiColors.primary : UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ class _TimeCardPageState extends State<TimeCardPage> {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
t.staff_time_card.title,
|
t.staff_time_card.title,
|
||||||
style: UiTypography.headline4m.copyWith(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
|
|||||||
@@ -19,22 +19,22 @@ class TimesheetCard extends StatelessWidget {
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case TimeCardStatus.approved:
|
case TimeCardStatus.approved:
|
||||||
statusBg = UiColors.textSuccess.withOpacity(0.12);
|
statusBg = UiColors.tagSuccess;
|
||||||
statusColor = UiColors.textSuccess;
|
statusColor = UiColors.textSuccess;
|
||||||
statusText = t.staff_time_card.status.approved;
|
statusText = t.staff_time_card.status.approved;
|
||||||
break;
|
break;
|
||||||
case TimeCardStatus.disputed:
|
case TimeCardStatus.disputed:
|
||||||
statusBg = UiColors.destructive.withOpacity(0.12);
|
statusBg = UiColors.destructive.withValues(alpha: 0.12);
|
||||||
statusColor = UiColors.destructive;
|
statusColor = UiColors.destructive;
|
||||||
statusText = t.staff_time_card.status.disputed;
|
statusText = t.staff_time_card.status.disputed;
|
||||||
break;
|
break;
|
||||||
case TimeCardStatus.paid:
|
case TimeCardStatus.paid:
|
||||||
statusBg = UiColors.primary.withOpacity(0.12);
|
statusBg = UiColors.primary.withValues(alpha: 0.12);
|
||||||
statusColor = UiColors.primary;
|
statusColor = UiColors.primary;
|
||||||
statusText = t.staff_time_card.status.paid;
|
statusText = t.staff_time_card.status.paid;
|
||||||
break;
|
break;
|
||||||
case TimeCardStatus.pending:
|
case TimeCardStatus.pending:
|
||||||
statusBg = UiColors.textWarning.withOpacity(0.12);
|
statusBg = UiColors.tagPending;
|
||||||
statusColor = UiColors.textWarning;
|
statusColor = UiColors.textWarning;
|
||||||
statusText = t.staff_time_card.status.pending;
|
statusText = t.staff_time_card.status.pending;
|
||||||
break;
|
break;
|
||||||
@@ -61,15 +61,11 @@ class TimesheetCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
timesheet.shiftTitle,
|
timesheet.shiftTitle,
|
||||||
style: UiTypography.body1m.copyWith(
|
style: UiTypography.body1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
timesheet.clientName,
|
timesheet.clientName,
|
||||||
style: UiTypography.body2r.copyWith(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -116,13 +112,11 @@ class TimesheetCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'${timesheet.totalHours.toStringAsFixed(1)} ${t.staff_time_card.hours} @ \$${timesheet.hourlyRate.toStringAsFixed(2)}${t.staff_time_card.per_hr}',
|
'${timesheet.totalHours.toStringAsFixed(1)} ${t.staff_time_card.hours} @ \$${timesheet.hourlyRate.toStringAsFixed(2)}${t.staff_time_card.per_hr}',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'\$${timesheet.totalPay.toStringAsFixed(2)}',
|
'\$${timesheet.totalPay.toStringAsFixed(2)}',
|
||||||
style: UiTypography.title2b.copyWith(
|
style: UiTypography.title2b.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -163,7 +157,7 @@ class _IconText extends StatelessWidget {
|
|||||||
const SizedBox(width: UiConstants.space1),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -32,9 +32,7 @@ class AttirePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
t.staff_profile_attire.title,
|
t.staff_profile_attire.title,
|
||||||
style: UiTypography.headline3m.copyWith(
|
style: UiTypography.headline3m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ class AttireInfoCard extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.08),
|
color: UiColors.primary.withValues(alpha: 0.08),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -26,16 +26,12 @@ class AttireInfoCard extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
t.staff_profile_attire.info_card.title,
|
t.staff_profile_attire.info_card.title,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 2),
|
const SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
t.staff_profile_attire.info_card.description,
|
t.staff_profile_attire.info_card.description,
|
||||||
style: UiTypography.body2r.copyWith(
|
style: UiTypography.body2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -23,12 +23,12 @@ class EmergencyContactScreen extends StatelessWidget {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
|
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
|
||||||
onPressed: () => Modular.to.pop(),
|
onPressed: () => Modular.to.pop(),
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Emergency Contact',
|
'Emergency Contact',
|
||||||
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
@@ -53,11 +53,11 @@ class EmergencyContactScreen extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: EdgeInsets.all(UiConstants.space6),
|
padding: const EdgeInsets.all(UiConstants.space6),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const EmergencyContactInfoBanner(),
|
const EmergencyContactInfoBanner(),
|
||||||
SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
...state.contacts.asMap().entries.map(
|
...state.contacts.asMap().entries.map(
|
||||||
(entry) => EmergencyContactFormItem(
|
(entry) => EmergencyContactFormItem(
|
||||||
index: entry.key,
|
index: entry.key,
|
||||||
@@ -66,7 +66,7 @@ class EmergencyContactScreen extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const EmergencyContactAddButton(),
|
const EmergencyContactAddButton(),
|
||||||
SizedBox(height: UiConstants.space16),
|
const SizedBox(height: UiConstants.space16),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,20 +12,20 @@ class EmergencyContactAddButton extends StatelessWidget {
|
|||||||
child: TextButton.icon(
|
child: TextButton.icon(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
context.read<EmergencyContactBloc>().add(EmergencyContactAdded()),
|
context.read<EmergencyContactBloc>().add(EmergencyContactAdded()),
|
||||||
icon: Icon(UiIcons.add, size: 20.0),
|
icon: const Icon(UiIcons.add, size: 20.0),
|
||||||
label: Text(
|
label: Text(
|
||||||
'Add Another Contact',
|
'Add Another Contact',
|
||||||
style: UiTypography.title2b,
|
style: UiTypography.title2b,
|
||||||
),
|
),
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: UiColors.primary,
|
foregroundColor: UiColors.primary,
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space6,
|
horizontal: UiConstants.space6,
|
||||||
vertical: UiConstants.space3,
|
vertical: UiConstants.space3,
|
||||||
),
|
),
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: UiConstants.radiusFull,
|
borderRadius: UiConstants.radiusFull,
|
||||||
side: BorderSide(color: UiColors.primary),
|
side: const BorderSide(color: UiColors.primary),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(bottom: UiConstants.space4),
|
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
@@ -30,33 +30,27 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
_buildHeader(context),
|
_buildHeader(context),
|
||||||
SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
_buildLabel('Full Name'),
|
_buildLabel('Full Name'),
|
||||||
_buildTextField(
|
_buildTextField(
|
||||||
initialValue: contact.name,
|
initialValue: contact.name,
|
||||||
hint: 'Contact name',
|
hint: 'Contact name',
|
||||||
icon: UiIcons.user,
|
icon: UiIcons.user,
|
||||||
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
||||||
EmergencyContactUpdated(
|
EmergencyContactUpdated(index, contact.copyWith(name: val)),
|
||||||
index,
|
),
|
||||||
contact.copyWith(name: val),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
_buildLabel('Phone Number'),
|
_buildLabel('Phone Number'),
|
||||||
_buildTextField(
|
_buildTextField(
|
||||||
initialValue: contact.phone,
|
initialValue: contact.phone,
|
||||||
hint: '+1 (555) 000-0000',
|
hint: '+1 (555) 000-0000',
|
||||||
icon: UiIcons.phone,
|
icon: UiIcons.phone,
|
||||||
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
onChanged: (val) => context.read<EmergencyContactBloc>().add(
|
||||||
EmergencyContactUpdated(
|
EmergencyContactUpdated(index, contact.copyWith(phone: val)),
|
||||||
index,
|
),
|
||||||
contact.copyWith(phone: val),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space4),
|
const SizedBox(height: UiConstants.space4),
|
||||||
_buildLabel('Relationship'),
|
_buildLabel('Relationship'),
|
||||||
_buildDropdown(
|
_buildDropdown(
|
||||||
context,
|
context,
|
||||||
@@ -65,11 +59,11 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
onChanged: (val) {
|
onChanged: (val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
context.read<EmergencyContactBloc>().add(
|
context.read<EmergencyContactBloc>().add(
|
||||||
EmergencyContactUpdated(
|
EmergencyContactUpdated(
|
||||||
index,
|
index,
|
||||||
contact.copyWith(relationship: val),
|
contact.copyWith(relationship: val),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -85,7 +79,7 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
required ValueChanged<RelationshipType?> onChanged,
|
required ValueChanged<RelationshipType?> onChanged,
|
||||||
}) {
|
}) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space4,
|
horizontal: UiConstants.space4,
|
||||||
vertical: UiConstants.space2,
|
vertical: UiConstants.space2,
|
||||||
),
|
),
|
||||||
@@ -99,13 +93,13 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
value: value,
|
value: value,
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
dropdownColor: UiColors.bgPopup,
|
dropdownColor: UiColors.bgPopup,
|
||||||
icon: Icon(UiIcons.chevronDown, color: UiColors.iconSecondary),
|
icon: const Icon(UiIcons.chevronDown, color: UiColors.iconSecondary),
|
||||||
items: items.map((type) {
|
items: items.map((type) {
|
||||||
return DropdownMenuItem<RelationshipType>(
|
return DropdownMenuItem<RelationshipType>(
|
||||||
value: type,
|
value: type,
|
||||||
child: Text(
|
child: Text(
|
||||||
_formatRelationship(type),
|
_formatRelationship(type),
|
||||||
style: UiTypography.body1r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body1r.textPrimary,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
@@ -116,11 +110,15 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _formatRelationship(RelationshipType type) {
|
String _formatRelationship(RelationshipType type) {
|
||||||
switch(type) {
|
switch (type) {
|
||||||
case RelationshipType.family: return 'Family';
|
case RelationshipType.family:
|
||||||
case RelationshipType.spouse: return 'Spouse';
|
return 'Family';
|
||||||
case RelationshipType.friend: return 'Friend';
|
case RelationshipType.spouse:
|
||||||
case RelationshipType.other: return 'Other';
|
return 'Spouse';
|
||||||
|
case RelationshipType.friend:
|
||||||
|
return 'Friend';
|
||||||
|
case RelationshipType.other:
|
||||||
|
return 'Other';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,22 +126,17 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text('Contact ${index + 1}', style: UiTypography.title2m.textPrimary),
|
||||||
'Contact ${index + 1}',
|
|
||||||
style: UiTypography.title2m.copyWith(
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (totalContacts > 1)
|
if (totalContacts > 1)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(
|
icon: const Icon(
|
||||||
UiIcons.delete,
|
UiIcons.delete,
|
||||||
color: UiColors.textError,
|
color: UiColors.textError,
|
||||||
size: 20.0,
|
size: 20.0,
|
||||||
),
|
),
|
||||||
onPressed: () => context
|
onPressed: () => context.read<EmergencyContactBloc>().add(
|
||||||
.read<EmergencyContactBloc>()
|
EmergencyContactRemoved(index),
|
||||||
.add(EmergencyContactRemoved(index)),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -151,13 +144,8 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildLabel(String label) {
|
Widget _buildLabel(String label) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(bottom: UiConstants.space2),
|
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
||||||
child: Text(
|
child: Text(label, style: UiTypography.body2m.textSecondary),
|
||||||
label,
|
|
||||||
style: UiTypography.body2m.copyWith(
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,16 +157,16 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
}) {
|
}) {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
initialValue: initialValue,
|
initialValue: initialValue,
|
||||||
style: UiTypography.body1r.copyWith(
|
style: UiTypography.body1r.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
hintStyle: TextStyle(color: UiColors.textPlaceholder),
|
hintStyle: const TextStyle(color: UiColors.textPlaceholder),
|
||||||
prefixIcon: Icon(icon, color: UiColors.textSecondary, size: 20.0),
|
prefixIcon: Icon(icon, color: UiColors.textSecondary, size: 20.0),
|
||||||
filled: true,
|
filled: true,
|
||||||
fillColor: UiColors.bgPopup,
|
fillColor: UiColors.bgPopup,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: UiConstants.space4),
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
border: OutlineInputBorder(
|
border: OutlineInputBorder(
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
borderSide: BorderSide(color: UiColors.border),
|
borderSide: BorderSide(color: UiColors.border),
|
||||||
@@ -196,4 +184,3 @@ class EmergencyContactFormItem extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ class EmergencyContactInfoBanner extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.accent.withOpacity(0.2),
|
color: UiColors.accent.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: UiConstants.radiusLg,
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Please provide at least one emergency contact. This information will only be used in case of an emergency during your shifts.',
|
'Please provide at least one emergency contact. This information will only be used in case of an emergency during your shifts.',
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,19 +30,18 @@ class EmergencyContactSaveButton extends StatelessWidget {
|
|||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final isLoading = state.status == EmergencyContactStatus.saving;
|
final isLoading = state.status == EmergencyContactStatus.saving;
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
),
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: UiButton.primary(
|
child: UiButton.primary(
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
onPressed: state.isValid && !isLoading
|
onPressed:
|
||||||
? () => _onSave(context)
|
state.isValid && !isLoading ? () => _onSave(context) : null,
|
||||||
: null,
|
|
||||||
child: isLoading
|
child: isLoading
|
||||||
? SizedBox(
|
? const SizedBox(
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
width: 20.0,
|
width: 20.0,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
|
|||||||
@@ -73,16 +73,16 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: EdgeInsets.all(UiConstants.space5),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
ExperienceSectionTitle(title: i18n.industries_title),
|
ExperienceSectionTitle(title: i18n.industries_title),
|
||||||
Text(
|
Text(
|
||||||
i18n.industries_subtitle,
|
i18n.industries_subtitle,
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2m.textSecondary,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
@@ -90,23 +90,26 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
.map(
|
.map(
|
||||||
(i) => UiChip(
|
(i) => UiChip(
|
||||||
label: _getIndustryLabel(i18n.industries, i),
|
label: _getIndustryLabel(i18n.industries, i),
|
||||||
isSelected: state.selectedIndustries.contains(i),
|
isSelected:
|
||||||
onTap: () => BlocProvider.of<ExperienceBloc>(context)
|
state.selectedIndustries.contains(i),
|
||||||
.add(ExperienceIndustryToggled(i)),
|
onTap: () =>
|
||||||
variant: state.selectedIndustries.contains(i)
|
BlocProvider.of<ExperienceBloc>(context)
|
||||||
? UiChipVariant.primary
|
.add(ExperienceIndustryToggled(i)),
|
||||||
: UiChipVariant.secondary,
|
variant:
|
||||||
|
state.selectedIndustries.contains(i)
|
||||||
|
? UiChipVariant.primary
|
||||||
|
: UiChipVariant.secondary,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
ExperienceSectionTitle(title: i18n.skills_title),
|
ExperienceSectionTitle(title: i18n.skills_title),
|
||||||
Text(
|
Text(
|
||||||
i18n.skills_subtitle,
|
i18n.skills_subtitle,
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2m.textSecondary,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
@@ -114,19 +117,22 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
.map(
|
.map(
|
||||||
(s) => UiChip(
|
(s) => UiChip(
|
||||||
label: _getSkillLabel(i18n.skills, s),
|
label: _getSkillLabel(i18n.skills, s),
|
||||||
isSelected: state.selectedSkills.contains(s.value),
|
isSelected:
|
||||||
onTap: () => BlocProvider.of<ExperienceBloc>(context)
|
state.selectedSkills.contains(s.value),
|
||||||
.add(ExperienceSkillToggled(s.value)),
|
onTap: () =>
|
||||||
variant: state.selectedSkills.contains(s.value)
|
BlocProvider.of<ExperienceBloc>(context)
|
||||||
? UiChipVariant.primary
|
.add(ExperienceSkillToggled(s.value)),
|
||||||
: UiChipVariant.secondary,
|
variant:
|
||||||
|
state.selectedSkills.contains(s.value)
|
||||||
|
? UiChipVariant.primary
|
||||||
|
: UiChipVariant.secondary,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildSaveButton(context, state, i18n),
|
_buildSaveButton(context, state, i18n),
|
||||||
],
|
],
|
||||||
@@ -148,9 +154,9 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.custom_skills_title,
|
i18n.custom_skills_title,
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2m.textSecondary,
|
||||||
),
|
),
|
||||||
SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Wrap(
|
Wrap(
|
||||||
spacing: UiConstants.space2,
|
spacing: UiConstants.space2,
|
||||||
runSpacing: UiConstants.space2,
|
runSpacing: UiConstants.space2,
|
||||||
@@ -165,10 +171,14 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSaveButton(BuildContext context, ExperienceState state, dynamic i18n) {
|
Widget _buildSaveButton(
|
||||||
|
BuildContext context,
|
||||||
|
ExperienceState state,
|
||||||
|
dynamic i18n,
|
||||||
|
) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: UiColors.bgPopup,
|
color: UiColors.bgPopup,
|
||||||
border: Border(top: BorderSide(color: UiColors.border)),
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
),
|
),
|
||||||
@@ -176,16 +186,21 @@ class ExperiencePage extends StatelessWidget {
|
|||||||
child: UiButton.primary(
|
child: UiButton.primary(
|
||||||
onPressed: state.status == ExperienceStatus.loading
|
onPressed: state.status == ExperienceStatus.loading
|
||||||
? null
|
? null
|
||||||
: () => BlocProvider.of<ExperienceBloc>(context).add(ExperienceSubmitted()),
|
: () => BlocProvider.of<ExperienceBloc>(context)
|
||||||
|
.add(ExperienceSubmitted()),
|
||||||
fullWidth: true,
|
fullWidth: true,
|
||||||
text: state.status == ExperienceStatus.loading ? null : i18n.save_button,
|
text: state.status == ExperienceStatus.loading
|
||||||
|
? null
|
||||||
|
: i18n.save_button,
|
||||||
child: state.status == ExperienceStatus.loading
|
child: state.status == ExperienceStatus.loading
|
||||||
? SizedBox(
|
? const SizedBox(
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
width: 20.0,
|
width: 20.0,
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(UiColors.white), // UiColors.primaryForeground is white mostly
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
UiColors.white,
|
||||||
|
), // UiColors.primaryForeground is white mostly
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -52,13 +51,16 @@ class PersonalInfoPage extends StatelessWidget {
|
|||||||
backgroundColor: UiColors.bgPopup,
|
backgroundColor: UiColors.bgPopup,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: const Icon(UiIcons.chevronLeft, color: UiColors.textSecondary),
|
icon: const Icon(
|
||||||
|
UiIcons.chevronLeft,
|
||||||
|
color: UiColors.textSecondary,
|
||||||
|
),
|
||||||
onPressed: () => Modular.to.pop(),
|
onPressed: () => Modular.to.pop(),
|
||||||
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
tooltip: MaterialLocalizations.of(context).backButtonTooltip,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
i18n.title,
|
i18n.title,
|
||||||
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1.0),
|
preferredSize: const Size.fromHeight(1.0),
|
||||||
@@ -82,9 +84,7 @@ class PersonalInfoPage extends StatelessWidget {
|
|||||||
return Center(
|
return Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
'Failed to load personal information',
|
'Failed to load personal information',
|
||||||
style: UiTypography.body1r.copyWith(
|
style: UiTypography.body1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class _FieldLabel extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Text(
|
return Text(
|
||||||
text,
|
text,
|
||||||
style: UiTypography.body2m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2m.textPrimary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,7 +101,6 @@ class _FieldLabel extends StatelessWidget {
|
|||||||
/// A read-only field widget for displaying non-editable information.
|
/// A read-only field widget for displaying non-editable information.
|
||||||
/// A read-only field widget for displaying non-editable information.
|
/// A read-only field widget for displaying non-editable information.
|
||||||
class _ReadOnlyField extends StatelessWidget {
|
class _ReadOnlyField extends StatelessWidget {
|
||||||
|
|
||||||
const _ReadOnlyField({required this.value});
|
const _ReadOnlyField({required this.value});
|
||||||
final String value;
|
final String value;
|
||||||
|
|
||||||
@@ -120,7 +119,7 @@ class _ReadOnlyField extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -129,7 +128,6 @@ class _ReadOnlyField extends StatelessWidget {
|
|||||||
/// An editable text field widget.
|
/// An editable text field widget.
|
||||||
/// An editable text field widget.
|
/// An editable text field widget.
|
||||||
class _EditableField extends StatelessWidget {
|
class _EditableField extends StatelessWidget {
|
||||||
|
|
||||||
const _EditableField({
|
const _EditableField({
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.hint,
|
required this.hint,
|
||||||
@@ -150,10 +148,10 @@ class _EditableField extends StatelessWidget {
|
|||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
keyboardType: keyboardType,
|
keyboardType: keyboardType,
|
||||||
autofillHints: autofillHints,
|
autofillHints: autofillHints,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.body2r.textPrimary,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
hintStyle: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
hintStyle: UiTypography.body2r.textSecondary,
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
horizontal: UiConstants.space3,
|
horizontal: UiConstants.space3,
|
||||||
vertical: UiConstants.space3,
|
vertical: UiConstants.space3,
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class ProfilePhotoWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TranslationsStaffOnboardingPersonalInfoEn i18n = t.staff.onboarding.personal_info;
|
final TranslationsStaffOnboardingPersonalInfoEn i18n =
|
||||||
|
t.staff.onboarding.personal_info;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@@ -41,7 +42,7 @@ class ProfilePhotoWidget extends StatelessWidget {
|
|||||||
height: 96,
|
height: 96,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
),
|
),
|
||||||
child: photoUrl != null
|
child: photoUrl != null
|
||||||
? ClipOval(
|
? ClipOval(
|
||||||
@@ -53,9 +54,7 @@ class ProfilePhotoWidget extends StatelessWidget {
|
|||||||
: Center(
|
: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
fullName.isNotEmpty ? fullName[0].toUpperCase() : '?',
|
fullName.isNotEmpty ? fullName[0].toUpperCase() : '?',
|
||||||
style: UiTypography.displayL.copyWith(
|
style: UiTypography.displayL.primary,
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -71,7 +70,7 @@ class ProfilePhotoWidget extends StatelessWidget {
|
|||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: <BoxShadow>[
|
boxShadow: <BoxShadow>[
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: UiColors.textPrimary.withOpacity(0.1),
|
color: UiColors.textPrimary.withValues(alpha: 0.1),
|
||||||
blurRadius: UiConstants.space1,
|
blurRadius: UiConstants.space1,
|
||||||
offset: const Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
),
|
),
|
||||||
@@ -92,7 +91,7 @@ class ProfilePhotoWidget extends StatelessWidget {
|
|||||||
const SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Text(
|
Text(
|
||||||
i18n.change_photo_hint,
|
i18n.change_photo_hint,
|
||||||
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
|
style: UiTypography.body2r.textSecondary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -64,10 +64,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
|
|
||||||
Widget _buildStatCard(IconData icon, String value, String label) {
|
Widget _buildStatCard(IconData icon, String value, String label) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF8FAFC),
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -76,21 +76,19 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(icon, size: 20, color: UiColors.iconSecondary),
|
child: Icon(icon, size: 20, color: UiColors.iconSecondary),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
|
style: UiTypography.title1m.textPrimary,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.footnote2r.copyWith(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -99,29 +97,21 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
|
|
||||||
Widget _buildTimeBox(String label, String time) {
|
Widget _buildTimeBox(String label, String time) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF8FAFC),
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: const TextStyle(
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
_formatTime(time),
|
_formatTime(time),
|
||||||
style: UiTypography.display2m.copyWith(
|
style: UiTypography.headline2m.textPrimary,
|
||||||
fontSize: 20,
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -149,7 +139,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(state.message),
|
content: Text(state.message),
|
||||||
backgroundColor: UiColors.tagSuccess,
|
backgroundColor: UiColors.success,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
Modular.to.toShifts(selectedDate: state.shiftDate);
|
Modular.to.toShifts(selectedDate: state.shiftDate);
|
||||||
@@ -158,7 +148,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(state.message),
|
content: Text(state.message),
|
||||||
backgroundColor: const Color(0xFFEF4444),
|
backgroundColor: UiColors.destructive,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -203,7 +193,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -211,16 +201,11 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
"VENDOR",
|
"VENDOR",
|
||||||
style: TextStyle(
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -229,7 +214,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
child: displayShift.logoUrl != null
|
child: displayShift.logoUrl != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(
|
borderRadius: BorderRadius.circular(
|
||||||
6,
|
UiConstants.radiusMdValue,
|
||||||
),
|
),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
displayShift.logoUrl!,
|
displayShift.logoUrl!,
|
||||||
@@ -244,33 +229,26 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
displayShift.clientName,
|
displayShift.clientName,
|
||||||
style: UiTypography.headline5m.copyWith(
|
style: UiTypography.headline5m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Date Section
|
// Date Section
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
"SHIFT DATE",
|
"SHIFT DATE",
|
||||||
style: TextStyle(
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
@@ -278,104 +256,44 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
size: 20,
|
size: 20,
|
||||||
color: UiColors.primary,
|
color: UiColors.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
_formatDate(displayShift.date),
|
_formatDate(displayShift.date),
|
||||||
style: UiTypography.headline5m.copyWith(
|
style: UiTypography.headline5m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Worker Capacity / Open Slots
|
// Worker Capacity / Open Slots
|
||||||
if ((displayShift.requiredSlots ?? 0) > 0)
|
if ((displayShift.requiredSlots ?? 0) > 0)
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF0FDF4), // green-50
|
color: UiColors.success.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
|
||||||
color: const Color(0xFFBBF7D0),
|
|
||||||
), // green-200
|
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.people_alt_outlined,
|
UiIcons.users,
|
||||||
size: 20,
|
size: 16,
|
||||||
color: Color(0xFF15803D),
|
color: UiColors.success,
|
||||||
), // green-700, using Material Icon as generic fallback
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"$openSlots spots remaining",
|
|
||||||
style: UiTypography.body2b.copyWith(
|
|
||||||
color: const Color(0xFF15803D),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"${displayShift.filledSlots ?? 0} filled out of ${displayShift.requiredSlots}",
|
|
||||||
style: UiTypography.body3r.copyWith(
|
|
||||||
color: const Color(0xFF166534),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
SizedBox(
|
const SizedBox(width: UiConstants.space2),
|
||||||
width: 60,
|
Text(
|
||||||
child: LinearProgressIndicator(
|
"$openSlots slots remaining",
|
||||||
value: (displayShift.requiredSlots! > 0)
|
style: UiTypography.footnote1m.textSuccess,
|
||||||
? (displayShift.filledSlots ?? 0) /
|
|
||||||
displayShift.requiredSlots!
|
|
||||||
: 0,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
color: const Color(0xFF15803D),
|
|
||||||
minHeight: 6,
|
|
||||||
borderRadius: BorderRadius.circular(3),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Stats Grid
|
const SizedBox(height: UiConstants.space6),
|
||||||
GridView.count(
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
crossAxisCount: 3,
|
|
||||||
crossAxisSpacing: 12,
|
|
||||||
childAspectRatio: 0.85,
|
|
||||||
children: [
|
|
||||||
_buildStatCard(
|
|
||||||
UiIcons.dollar,
|
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
|
||||||
"Total Pay",
|
|
||||||
),
|
|
||||||
_buildStatCard(
|
|
||||||
UiIcons.dollar,
|
|
||||||
"\$${displayShift.hourlyRate.toInt()}",
|
|
||||||
"Per Hour",
|
|
||||||
),
|
|
||||||
_buildStatCard(
|
|
||||||
UiIcons.clock,
|
|
||||||
"${duration.toInt()}h",
|
|
||||||
"Duration",
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Shift Timing
|
// Time Section
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -384,7 +302,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
displayShift.startTime,
|
displayShift.startTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildTimeBox(
|
child: _buildTimeBox(
|
||||||
"END TIME",
|
"END TIME",
|
||||||
@@ -393,129 +311,142 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Location
|
// Quick Info Grid
|
||||||
Column(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Expanded(
|
||||||
"LOCATION",
|
child: _buildStatCard(
|
||||||
style: TextStyle(
|
UiIcons.dollar,
|
||||||
fontSize: 10,
|
"\$${displayShift.hourlyRate.toStringAsFixed(0)}/hr",
|
||||||
fontWeight: FontWeight.bold,
|
"Base Rate",
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Column(
|
Expanded(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: _buildStatCard(
|
||||||
children: [
|
UiIcons.clock,
|
||||||
Text(
|
"${duration.toInt()} hours",
|
||||||
displayShift.location.isEmpty
|
"Duration",
|
||||||
? "TBD"
|
),
|
||||||
: displayShift.location,
|
),
|
||||||
style: UiTypography.title1m.copyWith(
|
const SizedBox(width: UiConstants.space4),
|
||||||
color: UiColors.textPrimary,
|
Expanded(
|
||||||
),
|
child: _buildStatCard(
|
||||||
),
|
UiIcons.wallet,
|
||||||
Text(
|
"\$${estimatedTotal.toStringAsFixed(0)}",
|
||||||
displayShift.location.isEmpty
|
"Est. Total",
|
||||||
? "TBD"
|
),
|
||||||
: displayShift.locationAddress,
|
|
||||||
style: UiTypography.title1m.copyWith(
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
// Additional Info
|
// Location Section
|
||||||
if (displayShift.description != null) ...[
|
Column(
|
||||||
SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
width: double.infinity,
|
children: [
|
||||||
child: Column(
|
Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
"LOCATION",
|
||||||
children: [
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
const Text(
|
|
||||||
"ADDITIONAL INFO",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
displayShift.description!,
|
|
||||||
style: UiTypography.body2m.copyWith(
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: UiConstants.space3),
|
||||||
],
|
Container(
|
||||||
const SizedBox(height: 20),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
if (displayShift.status != 'confirmed' &&
|
decoration: BoxDecoration(
|
||||||
displayShift.hasApplied != true &&
|
color: UiColors.white,
|
||||||
(displayShift.requiredSlots == null ||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
displayShift.filledSlots == null ||
|
border: Border.all(color: UiColors.border),
|
||||||
displayShift.filledSlots! <
|
|
||||||
displayShift.requiredSlots!))
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () => _declineShift(
|
|
||||||
context,
|
|
||||||
displayShift!.id,
|
|
||||||
),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
|
||||||
side: const BorderSide(
|
|
||||||
color: Color(0xFFEF4444),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 16,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text("Decline"),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
child: Column(
|
||||||
Expanded(
|
children: [
|
||||||
child: ElevatedButton(
|
Row(
|
||||||
onPressed: () => _bookShift(
|
children: [
|
||||||
context,
|
const Icon(
|
||||||
displayShift!,
|
UiIcons.mapPin,
|
||||||
|
color: UiColors.primary,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
displayShift.location,
|
||||||
|
style: UiTypography.body2b.textPrimary,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
displayShift.locationAddress,
|
||||||
|
style: UiTypography.body3r.textSecondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
style: ElevatedButton.styleFrom(
|
const SizedBox(height: UiConstants.space4),
|
||||||
backgroundColor: const Color(
|
const Divider(),
|
||||||
0xFF10B981,
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: () {},
|
||||||
|
icon: const Icon(
|
||||||
|
UiIcons.arrowRight,
|
||||||
|
size: 16,
|
||||||
),
|
),
|
||||||
foregroundColor: Colors.white,
|
label: const Text("Open in Maps"),
|
||||||
padding: const EdgeInsets.symmetric(
|
style: TextButton.styleFrom(
|
||||||
vertical: 16,
|
foregroundColor: UiColors.primary,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Text("Book Shift"),
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
SizedBox(
|
|
||||||
height: MediaQuery.of(context).padding.bottom + 10,
|
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
|
||||||
|
// Description / Instructions
|
||||||
|
if ((displayShift.description ?? '').isNotEmpty) ...[
|
||||||
|
Text(
|
||||||
|
"JOB DESCRIPTION",
|
||||||
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
Text(
|
||||||
|
displayShift.description!,
|
||||||
|
style: UiTypography.body2r.textSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space8),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
// Bottom Action Bar
|
||||||
|
Container(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
UiConstants.space5,
|
||||||
|
UiConstants.space4,
|
||||||
|
UiConstants.space5,
|
||||||
|
MediaQuery.of(context).padding.bottom + UiConstants.space4,
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.white,
|
||||||
|
border: Border(top: BorderSide(color: UiColors.border)),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
|
blurRadius: 10,
|
||||||
|
offset: const Offset(0, -4),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: _buildBottomButton(displayShift, context),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -552,7 +483,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF10B981),
|
foregroundColor: UiColors.success,
|
||||||
),
|
),
|
||||||
child: const Text('Book'),
|
child: const Text('Book'),
|
||||||
),
|
),
|
||||||
@@ -581,7 +512,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
).add(DeclineShiftDetailsEvent(id));
|
).add(DeclineShiftDetailsEvent(id));
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
foregroundColor: UiColors.destructive,
|
||||||
),
|
),
|
||||||
child: const Text('Decline'),
|
child: const Text('Decline'),
|
||||||
),
|
),
|
||||||
@@ -608,29 +539,23 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
width: 36,
|
width: 36,
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
Text(
|
Text(
|
||||||
shift.title,
|
shift.title,
|
||||||
style: UiTypography.body2b.copyWith(
|
style: UiTypography.body2b.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
'${_formatDate(shift.date)} • ${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}',
|
'${_formatDate(shift.date)} • ${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}',
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
if (shift.clientName.isNotEmpty) ...[
|
if (shift.clientName.isNotEmpty) ...[
|
||||||
const SizedBox(height: 6),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
shift.clientName,
|
shift.clientName,
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -647,4 +572,150 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
Navigator.of(context, rootNavigator: true).pop();
|
Navigator.of(context, rootNavigator: true).pop();
|
||||||
_actionDialogOpen = false;
|
_actionDialogOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildBottomButton(Shift shift, BuildContext context) {
|
||||||
|
final String status = shift.status ?? 'open';
|
||||||
|
|
||||||
|
if (status == 'confirmed') {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => _openCancelDialog(context),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.destructive,
|
||||||
|
foregroundColor: UiColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: Text("CANCEL SHIFT", style: UiTypography.body2b.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => Modular.to.toClockIn(),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.success,
|
||||||
|
foregroundColor: UiColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: Text("CLOCK IN", style: UiTypography.body2b.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == 'pending') {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () => BlocProvider.of<ShiftDetailsBloc>(context).add(DeclineShiftDetailsEvent(shift.id)),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: UiColors.destructive,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
side: const BorderSide(color: UiColors.destructive),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text("DECLINE", style: UiTypography.body2b.textError),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => BlocProvider.of<ShiftDetailsBloc>(context).add(BookShiftDetailsEvent(shift.id, roleId: shift.roleId)),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.primary,
|
||||||
|
foregroundColor: UiColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: Text("ACCEPT SHIFT", style: UiTypography.body2b.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == 'open' || status == 'available') {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () => _declineShift(context, shift.id),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: UiColors.textSecondary,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
side: const BorderSide(color: UiColors.border),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text("DECLINE", style: UiTypography.body2b.textSecondary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () => _bookShift(context, shift),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: UiColors.primary,
|
||||||
|
foregroundColor: UiColors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
elevation: 0,
|
||||||
|
),
|
||||||
|
child: Text("APPLY NOW", style: UiTypography.body2b.white),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openCancelDialog(BuildContext context) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: const Text('Cancel Shift'),
|
||||||
|
content: const Text('Are you sure you want to cancel this shift?'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Modular.to.pop(),
|
||||||
|
child: const Text('No'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Modular.to.pop();
|
||||||
|
BlocProvider.of<ShiftDetailsBloc>(context).add(
|
||||||
|
DeclineShiftDetailsEvent(widget.shiftId),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
foregroundColor: UiColors.destructive,
|
||||||
|
),
|
||||||
|
child: const Text('Yes, cancel it'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import '../blocs/shifts/shifts_bloc.dart';
|
|||||||
import '../widgets/tabs/my_shifts_tab.dart';
|
import '../widgets/tabs/my_shifts_tab.dart';
|
||||||
import '../widgets/tabs/find_shifts_tab.dart';
|
import '../widgets/tabs/find_shifts_tab.dart';
|
||||||
import '../widgets/tabs/history_shifts_tab.dart';
|
import '../widgets/tabs/history_shifts_tab.dart';
|
||||||
import '../styles/shifts_styles.dart';
|
|
||||||
|
|
||||||
class ShiftsPage extends StatefulWidget {
|
class ShiftsPage extends StatefulWidget {
|
||||||
final String? initialTab;
|
final String? initialTab;
|
||||||
@@ -107,29 +106,25 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
// Note: Calendar logic moved to MyShiftsTab
|
// Note: Calendar logic moved to MyShiftsTab
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.krowBackground,
|
backgroundColor: UiColors.background,
|
||||||
body: Column(
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
// Header (Blue)
|
// Header (Blue)
|
||||||
Container(
|
Container(
|
||||||
color: AppColors.krowBlue,
|
color: UiColors.primary,
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
20,
|
UiConstants.space5,
|
||||||
MediaQuery.of(context).padding.top + 10,
|
MediaQuery.of(context).padding.top + UiConstants.space2,
|
||||||
20,
|
UiConstants.space5,
|
||||||
20,
|
UiConstants.space5,
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
spacing: 16,
|
spacing: UiConstants.space4,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
Text(
|
||||||
"Shifts",
|
"Shifts",
|
||||||
style: TextStyle(
|
style: UiTypography.display1b.white,
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
|
||||||
// Tabs
|
// Tabs
|
||||||
@@ -143,17 +138,16 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
showCount: myShiftsLoaded,
|
showCount: myShiftsLoaded,
|
||||||
enabled: !blockTabsForFind,
|
enabled: !blockTabsForFind,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildTab(
|
_buildTab(
|
||||||
"find",
|
"find",
|
||||||
"Find Shifts",
|
"Find Shifts",
|
||||||
UiIcons.search,
|
UiIcons.search,
|
||||||
availableJobs
|
availableJobs.length,
|
||||||
.length, // Passed unfiltered count as badge? Or logic inside? Pass availableJobs.
|
|
||||||
showCount: availableLoaded,
|
showCount: availableLoaded,
|
||||||
enabled: baseLoaded,
|
enabled: baseLoaded,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildTab(
|
_buildTab(
|
||||||
"history",
|
"history",
|
||||||
"History",
|
"History",
|
||||||
@@ -245,12 +239,15 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space2,
|
||||||
|
horizontal: UiConstants.space2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isActive
|
color: isActive
|
||||||
? Colors.white
|
? UiColors.white
|
||||||
: Colors.white.withAlpha((0.2 * 255).round()),
|
: UiColors.white.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@@ -260,23 +257,17 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
icon,
|
icon,
|
||||||
size: 14,
|
size: 14,
|
||||||
color: !enabled
|
color: !enabled
|
||||||
? Colors.white.withAlpha((0.5 * 255).round())
|
? UiColors.white.withValues(alpha: 0.5)
|
||||||
: isActive
|
: isActive
|
||||||
? AppColors.krowBlue
|
? UiColors.primary
|
||||||
: Colors.white,
|
: UiColors.white,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: UiConstants.space1),
|
||||||
Flexible(
|
Flexible(
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(
|
style: (isActive ? UiTypography.body3m.copyWith(color: UiColors.primary) : UiTypography.body3m.white).copyWith(
|
||||||
fontSize: 13,
|
color: !enabled ? UiColors.white.withValues(alpha: 0.5) : null,
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: !enabled
|
|
||||||
? Colors.white.withAlpha((0.5 * 255).round())
|
|
||||||
: isActive
|
|
||||||
? AppColors.krowBlue
|
|
||||||
: Colors.white,
|
|
||||||
),
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
@@ -285,23 +276,21 @@ class _ShiftsPageState extends State<ShiftsPage> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 6,
|
horizontal: UiConstants.space1,
|
||||||
vertical: 2,
|
vertical: 2,
|
||||||
),
|
),
|
||||||
constraints: const BoxConstraints(minWidth: 18),
|
constraints: const BoxConstraints(minWidth: 18),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isActive
|
color: isActive
|
||||||
? AppColors.krowBlue.withAlpha((0.1 * 255).round())
|
? UiColors.primary.withValues(alpha: 0.1)
|
||||||
: Colors.white.withAlpha((0.2 * 255).round()),
|
: UiColors.white.withValues(alpha: 0.2),
|
||||||
borderRadius: BorderRadius.circular(999),
|
borderRadius: UiConstants.radiusFull,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
"$count",
|
"$count",
|
||||||
style: TextStyle(
|
style: UiTypography.footnote1b.copyWith(
|
||||||
fontSize: 10,
|
color: isActive ? UiColors.primary : UiColors.white,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: isActive ? AppColors.krowBlue : Colors.white,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -118,14 +118,14 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
Modular.to.pushShiftDetails(widget.shift);
|
Modular.to.pushShiftDetails(widget.shift);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -142,7 +142,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
// Status Badge
|
// Status Badge
|
||||||
if (statusText.isNotEmpty)
|
if (statusText.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 8),
|
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
if (statusIcon != null)
|
if (statusIcon != null)
|
||||||
@@ -173,14 +173,14 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
),
|
),
|
||||||
// Shift Type Badge for available/pending shifts
|
// Shift Type Badge for available/pending shifts
|
||||||
if (status == 'open' || status == 'pending') ...[
|
if (status == 'open' || status == 'pending') ...[
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 6,
|
horizontal: 6,
|
||||||
vertical: 2,
|
vertical: 2,
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.primary.withOpacity(0.1),
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
@@ -205,20 +205,20 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
UiColors.primary.withOpacity(0.09),
|
UiColors.primary.withValues(alpha: 0.09),
|
||||||
UiColors.primary.withOpacity(0.03),
|
UiColors.primary.withValues(alpha: 0.03),
|
||||||
],
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.primary.withOpacity(0.09),
|
color: UiColors.primary.withValues(alpha: 0.09),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: widget.shift.logoUrl != null
|
child: widget.shift.logoUrl != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
widget.shift.logoUrl!,
|
widget.shift.logoUrl!,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
@@ -232,7 +232,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -249,42 +249,34 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.shift.title,
|
widget.shift.title,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
widget.shift.clientName,
|
widget.shift.clientName,
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
"\$${estimatedTotal.toStringAsFixed(0)}",
|
||||||
style: UiTypography.title1m.copyWith(
|
style: UiTypography.title1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\$${widget.shift.hourlyRate.toInt()}/hr · ${duration.toInt()}h",
|
"\$${widget.shift.hourlyRate.toInt()}/hr · ${duration.toInt()}h",
|
||||||
style: UiTypography.footnote2r.copyWith(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
|
||||||
// Date & Time - Multi-Day or Single Day
|
// Date & Time - Multi-Day or Single Day
|
||||||
if (widget.shift.durationDays != null &&
|
if (widget.shift.durationDays != null &&
|
||||||
@@ -332,11 +324,9 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
_formatDate(widget.shift.date),
|
_formatDate(widget.shift.date),
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.clock,
|
UiIcons.clock,
|
||||||
size: 12,
|
size: 12,
|
||||||
@@ -345,9 +335,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
"${_formatTime(widget.shift.startTime)} - ${_formatTime(widget.shift.endTime)}",
|
"${_formatTime(widget.shift.startTime)} - ${_formatTime(widget.shift.endTime)}",
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -368,9 +356,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
|
|||||||
widget.shift.locationAddress.isNotEmpty
|
widget.shift.locationAddress.isNotEmpty
|
||||||
? widget.shift.locationAddress
|
? widget.shift.locationAddress
|
||||||
: widget.shift.location,
|
: widget.shift.location,
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
|
||||||
|
|
||||||
class ShiftAssignmentCard extends StatelessWidget {
|
class ShiftAssignmentCard extends StatelessWidget {
|
||||||
final Shift shift;
|
final Shift shift;
|
||||||
@@ -66,12 +65,12 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: UiConstants.radiusLg,
|
borderRadius: UiConstants.radiusLg,
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: Colors.black.withOpacity(0.05),
|
color: UiColors.black.withValues(alpha: 0.05),
|
||||||
blurRadius: 2,
|
blurRadius: 2,
|
||||||
offset: const Offset(0, 1),
|
offset: const Offset(0, 1),
|
||||||
),
|
),
|
||||||
@@ -81,7 +80,7 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
// Header
|
// Header
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -97,20 +96,20 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
UiColors.primary.withOpacity(0.09),
|
UiColors.primary.withValues(alpha: 0.09),
|
||||||
UiColors.primary.withOpacity(0.03),
|
UiColors.primary.withValues(alpha: 0.03),
|
||||||
],
|
],
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: UiColors.primary.withOpacity(0.09),
|
color: UiColors.primary.withValues(alpha: 0.09),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: shift.logoUrl != null
|
child: shift.logoUrl != null
|
||||||
? ClipRRect(
|
? ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
shift.logoUrl!,
|
shift.logoUrl!,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
@@ -124,7 +123,7 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -140,42 +139,34 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
shift.title,
|
shift.title,
|
||||||
style: UiTypography.body2m.copyWith(
|
style: UiTypography.body2m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
shift.clientName,
|
shift.clientName,
|
||||||
style: UiTypography.body3r.copyWith(
|
style: UiTypography.body3r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"\$${totalPay.toStringAsFixed(0)}",
|
"\$${totalPay.toStringAsFixed(0)}",
|
||||||
style: UiTypography.title1m.copyWith(
|
style: UiTypography.title1m.textPrimary,
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"\$${shift.hourlyRate.toInt()}/hr · ${hours.toInt()}h",
|
"\$${shift.hourlyRate.toInt()}/hr · ${hours.toInt()}h",
|
||||||
style: UiTypography.footnote2r.copyWith(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
|
|
||||||
// Date & Time
|
// Date & Time
|
||||||
Row(
|
Row(
|
||||||
@@ -188,11 +179,9 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
_formatDate(shift.date),
|
_formatDate(shift.date),
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.clock,
|
UiIcons.clock,
|
||||||
size: 12,
|
size: 12,
|
||||||
@@ -201,9 +190,7 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
"${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}",
|
"${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}",
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -223,9 +210,7 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
shift.locationAddress.isNotEmpty
|
shift.locationAddress.isNotEmpty
|
||||||
? shift.locationAddress
|
? shift.locationAddress
|
||||||
: shift.location,
|
: shift.location,
|
||||||
style: UiTypography.footnote1r.copyWith(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
color: UiColors.textSecondary,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -240,38 +225,55 @@ class ShiftAssignmentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Padding(
|
// Actions
|
||||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
Container(
|
||||||
|
padding: const EdgeInsets.all(UiConstants.space2),
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: UiColors.secondary,
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(UiConstants.radiusBase),
|
||||||
|
bottomRight: Radius.circular(UiConstants.radiusBase),
|
||||||
|
),
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: OutlinedButton(
|
child: TextButton(
|
||||||
onPressed: onDecline,
|
onPressed: onDecline,
|
||||||
style: OutlinedButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: UiColors.iconSecondary,
|
foregroundColor: UiColors.destructive,
|
||||||
side: const BorderSide(color: UiColors.border),
|
),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
child: Text(
|
||||||
shape: RoundedRectangleBorder(
|
"Decline", // Fallback if translation is broken
|
||||||
borderRadius: BorderRadius.circular(8),
|
style: UiTypography.body2m.textError,
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Text(t.staff_shifts.action.decline),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
onPressed: onConfirm,
|
onPressed: isConfirming ? null : onConfirm,
|
||||||
style: ElevatedButton.styleFrom(
|
style: ElevatedButton.styleFrom(
|
||||||
backgroundColor: UiColors.primary,
|
backgroundColor: UiColors.primary,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: UiColors.white,
|
||||||
elevation: 0,
|
elevation: 0,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(t.staff_shifts.action.confirm),
|
child: isConfirming
|
||||||
|
? const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
width: 16,
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
strokeWidth: 2,
|
||||||
|
color: UiColors.white,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Text(
|
||||||
|
"Accept", // Fallback
|
||||||
|
style: UiTypography.body2m.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'package:design_system/design_system.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
|
|
||||||
import '../../styles/shifts_styles.dart';
|
|
||||||
import '../my_shift_card.dart';
|
import '../my_shift_card.dart';
|
||||||
import '../shared/empty_state_view.dart';
|
import '../shared/empty_state_view.dart';
|
||||||
|
|
||||||
@@ -27,22 +26,21 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => setState(() => _jobType = id),
|
onTap: () => setState(() => _jobType = id),
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
vertical: UiConstants.space2,
|
||||||
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected ? AppColors.krowBlue : Colors.white,
|
color: isSelected ? UiColors.primary : UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(999),
|
borderRadius: UiConstants.radiusFull,
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected ? AppColors.krowBlue : const Color(0xFFE2E8F0),
|
color: isSelected ? UiColors.primary : UiColors.border,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
label,
|
label,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: (isSelected ? UiTypography.footnote2m.white : UiTypography.footnote2m.textSecondary),
|
||||||
fontSize: 12,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: isSelected ? Colors.white : const Color(0xFF64748B),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -73,8 +71,11 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
children: [
|
children: [
|
||||||
// Search and Filters
|
// Search and Filters
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space5,
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Search Bar
|
// Search Bar
|
||||||
@@ -83,12 +84,12 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 48,
|
height: 48,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xFFF8FAFC),
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: const Color(0xFFE2E8F0),
|
color: UiColors.border,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -96,20 +97,17 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
const Icon(
|
const Icon(
|
||||||
UiIcons.search,
|
UiIcons.search,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: Color(0xFF94A3B8),
|
color: UiColors.textInactive,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
onChanged: (v) =>
|
onChanged: (v) =>
|
||||||
setState(() => _searchQuery = v),
|
setState(() => _searchQuery = v),
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
hintText: "Search jobs, location...",
|
hintText: "Search jobs, location...",
|
||||||
hintStyle: TextStyle(
|
hintStyle: UiTypography.body2r.textPlaceholder,
|
||||||
color: Color(0xFF94A3B8),
|
|
||||||
fontSize: 14,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -117,37 +115,37 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Container(
|
Container(
|
||||||
height: 48,
|
height: 48,
|
||||||
width: 48,
|
width: 48,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: const Color(0xFFE2E8F0),
|
color: UiColors.border,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: const Icon(
|
child: const Icon(
|
||||||
UiIcons.filter,
|
UiIcons.filter,
|
||||||
size: 18,
|
size: 18,
|
||||||
color: Color(0xFF64748B),
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: UiConstants.space4),
|
||||||
// Filter Tabs
|
// Filter Tabs
|
||||||
SingleChildScrollView(
|
SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
_buildFilterTab('all', 'All Jobs'),
|
_buildFilterTab('all', 'All Jobs'),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildFilterTab('one-day', 'One Day'),
|
_buildFilterTab('one-day', 'One Day'),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildFilterTab('multi-day', 'Multi-Day'),
|
_buildFilterTab('multi-day', 'Multi-Day'),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
_buildFilterTab('long-term', 'Long Term'),
|
_buildFilterTab('long-term', 'Long Term'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -158,19 +156,19 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
|
|||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: filteredJobs.isEmpty
|
child: filteredJobs.isEmpty
|
||||||
? EmptyStateView(
|
? const EmptyStateView(
|
||||||
icon: UiIcons.search,
|
icon: UiIcons.search,
|
||||||
title: "No jobs available",
|
title: "No jobs available",
|
||||||
subtitle: "Check back later",
|
subtitle: "Check back later",
|
||||||
)
|
)
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: UiConstants.space5),
|
||||||
...filteredJobs.map(
|
...filteredJobs.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: MyShiftCard(
|
child: MyShiftCard(
|
||||||
shift: shift,
|
shift: shift,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class HistoryShiftsTab extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (historyShifts.isEmpty) {
|
if (historyShifts.isEmpty) {
|
||||||
return EmptyStateView(
|
return const EmptyStateView(
|
||||||
icon: UiIcons.clock,
|
icon: UiIcons.clock,
|
||||||
title: "No shift history",
|
title: "No shift history",
|
||||||
subtitle: "Completed shifts appear here",
|
subtitle: "Completed shifts appear here",
|
||||||
@@ -25,13 +25,13 @@ class HistoryShiftsTab extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: UiConstants.space5),
|
||||||
...historyShifts.map(
|
...historyShifts.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onTap: () => Modular.to.pushShiftDetails(shift),
|
onTap: () => Modular.to.pushShiftDetails(shift),
|
||||||
child: MyShiftCard(
|
child: MyShiftCard(
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:lucide_icons/lucide_icons.dart';
|
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import '../../blocs/shifts/shifts_bloc.dart';
|
import '../../blocs/shifts/shifts_bloc.dart';
|
||||||
import '../my_shift_card.dart';
|
import '../my_shift_card.dart';
|
||||||
import '../shift_assignment_card.dart';
|
import '../shift_assignment_card.dart';
|
||||||
import '../shared/empty_state_view.dart';
|
import '../shared/empty_state_view.dart';
|
||||||
import '../../styles/shifts_styles.dart';
|
|
||||||
|
|
||||||
class MyShiftsTab extends StatefulWidget {
|
class MyShiftsTab extends StatefulWidget {
|
||||||
final List<Shift> myShifts;
|
final List<Shift> myShifts;
|
||||||
@@ -118,14 +116,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
context.read<ShiftsBloc>().add(AcceptShiftEvent(id));
|
context.read<ShiftsBloc>().add(AcceptShiftEvent(id));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Shift confirmed!'),
|
content: const Text('Shift confirmed!'),
|
||||||
backgroundColor: Color(0xFF10B981),
|
backgroundColor: UiColors.success,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF10B981),
|
foregroundColor: UiColors.success,
|
||||||
),
|
),
|
||||||
child: const Text('Accept'),
|
child: const Text('Accept'),
|
||||||
),
|
),
|
||||||
@@ -152,14 +150,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
context.read<ShiftsBloc>().add(DeclineShiftEvent(id));
|
context.read<ShiftsBloc>().add(DeclineShiftEvent(id));
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Shift declined.'),
|
content: const Text('Shift declined.'),
|
||||||
backgroundColor: Color(0xFFEF4444),
|
backgroundColor: UiColors.destructive,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
foregroundColor: UiColors.destructive,
|
||||||
),
|
),
|
||||||
child: const Text('Decline'),
|
child: const Text('Decline'),
|
||||||
),
|
),
|
||||||
@@ -212,12 +210,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
children: [
|
children: [
|
||||||
// Calendar Selector
|
// Calendar Selector
|
||||||
Container(
|
Container(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: UiConstants.space4,
|
||||||
|
horizontal: UiConstants.space4,
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
@@ -225,7 +226,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
UiIcons.chevronLeft,
|
UiIcons.chevronLeft,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: AppColors.krowCharcoal,
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
onPressed: () => setState(() {
|
onPressed: () => setState(() {
|
||||||
_weekOffset--;
|
_weekOffset--;
|
||||||
@@ -237,17 +238,13 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
DateFormat('MMMM yyyy').format(weekStartDate),
|
DateFormat('MMMM yyyy').format(weekStartDate),
|
||||||
style: const TextStyle(
|
style: UiTypography.title1m.textPrimary,
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
UiIcons.chevronRight,
|
UiIcons.chevronRight,
|
||||||
size: 20,
|
size: 20,
|
||||||
color: AppColors.krowCharcoal,
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
onPressed: () => setState(() {
|
onPressed: () => setState(() {
|
||||||
_weekOffset++;
|
_weekOffset++;
|
||||||
@@ -284,13 +281,13 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
height: 60,
|
height: 60,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? AppColors.krowBlue
|
? UiColors.primary
|
||||||
: Colors.white,
|
: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? AppColors.krowBlue
|
? UiColors.primary
|
||||||
: AppColors.krowBorder,
|
: UiColors.border,
|
||||||
width: 1,
|
width: 1,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -299,31 +296,25 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
date.day.toString().padLeft(2, '0'),
|
date.day.toString().padLeft(2, '0'),
|
||||||
style: TextStyle(
|
style: isSelected
|
||||||
fontSize: 18,
|
? UiTypography.body1b.white
|
||||||
fontWeight: FontWeight.bold,
|
: UiTypography.body1b.textPrimary,
|
||||||
color: isSelected
|
|
||||||
? Colors.white
|
|
||||||
: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
DateFormat('E').format(date),
|
DateFormat('E').format(date),
|
||||||
style: TextStyle(
|
style: (isSelected
|
||||||
fontSize: 10,
|
? UiTypography.footnote2m.white
|
||||||
fontWeight: FontWeight.w500,
|
: UiTypography.footnote2m.textSecondary).copyWith(
|
||||||
color: isSelected
|
color: isSelected ? UiColors.white.withValues(alpha: 0.8) : null,
|
||||||
? Colors.white.withOpacity(0.8)
|
|
||||||
: AppColors.krowMuted,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (hasShifts && !isSelected)
|
if (hasShifts && !isSelected)
|
||||||
Container(
|
Container(
|
||||||
margin: const EdgeInsets.only(top: 4),
|
margin: const EdgeInsets.only(top: UiConstants.space1),
|
||||||
width: 4,
|
width: 4,
|
||||||
height: 4,
|
height: 4,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: AppColors.krowBlue,
|
color: UiColors.primary,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -338,22 +329,22 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Divider(height: 1, color: AppColors.krowBorder),
|
const Divider(height: 1, color: UiColors.border),
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: UiConstants.space5),
|
||||||
if (widget.pendingAssignments.isNotEmpty) ...[
|
if (widget.pendingAssignments.isNotEmpty) ...[
|
||||||
_buildSectionHeader(
|
_buildSectionHeader(
|
||||||
"Awaiting Confirmation",
|
"Awaiting Confirmation",
|
||||||
const Color(0xFFF59E0B),
|
UiColors.textWarning,
|
||||||
),
|
),
|
||||||
...widget.pendingAssignments.map(
|
...widget.pendingAssignments.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: ShiftAssignmentCard(
|
child: ShiftAssignmentCard(
|
||||||
shift: shift,
|
shift: shift,
|
||||||
onConfirm: () => _confirmShift(shift.id),
|
onConfirm: () => _confirmShift(shift.id),
|
||||||
@@ -362,14 +353,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
],
|
],
|
||||||
|
|
||||||
if (visibleCancelledShifts.isNotEmpty) ...[
|
if (visibleCancelledShifts.isNotEmpty) ...[
|
||||||
_buildSectionHeader("Cancelled Shifts", AppColors.krowMuted),
|
_buildSectionHeader("Cancelled Shifts", UiColors.textSecondary),
|
||||||
...visibleCancelledShifts.map(
|
...visibleCancelledShifts.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: _buildCancelledCard(
|
child: _buildCancelledCard(
|
||||||
title: shift.title,
|
title: shift.title,
|
||||||
client: shift.clientName,
|
client: shift.clientName,
|
||||||
@@ -383,15 +374,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Confirmed Shifts
|
// Confirmed Shifts
|
||||||
if (visibleMyShifts.isNotEmpty) ...[
|
if (visibleMyShifts.isNotEmpty) ...[
|
||||||
_buildSectionHeader("Confirmed Shifts", AppColors.krowMuted),
|
_buildSectionHeader("Confirmed Shifts", UiColors.textSecondary),
|
||||||
...visibleMyShifts.map(
|
...visibleMyShifts.map(
|
||||||
(shift) => Padding(
|
(shift) => Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 12),
|
padding: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
child: MyShiftCard(shift: shift),
|
child: MyShiftCard(shift: shift),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -417,7 +408,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
|
|
||||||
Widget _buildSectionHeader(String title, Color dotColor) {
|
Widget _buildSectionHeader(String title, Color dotColor) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
@@ -425,16 +416,12 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
height: 8,
|
height: 8,
|
||||||
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
|
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style: (dotColor == UiColors.textSecondary
|
||||||
fontSize: 14,
|
? UiTypography.body2b.textSecondary
|
||||||
fontWeight: FontWeight.w600,
|
: UiTypography.body2b.copyWith(color: dotColor)),
|
||||||
color: dotColor == AppColors.krowMuted
|
|
||||||
? AppColors.krowMuted
|
|
||||||
: dotColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -455,11 +442,11 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: UiColors.white,
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase + 4),
|
||||||
border: Border.all(color: AppColors.krowBorder),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -470,33 +457,25 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
width: 6,
|
width: 6,
|
||||||
height: 6,
|
height: 6,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Color(0xFFEF4444),
|
color: UiColors.destructive,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
const Text(
|
Text(
|
||||||
"CANCELLED",
|
"CANCELLED",
|
||||||
style: TextStyle(
|
style: UiTypography.footnote2b.textError,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Color(0xFFEF4444),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
if (isLastMinute) ...[
|
if (isLastMinute) ...[
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
const Text(
|
Text(
|
||||||
"• 4hr compensation",
|
"• 4hr compensation",
|
||||||
style: TextStyle(
|
style: UiTypography.footnote2m.textSuccess,
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Color(0xFF10B981),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -504,18 +483,18 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
width: 44,
|
width: 44,
|
||||||
height: 44,
|
height: 44,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: AppColors.krowBlue.withOpacity(0.05),
|
color: UiColors.primary.withValues(alpha: 0.05),
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
),
|
),
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: Icon(
|
child: Icon(
|
||||||
LucideIcons.briefcase,
|
UiIcons.briefcase,
|
||||||
color: AppColors.krowBlue,
|
color: UiColors.primary,
|
||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -529,18 +508,11 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: const TextStyle(
|
style: UiTypography.body2b.textPrimary,
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
client,
|
client,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -550,52 +522,39 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
pay,
|
pay,
|
||||||
style: const TextStyle(
|
style: UiTypography.headline4m.textPrimary,
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: AppColors.krowCharcoal,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
rate,
|
rate,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
fontSize: 10,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.calendar,
|
UiIcons.calendar,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: AppColors.krowMuted,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
date,
|
date,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: UiConstants.space3),
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.clock,
|
UiIcons.clock,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: AppColors.krowMuted,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
time,
|
time,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -603,18 +562,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
LucideIcons.mapPin,
|
UiIcons.mapPin,
|
||||||
size: 12,
|
size: 12,
|
||||||
color: AppColors.krowMuted,
|
color: UiColors.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
address,
|
address,
|
||||||
style: const TextStyle(
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
fontSize: 12,
|
|
||||||
color: AppColors.krowMuted,
|
|
||||||
),
|
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user