Refactor UI components to utilize design system colors and typography

- Removed deprecated app_colors.dart file.
- Updated AttendanceCard widget to use design system colors and typography.
- Refactored CommuteTracker widget for consistent styling with design system.
- Modified DateSelector widget to apply design system styles.
- Adjusted LocationMapPlaceholder widget to align with design system.
- Enhanced LunchBreakDialog with design system colors and typography.
- Updated SwipeToCheckIn widget to utilize design system for colors and text styles.
This commit is contained in:
Achintha Isuru
2026-02-10 16:05:05 -05:00
parent 8edfc72370
commit c6448ad5c1
8 changed files with 309 additions and 440 deletions

View File

@@ -9,7 +9,6 @@ import 'package:lucide_icons/lucide_icons.dart';
import '../bloc/clock_in_bloc.dart';
import '../bloc/clock_in_event.dart';
import '../bloc/clock_in_state.dart';
import '../theme/app_colors.dart';
import '../widgets/commute_tracker.dart';
import '../widgets/date_selector.dart';
import '../widgets/lunch_break_modal.dart';
@@ -39,9 +38,11 @@ class _ClockInPageState extends State<ClockInPage> {
listener: (BuildContext context, ClockInState state) {
if (state.status == ClockInStatus.failure &&
state.errorMessage != null) {
ScaffoldMessenger.of(
UiSnackbar.show(
context,
).showSnackBar(SnackBar(content: Text(state.errorMessage!)));
message: state.errorMessage!,
type: UiSnackbarType.error,
);
}
},
builder: (BuildContext context, ClockInState state) {
@@ -110,14 +111,10 @@ class _ClockInPageState extends State<ClockInPage> {
const SizedBox(height: 20),
// Your Activity Header
const Text(
Text(
"Your Activity",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.krowCharcoal,
),
style: UiTypography.headline4m,
),
const SizedBox(height: 16),
@@ -135,15 +132,15 @@ class _ClockInPageState extends State<ClockInPage> {
margin:
const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: BorderRadius.circular(
12,
),
border: Border.all(
color: shift.id ==
selectedShift?.id
? AppColors.krowBlue
: const Color(0xFFE2E8F0),
? UiColors.primary
: UiColors.border,
width:
shift.id == selectedShift?.id
? 2
@@ -164,34 +161,25 @@ class _ClockInPageState extends State<ClockInPage> {
selectedShift?.id
? "SELECTED SHIFT"
: "TODAY'S SHIFT",
style: TextStyle(
fontSize: 10,
fontWeight:
FontWeight.w600,
style: UiTypography
.titleUppercase4b
.copyWith(
color: shift.id ==
selectedShift?.id
? AppColors.krowBlue
: AppColors
.krowCharcoal,
letterSpacing: 0.5,
? UiColors.primary
: UiColors
.textSecondary,
),
),
const SizedBox(height: 2),
Text(
shift.title,
style: const TextStyle(
fontSize: 14,
fontWeight:
FontWeight.w600,
color: Color(0xFF1E293B),
),
style: UiTypography.body2b,
),
Text(
"${shift.clientName}${shift.location}",
style: const TextStyle(
fontSize: 12,
color: Color(0xFF64748B),
),
style: UiTypography.body3r
.textSecondary,
),
],
),
@@ -202,18 +190,14 @@ class _ClockInPageState extends State<ClockInPage> {
children: <Widget>[
Text(
"${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF475569),
),
style: UiTypography.body3m
.textSecondary,
),
Text(
"\$${shift.hourlyRate}/hr",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.krowBlue,
style: UiTypography.body3m
.copyWith(
color: UiColors.primary,
),
),
],
@@ -234,7 +218,7 @@ class _ClockInPageState extends State<ClockInPage> {
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // slate-100
color: UiColors.bgSecondary,
borderRadius: BorderRadius.circular(16),
),
child: Column(
@@ -242,24 +226,17 @@ class _ClockInPageState extends State<ClockInPage> {
const Icon(
LucideIcons.clock,
size: 48,
color: Color(0xFF94A3B8), // slate-400
color: UiColors.iconThird,
),
const SizedBox(height: 16),
const Text(
Text(
"You're early!",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF475569), // slate-600
),
style: UiTypography.body1m.textSecondary,
),
const SizedBox(height: 4),
Text(
"Check-in available at ${_getCheckInAvailabilityTime(selectedShift)}",
style: const TextStyle(
fontSize: 14,
color: Color(0xFF64748B), // slate-500
),
style: UiTypography.body2r.textSecondary,
textAlign: TextAlign.center,
),
],
@@ -304,11 +281,11 @@ class _ClockInPageState extends State<ClockInPage> {
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: const Color(0xFFECFDF5), // emerald-50
color: UiColors.tagSuccess,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: const Color(0xFFA7F3D0),
), // emerald-200
color: UiColors.success.withValues(alpha: 0.3),
),
),
child: Column(
children: <Widget>[
@@ -316,31 +293,24 @@ class _ClockInPageState extends State<ClockInPage> {
width: 48,
height: 48,
decoration: const BoxDecoration(
color: Color(0xFFD1FAE5), // emerald-100
color: UiColors.tagActive,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.check,
color: Color(0xFF059669), // emerald-600
color: UiColors.textSuccess,
size: 24,
),
),
const SizedBox(height: 12),
const Text(
Text(
"Shift Completed!",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF065F46), // emerald-800
),
style: UiTypography.body1b.textSuccess,
),
const SizedBox(height: 4),
const Text(
Text(
"Great work today",
style: TextStyle(
fontSize: 14,
color: Color(0xFF059669), // emerald-600
),
style: UiTypography.body2r.textSuccess,
),
],
),
@@ -351,27 +321,20 @@ class _ClockInPageState extends State<ClockInPage> {
width: double.infinity,
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // slate-100
color: UiColors.bgSecondary,
borderRadius: BorderRadius.circular(16),
),
child: const Column(
child: Column(
children: <Widget>[
Text(
"No confirmed shifts for today",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Color(0xFF475569), // slate-600
),
style: UiTypography.body1m.textSecondary,
textAlign: TextAlign.center,
),
SizedBox(height: 4),
const SizedBox(height: 4),
Text(
"Accept a shift to clock in",
style: TextStyle(
fontSize: 14,
color: Color(0xFF64748B), // slate-500
),
style: UiTypography.body2r.textSecondary,
textAlign: TextAlign.center,
),
],
@@ -385,11 +348,11 @@ class _ClockInPageState extends State<ClockInPage> {
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: const Color(0xFFECFDF5), // emerald-50
color: UiColors.tagSuccess,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFFA7F3D0),
), // emerald-200
color: UiColors.success.withValues(alpha: 0.3),
),
),
child: Row(
mainAxisAlignment:
@@ -399,23 +362,15 @@ class _ClockInPageState extends State<ClockInPage> {
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
const Text(
Text(
"Checked in at",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF059669),
),
style: UiTypography.body3m.textSuccess,
),
Text(
DateFormat(
'h:mm a',
).format(checkInTime),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF065F46),
),
style: UiTypography.body1b.textSuccess,
),
],
),
@@ -423,12 +378,12 @@ class _ClockInPageState extends State<ClockInPage> {
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Color(0xFFD1FAE5),
color: UiColors.tagActive,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.check,
color: Color(0xFF059669),
color: UiColors.textSuccess,
),
),
],
@@ -466,12 +421,12 @@ class _ClockInPageState extends State<ClockInPage> {
child: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: isSelected ? Colors.white : Colors.transparent,
color: isSelected ? UiColors.white : UiColors.transparent,
borderRadius: BorderRadius.circular(8),
boxShadow: isSelected
? <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -484,15 +439,15 @@ class _ClockInPageState extends State<ClockInPage> {
Icon(
icon,
size: 16,
color: isSelected ? Colors.black : Colors.grey,
color: isSelected ? UiColors.foreground : UiColors.iconThird,
),
const SizedBox(width: 6),
Text(
label,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: isSelected ? Colors.black : Colors.grey,
style: UiTypography.body2m.copyWith(
color: isSelected
? UiColors.foreground
: UiColors.textSecondary,
),
),
],
@@ -521,26 +476,19 @@ class _ClockInPageState extends State<ClockInPage> {
width: 96,
height: 96,
decoration: BoxDecoration(
color: scanned
? Colors.green.shade50
: Colors.blue.shade50,
color: scanned ? UiColors.tagSuccess : UiColors.tagInProgress,
shape: BoxShape.circle,
),
child: Icon(
scanned ? LucideIcons.check : LucideIcons.nfc,
size: 48,
color: scanned
? Colors.green.shade600
: Colors.blue.shade600,
color: scanned ? UiColors.textSuccess : UiColors.primary,
),
),
const SizedBox(height: 24),
Text(
scanned ? 'Processing check-in...' : 'Ready to scan',
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
style: UiTypography.headline4m,
),
const SizedBox(height: 8),
Text(
@@ -548,7 +496,7 @@ class _ClockInPageState extends State<ClockInPage> {
? 'Please wait...'
: 'Hold your phone near the NFC tag at the clock-in station',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14, color: Colors.grey.shade600),
style: UiTypography.body2r.textSecondary,
),
if (!scanned) ...<Widget>[
const SizedBox(height: 24),
@@ -573,16 +521,13 @@ class _ClockInPageState extends State<ClockInPage> {
// It's safer to just return a result
},
icon: const Icon(LucideIcons.nfc, size: 24),
label: const Text(
label: Text(
'Tap to Scan',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
style: UiTypography.headline4m.white,
),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0047FF),
foregroundColor: Colors.white,
backgroundColor: UiColors.primary,
foregroundColor: UiColors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),

View File

@@ -1,13 +0,0 @@
import 'package:flutter/material.dart';
class AppColors {
static const Color krowBlue = Color(0xFF0A39DF);
static const Color krowYellow = Color(0xFFFFED4A);
static const Color krowCharcoal = Color(0xFF121826);
static const Color krowMuted = Color(0xFF6A7382);
static const Color krowBorder = Color(0xFFE3E6E9);
static const Color krowBackground = Color(0xFFFAFBFC);
static const Color white = Colors.white;
static const Color black = Colors.black;
}

View File

@@ -1,10 +1,10 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
enum AttendanceType { checkin, checkout, breaks, days }
class AttendanceCard extends StatelessWidget {
const AttendanceCard({
super.key,
required this.type,
@@ -26,12 +26,12 @@ class AttendanceCard extends StatelessWidget {
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: Colors.grey.shade100),
border: Border.all(color: UiColors.bgSecondary),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -53,10 +53,7 @@ class AttendanceCard extends StatelessWidget {
const SizedBox(height: 8),
Text(
title,
style: const TextStyle(
fontSize: 11,
color: Color(0xFF64748B), // slate-500
),
style: UiTypography.titleUppercase4m.textSecondary,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
@@ -65,27 +62,20 @@ class AttendanceCard extends StatelessWidget {
fit: BoxFit.scaleDown,
child: Text(
value,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF0F172A), // slate-900
),
style: UiTypography.headline4m,
),
),
if (scheduledTime != null) ...<Widget>[
const SizedBox(height: 2),
Text(
"Scheduled: $scheduledTime",
style: const TextStyle(
fontSize: 10,
color: Color(0xFF94A3B8), // slate-400
),
style: UiTypography.footnote2r.textInactive,
),
],
const SizedBox(height: 2),
Text(
subtitle,
style: const TextStyle(fontSize: 12, color: Color(0xFF0032A0)),
style: UiTypography.footnote1r.copyWith(color: UiColors.primary),
),
],
),
@@ -97,26 +87,26 @@ class AttendanceCard extends StatelessWidget {
case AttendanceType.checkin:
return _AttendanceStyle(
icon: LucideIcons.logIn,
bgColor: const Color(0xFF0032A0).withOpacity(0.1),
iconColor: const Color(0xFF0032A0),
bgColor: UiColors.primary.withValues(alpha: 0.1),
iconColor: UiColors.primary,
);
case AttendanceType.checkout:
return _AttendanceStyle(
icon: LucideIcons.logOut,
bgColor: const Color(0xFF333F48).withOpacity(0.1),
iconColor: const Color(0xFF333F48),
bgColor: UiColors.foreground.withValues(alpha: 0.1),
iconColor: UiColors.foreground,
);
case AttendanceType.breaks:
return _AttendanceStyle(
icon: LucideIcons.coffee,
bgColor: const Color(0xFFF9E547).withOpacity(0.2),
iconColor: const Color(0xFF4C460D),
bgColor: UiColors.accent.withValues(alpha: 0.2),
iconColor: UiColors.accentForeground,
);
case AttendanceType.days:
return _AttendanceStyle(
icon: LucideIcons.calendar,
bgColor: Colors.green.withOpacity(0.1),
iconColor: Colors.green,
bgColor: UiColors.success.withValues(alpha: 0.1),
iconColor: UiColors.textSuccess,
);
}
}

View File

@@ -1,7 +1,7 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
import '../theme/app_colors.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:lucide_icons/lucide_icons.dart';
enum CommuteMode {
lockedNoShift,
@@ -161,18 +161,18 @@ class _CommuteTrackerState extends State<CommuteTracker> {
margin: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Color(0xFFEFF6FF), // blue-50
Color(0xFFECFEFF), // cyan-50
UiColors.primary.withValues(alpha: 0.05),
UiColors.primary.withValues(alpha: 0.1),
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -188,35 +188,28 @@ class _CommuteTrackerState extends State<CommuteTracker> {
width: 32,
height: 32,
decoration: const BoxDecoration(
color: Color(0xFF2563EB), // blue-600
color: UiColors.primary,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.mapPin,
size: 16,
color: Colors.white,
color: UiColors.white,
),
),
const SizedBox(width: 12),
const Expanded(
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Enable Commute Tracking?',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF0F172A), // slate-900
),
style: UiTypography.body2m.textPrimary,
),
SizedBox(height: 4),
const SizedBox(height: 4),
Text(
'Share location 1hr before shift so your manager can see you\'re on the way.',
style: TextStyle(
fontSize: 12,
color: Color(0xFF475569), // slate-600
),
style: UiTypography.body4r.textSecondary,
),
],
),
@@ -233,9 +226,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
},
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 8),
side: const BorderSide(color: Color(0xFFE2E8F0)),
side: const BorderSide(color: UiColors.border),
),
child: const Text('Not Now', style: TextStyle(fontSize: 12)),
child: Text('Not Now', style: UiTypography.footnote1m),
),
),
const SizedBox(width: 8),
@@ -245,12 +238,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
setState(() => _localHasConsent = true);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2563EB), // blue-600
backgroundColor: UiColors.primary,
padding: const EdgeInsets.symmetric(vertical: 8),
),
child: const Text(
child: Text(
'Enable',
style: TextStyle(fontSize: 12, color: Colors.white),
style: UiTypography.footnote1m.white,
),
),
),
@@ -266,11 +259,11 @@ class _CommuteTrackerState extends State<CommuteTracker> {
margin: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -282,13 +275,13 @@ class _CommuteTrackerState extends State<CommuteTracker> {
width: 32,
height: 32,
decoration: const BoxDecoration(
color: Color(0xFFF1F5F9), // slate-100
color: UiColors.bgSecondary,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.navigation,
size: 16,
color: Color(0xFF475569), // slate-600
color: UiColors.textSecondary,
),
),
const SizedBox(width: 8),
@@ -298,13 +291,9 @@ class _CommuteTrackerState extends State<CommuteTracker> {
children: <Widget>[
Row(
children: <Widget>[
const Text(
Text(
'On My Way',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF0F172A), // slate-900
),
style: UiTypography.body2m.textPrimary,
),
const SizedBox(width: 8),
Row(
@@ -312,26 +301,20 @@ class _CommuteTrackerState extends State<CommuteTracker> {
const Icon(
LucideIcons.clock,
size: 12,
color: Color(0xFF64748B), // slate-500
color: UiColors.textInactive,
),
const SizedBox(width: 2),
Text(
'Shift starts in ${_getMinutesUntilShift()} min',
style: const TextStyle(
fontSize: 11,
color: Color(0xFF64748B), // slate-500
),
style: UiTypography.titleUppercase4m.textSecondary,
),
],
),
],
),
const Text(
Text(
'Track arrival',
style: TextStyle(
fontSize: 10,
color: Color(0xFF64748B), // slate-500
),
style: UiTypography.titleUppercase4m.textSecondary,
),
],
),
@@ -342,7 +325,7 @@ class _CommuteTrackerState extends State<CommuteTracker> {
setState(() => _localIsCommuteOn = value);
widget.onCommuteToggled?.call(value);
},
activeThumbColor: AppColors.krowBlue,
activeThumbColor: UiColors.primary,
),
],
),
@@ -357,8 +340,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Color(0xFF2563EB), // blue-600
Color(0xFF0891B2), // cyan-600
UiColors.primary,
UiColors.iconActive,
],
),
),
@@ -383,13 +366,13 @@ class _CommuteTrackerState extends State<CommuteTracker> {
width: 96,
height: 96,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
color: UiColors.white.withValues(alpha: 0.2),
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.navigation,
size: 48,
color: Colors.white,
color: UiColors.white,
),
),
);
@@ -400,20 +383,15 @@ class _CommuteTrackerState extends State<CommuteTracker> {
},
),
const SizedBox(height: 24),
const Text(
Text(
'On My Way',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
color: Colors.white,
),
style: UiTypography.displayMb.white,
),
const SizedBox(height: 8),
Text(
'Your manager can see you\'re heading to the site',
style: TextStyle(
fontSize: 14,
color: Colors.blue.shade100,
style: UiTypography.body2r.copyWith(
color: UiColors.primaryForeground.withValues(alpha: 0.8),
),
textAlign: TextAlign.center,
),
@@ -424,29 +402,24 @@ class _CommuteTrackerState extends State<CommuteTracker> {
constraints: const BoxConstraints(maxWidth: 300),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
color: UiColors.white.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.white.withOpacity(0.2),
color: UiColors.white.withValues(alpha: 0.2),
),
),
child: Column(
children: <Widget>[
Text(
'Distance to Site',
style: TextStyle(
fontSize: 14,
color: Colors.blue.shade100,
style: UiTypography.body2r.copyWith(
color: UiColors.primaryForeground.withValues(alpha: 0.8),
),
),
const SizedBox(height: 4),
Text(
_formatDistance(widget.distanceMeters!),
style: const TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Colors.white,
),
style: UiTypography.displayM.white,
),
],
),
@@ -458,29 +431,24 @@ class _CommuteTrackerState extends State<CommuteTracker> {
constraints: const BoxConstraints(maxWidth: 300),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.1),
color: UiColors.white.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Colors.white.withOpacity(0.2),
color: UiColors.white.withValues(alpha: 0.2),
),
),
child: Column(
children: <Widget>[
Text(
'Estimated Arrival',
style: TextStyle(
fontSize: 14,
color: Colors.blue.shade100,
style: UiTypography.body2r.copyWith(
color: UiColors.primaryForeground.withValues(alpha: 0.8),
),
),
const SizedBox(height: 4),
Text(
'${widget.etaMinutes} min',
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
style: UiTypography.headline1m.white,
),
],
),
@@ -490,9 +458,8 @@ class _CommuteTrackerState extends State<CommuteTracker> {
const SizedBox(height: 32),
Text(
'Most app features are locked while commute mode is on. You\'ll be able to clock in once you arrive.',
style: TextStyle(
fontSize: 12,
color: Colors.blue.shade100,
style: UiTypography.footnote1r.copyWith(
color: UiColors.primaryForeground.withValues(alpha: 0.8),
),
textAlign: TextAlign.center,
),
@@ -508,12 +475,12 @@ class _CommuteTrackerState extends State<CommuteTracker> {
setState(() => _localIsCommuteOn = false);
},
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white,
side: BorderSide(color: Colors.white.withOpacity(0.3)),
foregroundColor: UiColors.white,
side: BorderSide(color: UiColors.white.withValues(alpha: 0.3)),
padding: const EdgeInsets.symmetric(vertical: 16),
minimumSize: const Size(double.infinity, 48),
),
child: const Text('Turn Off Commute Mode'),
child: Text('Turn Off Commute Mode', style: UiTypography.buttonL),
),
),
],
@@ -527,18 +494,18 @@ class _CommuteTrackerState extends State<CommuteTracker> {
margin: const EdgeInsets.only(bottom: 20),
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: <Color>[
Color(0xFFECFDF5), // emerald-50
Color(0xFFD1FAE5), // green-50
UiColors.tagSuccess,
UiColors.tagActive,
],
),
borderRadius: BorderRadius.circular(12),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: UiColors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
@@ -550,31 +517,24 @@ class _CommuteTrackerState extends State<CommuteTracker> {
width: 64,
height: 64,
decoration: const BoxDecoration(
color: Color(0xFF10B981), // emerald-500
color: UiColors.success,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.checkCircle,
size: 32,
color: Colors.white,
color: UiColors.white,
),
),
const SizedBox(height: 16),
const Text(
Text(
'You\'ve Arrived! 🎉',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF0F172A), // slate-900
),
style: UiTypography.headline3m.textPrimary,
),
const SizedBox(height: 8),
const Text(
Text(
'You\'re at the shift location. Ready to clock in?',
style: TextStyle(
fontSize: 14,
color: Color(0xFF475569), // slate-600
),
style: UiTypography.body2r.textSecondary,
textAlign: TextAlign.center,
),
],

View File

@@ -1,8 +1,8 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
class DateSelector extends StatelessWidget {
const DateSelector({
super.key,
required this.selectedDate,
@@ -36,12 +36,12 @@ class DateSelector extends StatelessWidget {
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.symmetric(horizontal: 4),
decoration: BoxDecoration(
color: isSelected ? const Color(0xFF0032A0) : Colors.white,
color: isSelected ? UiColors.primary : UiColors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: isSelected
? <BoxShadow>[
BoxShadow(
color: const Color(0xFF0032A0).withOpacity(0.3),
color: UiColors.primary.withValues(alpha: 0.3),
blurRadius: 10,
offset: const Offset(0, 4),
),
@@ -53,22 +53,18 @@ class DateSelector extends StatelessWidget {
children: <Widget>[
Text(
DateFormat('d').format(date),
style: TextStyle(
fontSize: 18,
style: UiTypography.title1m.copyWith(
fontWeight: FontWeight.bold,
color: isSelected
? Colors.white
: const Color(0xFF0F172A),
color: isSelected ? UiColors.white : UiColors.foreground,
),
),
const SizedBox(height: 2),
Text(
DateFormat('E').format(date),
style: TextStyle(
fontSize: 12,
style: UiTypography.footnote2r.copyWith(
color: isSelected
? Colors.white.withOpacity(0.8)
: const Color(0xFF94A3B8),
? UiColors.white.withValues(alpha: 0.8)
: UiColors.textInactive,
),
),
const SizedBox(height: 4),
@@ -77,9 +73,7 @@ class DateSelector extends StatelessWidget {
width: 6,
height: 6,
decoration: BoxDecoration(
color: isSelected
? Colors.white
: const Color(0xFF0032A0),
color: isSelected ? UiColors.white : UiColors.primary,
shape: BoxShape.circle,
),
)
@@ -87,8 +81,8 @@ class DateSelector extends StatelessWidget {
Container(
width: 6,
height: 6,
decoration: BoxDecoration(
color: Colors.grey.shade300,
decoration: const BoxDecoration(
color: UiColors.border,
shape: BoxShape.circle,
),
)

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class LocationMapPlaceholder extends StatelessWidget {
@@ -18,7 +18,7 @@ class LocationMapPlaceholder extends StatelessWidget {
height: 200,
width: double.infinity,
decoration: BoxDecoration(
color: const Color(0xFFE2E8F0),
color: UiColors.border,
borderRadius: BorderRadius.circular(16),
image: DecorationImage(
image: const NetworkImage(
@@ -33,17 +33,18 @@ class LocationMapPlaceholder extends StatelessWidget {
child: Stack(
children: <Widget>[
// Fallback UI if image fails (which it will without key)
const Center(
Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Icon(LucideIcons.mapPin, size: 48, color: UiColors.iconSecondary),
SizedBox(height: 8),
Text('Map View (GPS)', style: TextStyle(color: UiColors.textSecondary)),
const Icon(LucideIcons.mapPin,
size: 48, color: UiColors.iconSecondary),
const SizedBox(height: 8),
Text('Map View (GPS)', style: UiTypography.body2r.textSecondary),
],
),
),
// Status Overlay
Positioned(
bottom: 16,
@@ -52,11 +53,11 @@ class LocationMapPlaceholder extends StatelessWidget {
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: UiColors.black.withValues(alpha: 0.1),
blurRadius: 8,
offset: const Offset(0, 4),
),
@@ -65,8 +66,12 @@ class LocationMapPlaceholder extends StatelessWidget {
child: Row(
children: <Widget>[
Icon(
isVerified ? LucideIcons.checkCircle : LucideIcons.alertCircle,
color: isVerified ? UiColors.textSuccess : UiColors.destructive,
isVerified
? LucideIcons.checkCircle
: LucideIcons.alertCircle,
color: isVerified
? UiColors.textSuccess
: UiColors.destructive,
size: 20,
),
const SizedBox(width: 12),
@@ -76,12 +81,12 @@ class LocationMapPlaceholder extends StatelessWidget {
children: <Widget>[
Text(
isVerified ? 'Location Verified' : 'Location Check',
style: UiTypography.body1b.copyWith(color: UiColors.textPrimary),
style: UiTypography.body1b.textPrimary,
),
if (distance != null)
Text(
'${distance!.toStringAsFixed(0)}m from venue',
style: UiTypography.body2r.copyWith(color: UiColors.textSecondary),
style: UiTypography.body2r.textSecondary,
),
],
),

View File

@@ -1,8 +1,8 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
class LunchBreakDialog extends StatefulWidget {
const LunchBreakDialog({super.key, required this.onComplete});
final VoidCallback onComplete;
@@ -47,7 +47,7 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.white,
backgroundColor: UiColors.white,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
@@ -82,25 +82,21 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
Container(
width: 80,
height: 80,
decoration: BoxDecoration(
color: Colors.grey.shade100,
decoration: const BoxDecoration(
color: UiColors.bgSecondary,
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.coffee,
size: 40,
color: Color(0xFF6A7382),
color: UiColors.iconSecondary,
),
),
const SizedBox(height: 24),
const Text(
Text(
"Did You Take\na Lunch?",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xFF121826),
),
style: UiTypography.headline1m.textPrimary,
),
const SizedBox(height: 24),
Row(
@@ -116,18 +112,14 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
child: Container(
padding: const EdgeInsets.symmetric(vertical: 16),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey.shade300),
border: Border.all(color: UiColors.border),
borderRadius: BorderRadius.circular(12),
color: Colors.transparent,
color: UiColors.transparent,
),
alignment: Alignment.center,
child: const Text(
child: Text(
"No",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF121826),
),
style: UiTypography.body1m.textPrimary,
),
),
),
@@ -142,19 +134,15 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
});
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0032A0),
backgroundColor: UiColors.primary,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
child: const Text(
child: Text(
"Yes",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
),
style: UiTypography.body1m.white,
),
),
),
@@ -172,76 +160,51 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text(
Text(
"When did you take lunch?",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
style: UiTypography.headline4m,
),
const SizedBox(height: 24),
// Mock Inputs
Row(
children: <Widget>[
Expanded(
child: DropdownButtonFormField<String>(
isExpanded: true,
initialValue: _breakStart,
items: _timeOptions.map((String t) => DropdownMenuItem(value: t, child: Text(t, style: const TextStyle(fontSize: 13)))).toList(),
onChanged: (String? v) => setState(() => _breakStart = v),
decoration: const InputDecoration(
labelText: 'Start',
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
),
),
),
const SizedBox(width: 10),
Expanded(
child: DropdownButtonFormField<String>(
isExpanded: true,
initialValue: _breakEnd,
items: _timeOptions.map((String t) => DropdownMenuItem(value: t, child: Text(t, style: const TextStyle(fontSize: 13)))).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: const Color(0xFF0032A0),
minimumSize: const Size(double.infinity, 48),
// Mock Inputs
Row(
children: <Widget>[
Expanded(
child: DropdownButtonFormField<String>(
isExpanded: true,
value: _breakStart,
items: _timeOptions
.map((String t) => DropdownMenuItem(
value: t,
child: Text(t, style: UiTypography.body3r)))
.toList(),
onChanged: (String? v) => setState(() => _breakStart = v),
decoration: const InputDecoration(
labelText: 'Start',
contentPadding:
EdgeInsets.symmetric(horizontal: 10, vertical: 8),
),
),
),
child: const Text("Next", style: TextStyle(color: Colors.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),
),
),
),
],
),
],
));
}
Widget _buildStep2b() {
// No lunch reason
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const Text(
"Why didn't you take lunch?",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const SizedBox(height: 16),
..._noLunchReasons.map((String reason) => RadioListTile<String>(
title: Text(reason),
value: reason,
groupValue: _noLunchReason,
onChanged: (String? val) => setState(() => _noLunchReason = val),
)),
const SizedBox(height: 24),
ElevatedButton(
@@ -249,10 +212,47 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
setState(() => _step = 3);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0032A0),
minimumSize: const Size(double.infinity, 48),
),
child: const Text("Next", style: TextStyle(color: Colors.white)),
backgroundColor: UiColors.primary,
minimumSize: const Size(double.infinity, 48),
),
child: Text("Next", style: UiTypography.body1m.white),
),
],
));
}
Widget _buildStep2b() {
// No lunch reason
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
"Why didn't you take lunch?",
style: UiTypography.headline4m,
),
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),
ElevatedButton(
onPressed: () {
setState(() => _step = 3);
},
style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary,
minimumSize: const Size(double.infinity, 48),
),
child: Text("Next", style: UiTypography.body1m.white),
),
],
));
@@ -260,35 +260,36 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
Widget _buildStep3() {
// Additional Notes
return Padding(
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text(
Text(
"Additional Notes",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
style: UiTypography.headline4m,
),
const SizedBox(height: 16),
TextField(
onChanged: (String v) => _additionalNotes = v,
style: UiTypography.body2r,
decoration: const InputDecoration(
hintText: 'Add any details...',
border: OutlineInputBorder(),
),
maxLines: 3,
),
const SizedBox(height: 16),
TextField(
onChanged: (String v) => _additionalNotes = v,
decoration: const InputDecoration(
hintText: 'Add any details...',
border: OutlineInputBorder(),
),
maxLines: 3,
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: () {
setState(() => _step = 4);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0032A0),
minimumSize: const Size(double.infinity, 48),
),
child: const Text("Submit", style: TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary,
minimumSize: const Size(double.infinity, 48),
),
child: Text("Submit", style: UiTypography.body1m.white),
),
],
));
@@ -296,25 +297,26 @@ class _LunchBreakDialogState extends State<LunchBreakDialog> {
Widget _buildStep4() {
// Success
return Padding(
return Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Icon(LucideIcons.checkCircle, size: 64, color: Colors.green),
const Icon(LucideIcons.checkCircle,
size: 64, color: UiColors.success),
const SizedBox(height: 24),
const Text(
Text(
"Break Logged!",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
style: UiTypography.headline1m,
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: widget.onComplete,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0032A0),
minimumSize: const Size(double.infinity, 48),
),
child: const Text("Close", style: TextStyle(color: Colors.white)),
style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary,
minimumSize: const Size(double.infinity, 48),
),
child: Text("Close", style: UiTypography.body1m.white),
),
],
));

View File

@@ -1,3 +1,4 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:lucide_icons/lucide_icons.dart';
@@ -72,9 +73,8 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
@override
Widget build(BuildContext context) {
final Color baseColor = widget.isCheckedIn
? const Color(0xFF10B981)
: const Color(0xFF0032A0);
final Color baseColor =
widget.isCheckedIn ? UiColors.success : UiColors.primary;
if (widget.mode == 'nfc') {
return GestureDetector(
@@ -96,7 +96,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
borderRadius: BorderRadius.circular(16),
boxShadow: <BoxShadow>[
BoxShadow(
color: baseColor.withOpacity(0.4),
color: baseColor.withValues(alpha: 0.4),
blurRadius: 25,
offset: const Offset(0, 10),
spreadRadius: -5,
@@ -106,7 +106,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Icon(LucideIcons.wifi, color: Colors.white),
const Icon(LucideIcons.wifi, color: UiColors.white),
const SizedBox(width: 12),
Text(
widget.isLoading
@@ -114,11 +114,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
? "Checking out..."
: "Checking in...")
: (widget.isCheckedIn ? "NFC Check Out" : "NFC Check In"),
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
style: UiTypography.body1b.white,
),
],
),
@@ -133,12 +129,10 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
// Calculate background color based on drag
final double progress = _dragValue / maxDrag;
final Color startColor = widget.isCheckedIn
? const Color(0xFF10B981)
: const Color(0xFF0032A0);
final Color endColor = widget.isCheckedIn
? const Color(0xFF0032A0)
: const Color(0xFF10B981);
final Color startColor =
widget.isCheckedIn ? UiColors.success : UiColors.primary;
final Color endColor =
widget.isCheckedIn ? UiColors.primary : UiColors.success;
final Color currentColor =
Color.lerp(startColor, endColor, progress) ?? startColor;
@@ -149,7 +143,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
borderRadius: BorderRadius.circular(16),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: UiColors.black.withValues(alpha: 0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
@@ -164,11 +158,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
widget.isCheckedIn
? "Swipe to Check Out"
: "Swipe to Check In",
style: TextStyle(
color: Colors.white.withOpacity(0.8),
fontWeight: FontWeight.w600,
fontSize: 18,
),
style: UiTypography.body1b,
),
),
),
@@ -176,11 +166,7 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
Center(
child: Text(
widget.isCheckedIn ? "Check Out!" : "Check In!",
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 18,
),
style: UiTypography.body1b,
),
),
Positioned(
@@ -193,11 +179,11 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
width: _handleSize,
height: _handleSize,
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.1),
color: UiColors.black.withValues(alpha: 0.1),
blurRadius: 2,
offset: const Offset(0, 1),
),