Merge branch 'dev' into feature/centralized-data-error-handling and resolve conflicts

This commit is contained in:
2026-02-11 12:34:29 +05:30
158 changed files with 10945 additions and 5478 deletions

View File

@@ -176,7 +176,7 @@ class ShiftsRepositoryImpl
final DateTime? endDt = _toDateTime(app.shiftRole.endTime);
final DateTime? createdDt = _toDateTime(app.createdAt);
// Override status to reflect the application state (e.g., CHECKED_OUT, ACCEPTED)
// Override status to reflect the application state (e.g., CHECKED_OUT, CONFIRMED)
final bool hasCheckIn = app.checkInTime != null;
final bool hasCheckOut = app.checkOutTime != null;
dc.ApplicationStatus? appStatus;
@@ -187,7 +187,7 @@ class ShiftsRepositoryImpl
? 'completed'
: hasCheckIn
? 'checked_in'
: _mapStatus(appStatus ?? dc.ApplicationStatus.ACCEPTED);
: _mapStatus(appStatus ?? dc.ApplicationStatus.CONFIRMED);
shifts.add(
Shift(
id: app.shift.id,
@@ -223,7 +223,6 @@ class ShiftsRepositoryImpl
String _mapStatus(dc.ApplicationStatus status) {
switch (status) {
case dc.ApplicationStatus.ACCEPTED:
case dc.ApplicationStatus.CONFIRMED:
return 'confirmed';
case dc.ApplicationStatus.PENDING:
@@ -477,7 +476,7 @@ class ShiftsRepositoryImpl
shiftId: shiftId,
staffId: staffId,
roleId: targetRoleId,
status: dc.ApplicationStatus.ACCEPTED,
status: dc.ApplicationStatus.CONFIRMED,
origin: dc.ApplicationOrigin.STAFF,
)
// TODO: this should be PENDING so a vendor can accept it.
@@ -518,7 +517,7 @@ class ShiftsRepositoryImpl
@override
Future<void> acceptShift(String shiftId) async {
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.ACCEPTED);
await _updateApplicationStatus(shiftId, dc.ApplicationStatus.CONFIRMED);
}
@override

View File

@@ -281,15 +281,20 @@ class ShiftsBloc extends Bloc<ShiftsEvent, ShiftsState>
List<Shift> _filterPastShifts(List<Shift> shifts) {
final now = DateTime.now();
final today = DateTime(now.year, now.month, now.day);
return shifts.where((shift) {
if (shift.date.isEmpty) return false;
try {
final shiftDate = DateTime.parse(shift.date);
return shiftDate.isAfter(now);
final dateOnly = DateTime(
shiftDate.year,
shiftDate.month,
shiftDate.day,
);
return !dateOnly.isBefore(today);
} catch (_) {
return false;
}
}).toList();
}
}

View File

@@ -64,10 +64,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Widget _buildStatCard(IconData icon, String value, String label) {
return Container(
padding: const EdgeInsets.symmetric(vertical: 12),
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(16),
color: UiColors.background,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border),
),
child: Column(
@@ -76,21 +76,19 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Colors.white,
color: UiColors.white,
shape: BoxShape.circle,
),
child: Icon(icon, size: 20, color: UiColors.iconSecondary),
),
const SizedBox(height: 8),
const SizedBox(height: UiConstants.space2),
Text(
value,
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
style: UiTypography.title1m.textPrimary,
),
Text(
label,
style: UiTypography.footnote2r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote2r.textSecondary,
),
],
),
@@ -99,29 +97,21 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Widget _buildTimeBox(String label, String time) {
return Container(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(16),
color: UiColors.background,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: Column(
children: [
Text(
label,
style: const TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: UiColors.textSecondary,
letterSpacing: 0.5,
),
style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: 4),
const SizedBox(height: UiConstants.space1),
Text(
_formatTime(time),
style: UiTypography.display2m.copyWith(
fontSize: 20,
color: UiColors.textPrimary,
),
style: UiTypography.headline2m.textPrimary,
),
],
),
@@ -149,7 +139,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: UiColors.tagSuccess,
backgroundColor: UiColors.success,
),
);
Modular.to.toShifts(selectedDate: state.shiftDate);
@@ -158,7 +148,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(state.message),
backgroundColor: const Color(0xFFEF4444),
backgroundColor: UiColors.destructive,
),
);
}
@@ -203,7 +193,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
children: [
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
padding: const EdgeInsets.all(UiConstants.space5),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -211,16 +201,11 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
"VENDOR",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: UiColors.textSecondary,
letterSpacing: 0.5,
),
style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: 8),
const SizedBox(height: UiConstants.space2),
Row(
children: [
SizedBox(
@@ -229,7 +214,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
child: displayShift.logoUrl != null
? ClipRRect(
borderRadius: BorderRadius.circular(
6,
UiConstants.radiusMdValue,
),
child: Image.network(
displayShift.logoUrl!,
@@ -244,33 +229,26 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
),
),
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Text(
displayShift.clientName,
style: UiTypography.headline5m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.headline5m.textPrimary,
),
],
),
],
),
const SizedBox(height: 24),
const SizedBox(height: UiConstants.space6),
// Date Section
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
Text(
"SHIFT DATE",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: UiColors.textSecondary,
letterSpacing: 0.5,
),
style: UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: 8),
const SizedBox(height: UiConstants.space2),
Row(
children: [
const Icon(
@@ -278,104 +256,44 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
size: 20,
color: UiColors.primary,
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Text(
_formatDate(displayShift.date),
style: UiTypography.headline5m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.headline5m.textPrimary,
),
],
),
],
),
const SizedBox(height: 24),
const SizedBox(height: UiConstants.space6),
// Worker Capacity / Open Slots
if ((displayShift.requiredSlots ?? 0) > 0)
Container(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: const Color(0xFFF0FDF4), // green-50
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: const Color(0xFFBBF7D0),
), // green-200
color: UiColors.success.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: Row(
children: [
const Icon(
Icons.people_alt_outlined,
size: 20,
color: Color(0xFF15803D),
), // 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),
),
),
],
),
UiIcons.users,
size: 16,
color: UiColors.success,
),
SizedBox(
width: 60,
child: LinearProgressIndicator(
value: (displayShift.requiredSlots! > 0)
? (displayShift.filledSlots ?? 0) /
displayShift.requiredSlots!
: 0,
backgroundColor: Colors.white,
color: const Color(0xFF15803D),
minHeight: 6,
borderRadius: BorderRadius.circular(3),
),
const SizedBox(width: UiConstants.space2),
Text(
"$openSlots slots remaining",
style: UiTypography.footnote1m.textSuccess,
),
],
),
),
const SizedBox(height: 24),
// Stats Grid
GridView.count(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
crossAxisCount: 3,
crossAxisSpacing: 12,
childAspectRatio: 0.72,
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),
const SizedBox(height: UiConstants.space6),
// Shift Timing
// Time Section
Row(
children: [
Expanded(
@@ -384,7 +302,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
displayShift.startTime,
),
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space4),
Expanded(
child: _buildTimeBox(
"END TIME",
@@ -393,129 +311,142 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
),
],
),
const SizedBox(height: 24),
const SizedBox(height: UiConstants.space6),
// Location
Column(
crossAxisAlignment: CrossAxisAlignment.start,
// Quick Info Grid
Row(
children: [
const Text(
"LOCATION",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: UiColors.textSecondary,
letterSpacing: 0.5,
Expanded(
child: _buildStatCard(
UiIcons.dollar,
"\$${displayShift.hourlyRate.toStringAsFixed(0)}/hr",
"Base Rate",
),
),
const SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
displayShift.location.isEmpty
? "TBD"
: displayShift.location,
style: UiTypography.title1m.copyWith(
color: UiColors.textPrimary,
),
),
Text(
displayShift.location.isEmpty
? "TBD"
: displayShift.locationAddress,
style: UiTypography.title1m.copyWith(
color: UiColors.textPrimary,
),
),
],
const SizedBox(width: UiConstants.space4),
Expanded(
child: _buildStatCard(
UiIcons.clock,
"${duration.toInt()} hours",
"Duration",
),
),
const SizedBox(width: UiConstants.space4),
Expanded(
child: _buildStatCard(
UiIcons.wallet,
"\$${estimatedTotal.toStringAsFixed(0)}",
"Est. Total",
),
),
],
),
const SizedBox(height: 24),
const SizedBox(height: UiConstants.space8),
// Additional Info
if (displayShift.description != null) ...[
SizedBox(
width: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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,
),
),
],
// Location Section
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"LOCATION",
style: UiTypography.titleUppercase4b.textSecondary,
),
),
],
const SizedBox(height: 20),
if (displayShift.status != 'confirmed' &&
displayShift.hasApplied != true &&
(displayShift.requiredSlots == null ||
displayShift.filledSlots == null ||
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(height: UiConstants.space3),
Container(
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border),
),
const SizedBox(width: 16),
Expanded(
child: ElevatedButton(
onPressed: () => _bookShift(
context,
displayShift!,
child: Column(
children: [
Row(
children: [
const Icon(
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(
backgroundColor: const Color(
0xFF10B981,
const SizedBox(height: UiConstants.space4),
const Divider(),
const SizedBox(height: UiConstants.space2),
TextButton.icon(
onPressed: () {},
icon: const Icon(
UiIcons.arrowRight,
size: 16,
),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(
vertical: 16,
label: const Text("Open in Maps"),
style: TextButton.styleFrom(
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(
foregroundColor: const Color(0xFF10B981),
foregroundColor: UiColors.success,
),
child: const Text('Book'),
),
@@ -581,7 +512,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
).add(DeclineShiftDetailsEvent(id));
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFFEF4444),
foregroundColor: UiColors.destructive,
),
child: const Text('Decline'),
),
@@ -608,29 +539,23 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
width: 36,
child: CircularProgressIndicator(),
),
const SizedBox(height: 16),
const SizedBox(height: UiConstants.space4),
Text(
shift.title,
style: UiTypography.body2b.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.body2b.textPrimary,
textAlign: TextAlign.center,
),
const SizedBox(height: 6),
Text(
'${_formatDate(shift.date)}${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}',
style: UiTypography.body3r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body3r.textSecondary,
textAlign: TextAlign.center,
),
if (shift.clientName.isNotEmpty) ...[
const SizedBox(height: 6),
Text(
shift.clientName,
style: UiTypography.body3r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body3r.textSecondary,
textAlign: TextAlign.center,
),
],
@@ -647,4 +572,150 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Navigator.of(context, rootNavigator: true).pop();
_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'),
),
],
),
);
}
}

View File

@@ -8,7 +8,6 @@ import '../blocs/shifts/shifts_bloc.dart';
import '../widgets/tabs/my_shifts_tab.dart';
import '../widgets/tabs/find_shifts_tab.dart';
import '../widgets/tabs/history_shifts_tab.dart';
import '../styles/shifts_styles.dart';
class ShiftsPage extends StatefulWidget {
final String? initialTab;
@@ -119,29 +118,25 @@ class _ShiftsPageState extends State<ShiftsPage> {
// Note: Calendar logic moved to MyShiftsTab
return Scaffold(
backgroundColor: AppColors.krowBackground,
backgroundColor: UiColors.background,
body: Column(
children: [
// Header (Blue)
Container(
color: AppColors.krowBlue,
color: UiColors.primary,
padding: EdgeInsets.fromLTRB(
20,
MediaQuery.of(context).padding.top + 10,
20,
20,
UiConstants.space5,
MediaQuery.of(context).padding.top + UiConstants.space2,
UiConstants.space5,
UiConstants.space5,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 16,
spacing: UiConstants.space4,
children: [
Text(
t.staff_shifts.title,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.white,
),
style: UiTypography.display1b.white,
),
// Tabs
@@ -155,17 +150,16 @@ class _ShiftsPageState extends State<ShiftsPage> {
showCount: myShiftsLoaded,
enabled: !blockTabsForFind,
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
_buildTab(
"find",
t.staff_shifts.tabs.find_work,
UiIcons.search,
availableJobs
.length, // Passed unfiltered count as badge? Or logic inside? Pass availableJobs.
availableJobs.length,
showCount: availableLoaded,
enabled: baseLoaded,
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
_buildTab(
"history",
t.staff_shifts.tabs.history,
@@ -276,12 +270,15 @@ class _ShiftsPageState extends State<ShiftsPage> {
}
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8),
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space2,
horizontal: UiConstants.space2,
),
decoration: BoxDecoration(
color: isActive
? Colors.white
: Colors.white.withAlpha((0.2 * 255).round()),
borderRadius: BorderRadius.circular(8),
? UiColors.white
: UiColors.white.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -291,23 +288,17 @@ class _ShiftsPageState extends State<ShiftsPage> {
icon,
size: 14,
color: !enabled
? Colors.white.withAlpha((0.5 * 255).round())
? UiColors.white.withValues(alpha: 0.5)
: isActive
? AppColors.krowBlue
: Colors.white,
? UiColors.primary
: UiColors.white,
),
const SizedBox(width: 6),
const SizedBox(width: UiConstants.space1),
Flexible(
child: Text(
label,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w500,
color: !enabled
? Colors.white.withAlpha((0.5 * 255).round())
: isActive
? AppColors.krowBlue
: Colors.white,
style: (isActive ? UiTypography.body3m.copyWith(color: UiColors.primary) : UiTypography.body3m.white).copyWith(
color: !enabled ? UiColors.white.withValues(alpha: 0.5) : null,
),
overflow: TextOverflow.ellipsis,
),
@@ -316,23 +307,21 @@ class _ShiftsPageState extends State<ShiftsPage> {
const SizedBox(width: 4),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
horizontal: UiConstants.space1,
vertical: 2,
),
constraints: const BoxConstraints(minWidth: 18),
decoration: BoxDecoration(
color: isActive
? AppColors.krowBlue.withAlpha((0.1 * 255).round())
: Colors.white.withAlpha((0.2 * 255).round()),
borderRadius: BorderRadius.circular(999),
? UiColors.primary.withValues(alpha: 0.1)
: UiColors.white.withValues(alpha: 0.2),
borderRadius: UiConstants.radiusFull,
),
child: Center(
child: Text(
"$count",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: isActive ? AppColors.krowBlue : Colors.white,
style: UiTypography.footnote1b.copyWith(
color: isActive ? UiColors.primary : UiColors.white,
),
),
),

View File

@@ -118,14 +118,14 @@ class _MyShiftCardState extends State<MyShiftCard> {
Modular.to.pushShiftDetails(widget.shift);
},
child: Container(
margin: const EdgeInsets.only(bottom: 12),
margin: const EdgeInsets.only(bottom: UiConstants.space3),
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -142,7 +142,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
// Status Badge
if (statusText.isNotEmpty)
Padding(
padding: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.only(bottom: UiConstants.space2),
child: Row(
children: [
if (statusIcon != null)
@@ -173,14 +173,14 @@ class _MyShiftCardState extends State<MyShiftCard> {
),
// Shift Type Badge for available/pending shifts
if (status == 'open' || status == 'pending') ...[
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: UiColors.primary.withOpacity(0.1),
color: UiColors.primary.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(4),
),
child: Text(
@@ -205,20 +205,20 @@ class _MyShiftCardState extends State<MyShiftCard> {
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
UiColors.primary.withOpacity(0.09),
UiColors.primary.withOpacity(0.03),
UiColors.primary.withValues(alpha: 0.09),
UiColors.primary.withValues(alpha: 0.03),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: UiColors.primary.withOpacity(0.09),
color: UiColors.primary.withValues(alpha: 0.09),
),
),
child: widget.shift.logoUrl != null
? ClipRRect(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
child: Image.network(
widget.shift.logoUrl!,
fit: BoxFit.contain,
@@ -232,7 +232,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
),
),
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
// Details
Expanded(
@@ -249,42 +249,34 @@ class _MyShiftCardState extends State<MyShiftCard> {
children: [
Text(
widget.shift.title,
style: UiTypography.body2m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.body2m.textPrimary,
overflow: TextOverflow.ellipsis,
),
Text(
widget.shift.clientName,
style: UiTypography.body3r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body3r.textSecondary,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"\$${estimatedTotal.toStringAsFixed(0)}",
style: UiTypography.title1m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.title1m.textPrimary,
),
Text(
"\$${widget.shift.hourlyRate.toInt()}/hr · ${duration.toInt()}h",
style: UiTypography.footnote2r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote2r.textSecondary,
),
],
),
],
),
const SizedBox(height: 8),
const SizedBox(height: UiConstants.space2),
// Date & Time - Multi-Day or Single Day
if (widget.shift.durationDays != null &&
@@ -332,11 +324,9 @@ class _MyShiftCardState extends State<MyShiftCard> {
const SizedBox(width: 4),
Text(
_formatDate(widget.shift.date),
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
const Icon(
UiIcons.clock,
size: 12,
@@ -345,9 +335,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
const SizedBox(width: 4),
Text(
"${_formatTime(widget.shift.startTime)} - ${_formatTime(widget.shift.endTime)}",
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
),
],
),
@@ -368,9 +356,7 @@ class _MyShiftCardState extends State<MyShiftCard> {
widget.shift.locationAddress.isNotEmpty
? widget.shift.locationAddress
: widget.shift.location,
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
overflow: TextOverflow.ellipsis,
),
),

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:krow_domain/krow_domain.dart';
import 'package:design_system/design_system.dart';
import 'package:core_localization/core_localization.dart';
class ShiftAssignmentCard extends StatelessWidget {
final Shift shift;
@@ -66,12 +65,12 @@ class ShiftAssignmentCard extends StatelessWidget {
return Container(
decoration: BoxDecoration(
color: Colors.white,
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
@@ -81,7 +80,7 @@ class ShiftAssignmentCard extends StatelessWidget {
children: [
// Header
Padding(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(UiConstants.space4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -97,20 +96,20 @@ class ShiftAssignmentCard extends StatelessWidget {
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
UiColors.primary.withOpacity(0.09),
UiColors.primary.withOpacity(0.03),
UiColors.primary.withValues(alpha: 0.09),
UiColors.primary.withValues(alpha: 0.03),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: UiColors.primary.withOpacity(0.09),
color: UiColors.primary.withValues(alpha: 0.09),
),
),
child: shift.logoUrl != null
? ClipRRect(
borderRadius: BorderRadius.circular(12),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
child: Image.network(
shift.logoUrl!,
fit: BoxFit.contain,
@@ -124,7 +123,7 @@ class ShiftAssignmentCard extends StatelessWidget {
),
),
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
// Details
Expanded(
@@ -140,42 +139,34 @@ class ShiftAssignmentCard extends StatelessWidget {
children: [
Text(
shift.title,
style: UiTypography.body2m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.body2m.textPrimary,
overflow: TextOverflow.ellipsis,
),
Text(
shift.clientName,
style: UiTypography.body3r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.body3r.textSecondary,
overflow: TextOverflow.ellipsis,
),
],
),
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"\$${totalPay.toStringAsFixed(0)}",
style: UiTypography.title1m.copyWith(
color: UiColors.textPrimary,
),
style: UiTypography.title1m.textPrimary,
),
Text(
"\$${shift.hourlyRate.toInt()}/hr · ${hours.toInt()}h",
style: UiTypography.footnote2r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote2r.textSecondary,
),
],
),
],
),
const SizedBox(height: 12),
const SizedBox(height: UiConstants.space3),
// Date & Time
Row(
@@ -188,11 +179,9 @@ class ShiftAssignmentCard extends StatelessWidget {
const SizedBox(width: 4),
Text(
_formatDate(shift.date),
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
const Icon(
UiIcons.clock,
size: 12,
@@ -201,9 +190,7 @@ class ShiftAssignmentCard extends StatelessWidget {
const SizedBox(width: 4),
Text(
"${_formatTime(shift.startTime)} - ${_formatTime(shift.endTime)}",
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
),
],
),
@@ -223,9 +210,7 @@ class ShiftAssignmentCard extends StatelessWidget {
shift.locationAddress.isNotEmpty
? shift.locationAddress
: shift.location,
style: UiTypography.footnote1r.copyWith(
color: UiColors.textSecondary,
),
style: UiTypography.footnote1r.textSecondary,
overflow: TextOverflow.ellipsis,
),
),
@@ -240,38 +225,55 @@ class ShiftAssignmentCard extends StatelessWidget {
),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
// Actions
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(
children: [
Expanded(
child: OutlinedButton(
child: TextButton(
onPressed: onDecline,
style: OutlinedButton.styleFrom(
foregroundColor: UiColors.iconSecondary,
side: const BorderSide(color: UiColors.border),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
style: TextButton.styleFrom(
foregroundColor: UiColors.destructive,
),
child: Text(
"Decline", // Fallback if translation is broken
style: UiTypography.body2m.textError,
),
child: Text(t.staff_shifts.action.decline),
),
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space2),
Expanded(
child: ElevatedButton(
onPressed: onConfirm,
onPressed: isConfirming ? null : onConfirm,
style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary,
foregroundColor: Colors.white,
foregroundColor: UiColors.white,
elevation: 0,
padding: const EdgeInsets.symmetric(vertical: 14),
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,
),
),
),
],

View File

@@ -2,7 +2,6 @@ import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../styles/shifts_styles.dart';
import '../my_shift_card.dart';
import '../shared/empty_state_view.dart';
@@ -27,22 +26,21 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
return GestureDetector(
onTap: () => setState(() => _jobType = id),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
vertical: UiConstants.space2,
),
decoration: BoxDecoration(
color: isSelected ? AppColors.krowBlue : Colors.white,
borderRadius: BorderRadius.circular(999),
color: isSelected ? UiColors.primary : UiColors.white,
borderRadius: UiConstants.radiusFull,
border: Border.all(
color: isSelected ? AppColors.krowBlue : const Color(0xFFE2E8F0),
color: isSelected ? UiColors.primary : UiColors.border,
),
),
child: Text(
label,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: isSelected ? Colors.white : const Color(0xFF64748B),
),
style: (isSelected ? UiTypography.footnote2m.white : UiTypography.footnote2m.textSecondary),
),
),
);
@@ -73,8 +71,11 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
children: [
// Search and Filters
Container(
color: Colors.white,
padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
color: UiColors.white,
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
vertical: UiConstants.space4,
),
child: Column(
children: [
// Search Bar
@@ -83,12 +84,12 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
Expanded(
child: Container(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: 12),
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
decoration: BoxDecoration(
color: const Color(0xFFF8FAFC),
borderRadius: BorderRadius.circular(12),
color: UiColors.background,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: const Color(0xFFE2E8F0),
color: UiColors.border,
),
),
child: Row(
@@ -96,20 +97,17 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
const Icon(
UiIcons.search,
size: 20,
color: Color(0xFF94A3B8),
color: UiColors.textInactive,
),
const SizedBox(width: 10),
const SizedBox(width: UiConstants.space2),
Expanded(
child: TextField(
onChanged: (v) =>
setState(() => _searchQuery = v),
decoration: const InputDecoration(
decoration: InputDecoration(
border: InputBorder.none,
hintText: "Search jobs, location...",
hintStyle: TextStyle(
color: Color(0xFF94A3B8),
fontSize: 14,
),
hintStyle: UiTypography.body2r.textPlaceholder,
),
),
),
@@ -117,37 +115,37 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
),
),
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Container(
height: 48,
width: 48,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
color: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: const Color(0xFFE2E8F0),
color: UiColors.border,
),
),
child: const Icon(
UiIcons.filter,
size: 18,
color: Color(0xFF64748B),
color: UiColors.textSecondary,
),
),
],
),
const SizedBox(height: 16),
const SizedBox(height: UiConstants.space4),
// Filter Tabs
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
_buildFilterTab('all', 'All Jobs'),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
_buildFilterTab('one-day', 'One Day'),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
_buildFilterTab('multi-day', 'Multi-Day'),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
_buildFilterTab('long-term', 'Long Term'),
],
),
@@ -158,19 +156,19 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
Expanded(
child: filteredJobs.isEmpty
? EmptyStateView(
? const EmptyStateView(
icon: UiIcons.search,
title: "No jobs available",
subtitle: "Check back later",
)
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
child: Column(
children: [
const SizedBox(height: 20),
const SizedBox(height: UiConstants.space5),
...filteredJobs.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.only(bottom: UiConstants.space3),
child: MyShiftCard(
shift: shift,
),

View File

@@ -17,7 +17,7 @@ class HistoryShiftsTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (historyShifts.isEmpty) {
return EmptyStateView(
return const EmptyStateView(
icon: UiIcons.clock,
title: "No shift history",
subtitle: "Completed shifts appear here",
@@ -25,13 +25,13 @@ class HistoryShiftsTab extends StatelessWidget {
}
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
child: Column(
children: [
const SizedBox(height: 20),
const SizedBox(height: UiConstants.space5),
...historyShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.only(bottom: UiConstants.space3),
child: GestureDetector(
onTap: () => Modular.to.pushShiftDetails(shift),
child: MyShiftCard(

View File

@@ -1,14 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:design_system/design_system.dart';
import 'package:krow_domain/krow_domain.dart';
import '../../blocs/shifts/shifts_bloc.dart';
import '../my_shift_card.dart';
import '../shift_assignment_card.dart';
import '../shared/empty_state_view.dart';
import '../../styles/shifts_styles.dart';
class MyShiftsTab extends StatefulWidget {
final List<Shift> myShifts;
@@ -118,14 +116,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
Navigator.of(context).pop();
context.read<ShiftsBloc>().add(AcceptShiftEvent(id));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Shift confirmed!'),
backgroundColor: Color(0xFF10B981),
SnackBar(
content: const Text('Shift confirmed!'),
backgroundColor: UiColors.success,
),
);
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFF10B981),
foregroundColor: UiColors.success,
),
child: const Text('Accept'),
),
@@ -152,14 +150,14 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
Navigator.of(context).pop();
context.read<ShiftsBloc>().add(DeclineShiftEvent(id));
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Shift declined.'),
backgroundColor: Color(0xFFEF4444),
SnackBar(
content: const Text('Shift declined.'),
backgroundColor: UiColors.destructive,
),
);
},
style: TextButton.styleFrom(
foregroundColor: const Color(0xFFEF4444),
foregroundColor: UiColors.destructive,
),
child: const Text('Decline'),
),
@@ -212,12 +210,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
children: [
// Calendar Selector
Container(
color: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 16),
color: UiColors.white,
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
horizontal: UiConstants.space4,
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.only(bottom: UiConstants.space3),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@@ -225,7 +226,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
icon: const Icon(
UiIcons.chevronLeft,
size: 20,
color: AppColors.krowCharcoal,
color: UiColors.textPrimary,
),
onPressed: () => setState(() {
_weekOffset--;
@@ -237,17 +238,13 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
),
Text(
DateFormat('MMMM yyyy').format(weekStartDate),
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColors.krowCharcoal,
),
style: UiTypography.title1m.textPrimary,
),
IconButton(
icon: const Icon(
UiIcons.chevronRight,
size: 20,
color: AppColors.krowCharcoal,
color: UiColors.textPrimary,
),
onPressed: () => setState(() {
_weekOffset++;
@@ -284,13 +281,13 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
height: 60,
decoration: BoxDecoration(
color: isSelected
? AppColors.krowBlue
: Colors.white,
borderRadius: BorderRadius.circular(12),
? UiColors.primary
: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: isSelected
? AppColors.krowBlue
: AppColors.krowBorder,
? UiColors.primary
: UiColors.border,
width: 1,
),
),
@@ -299,31 +296,25 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
children: [
Text(
date.day.toString().padLeft(2, '0'),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isSelected
? Colors.white
: AppColors.krowCharcoal,
),
style: isSelected
? UiTypography.body1b.white
: UiTypography.body1b.textPrimary,
),
Text(
DateFormat('E').format(date),
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: isSelected
? Colors.white.withOpacity(0.8)
: AppColors.krowMuted,
style: (isSelected
? UiTypography.footnote2m.white
: UiTypography.footnote2m.textSecondary).copyWith(
color: isSelected ? UiColors.white.withValues(alpha: 0.8) : null,
),
),
if (hasShifts && !isSelected)
Container(
margin: const EdgeInsets.only(top: 4),
margin: const EdgeInsets.only(top: UiConstants.space1),
width: 4,
height: 4,
decoration: const BoxDecoration(
color: AppColors.krowBlue,
color: UiColors.primary,
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(
child: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20),
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
child: Column(
children: [
const SizedBox(height: 20),
const SizedBox(height: UiConstants.space5),
if (widget.pendingAssignments.isNotEmpty) ...[
_buildSectionHeader(
"Awaiting Confirmation",
const Color(0xFFF59E0B),
UiColors.textWarning,
),
...widget.pendingAssignments.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.only(bottom: UiConstants.space4),
child: ShiftAssignmentCard(
shift: shift,
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) ...[
_buildSectionHeader("Cancelled Shifts", AppColors.krowMuted),
_buildSectionHeader("Cancelled Shifts", UiColors.textSecondary),
...visibleCancelledShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.only(bottom: UiConstants.space4),
child: _buildCancelledCard(
title: shift.title,
client: shift.clientName,
@@ -383,15 +374,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
),
),
),
const SizedBox(height: 12),
const SizedBox(height: UiConstants.space3),
],
// Confirmed Shifts
if (visibleMyShifts.isNotEmpty) ...[
_buildSectionHeader("Confirmed Shifts", AppColors.krowMuted),
_buildSectionHeader("Confirmed Shifts", UiColors.textSecondary),
...visibleMyShifts.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.only(bottom: UiConstants.space3),
child: MyShiftCard(shift: shift),
),
),
@@ -417,7 +408,7 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
Widget _buildSectionHeader(String title, Color dotColor) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.only(bottom: UiConstants.space4),
child: Row(
children: [
Container(
@@ -425,16 +416,12 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
height: 8,
decoration: BoxDecoration(color: dotColor, shape: BoxShape.circle),
),
const SizedBox(width: 8),
const SizedBox(width: UiConstants.space2),
Text(
title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: dotColor == AppColors.krowMuted
? AppColors.krowMuted
: dotColor,
),
style: (dotColor == UiColors.textSecondary
? UiTypography.body2b.textSecondary
: UiTypography.body2b.copyWith(color: dotColor)),
),
],
),
@@ -455,11 +442,11 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(16),
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.krowBorder),
color: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase + 4),
border: Border.all(color: UiColors.border),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -470,33 +457,25 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
width: 6,
height: 6,
decoration: const BoxDecoration(
color: Color(0xFFEF4444),
color: UiColors.destructive,
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
const Text(
Text(
"CANCELLED",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
color: Color(0xFFEF4444),
),
style: UiTypography.footnote2b.textError,
),
if (isLastMinute) ...[
const SizedBox(width: 4),
const Text(
Text(
"• 4hr compensation",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w500,
color: Color(0xFF10B981),
),
style: UiTypography.footnote2m.textSuccess,
),
],
],
),
const SizedBox(height: 12),
const SizedBox(height: UiConstants.space3),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@@ -504,18 +483,18 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
width: 44,
height: 44,
decoration: BoxDecoration(
color: AppColors.krowBlue.withOpacity(0.05),
borderRadius: BorderRadius.circular(12),
color: UiColors.primary.withValues(alpha: 0.05),
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
child: const Center(
child: Icon(
LucideIcons.briefcase,
color: AppColors.krowBlue,
UiIcons.briefcase,
color: UiColors.primary,
size: 20,
),
),
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -529,18 +508,11 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
children: [
Text(
title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: AppColors.krowCharcoal,
),
style: UiTypography.body2b.textPrimary,
),
Text(
client,
style: const TextStyle(
fontSize: 12,
color: AppColors.krowMuted,
),
style: UiTypography.footnote1r.textSecondary,
),
],
),
@@ -550,52 +522,39 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
children: [
Text(
pay,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.krowCharcoal,
),
style: UiTypography.headline4m.textPrimary,
),
Text(
rate,
style: const TextStyle(
fontSize: 10,
color: AppColors.krowMuted,
),
style: UiTypography.footnote2r.textSecondary,
),
],
),
],
),
const SizedBox(height: 8),
const SizedBox(height: UiConstants.space2),
Row(
children: [
const Icon(
LucideIcons.calendar,
UiIcons.calendar,
size: 12,
color: AppColors.krowMuted,
color: UiColors.textSecondary,
),
const SizedBox(width: 4),
Text(
date,
style: const TextStyle(
fontSize: 12,
color: AppColors.krowMuted,
),
style: UiTypography.footnote1r.textSecondary,
),
const SizedBox(width: 12),
const SizedBox(width: UiConstants.space3),
const Icon(
LucideIcons.clock,
UiIcons.clock,
size: 12,
color: AppColors.krowMuted,
color: UiColors.textSecondary,
),
const SizedBox(width: 4),
Text(
time,
style: const TextStyle(
fontSize: 12,
color: AppColors.krowMuted,
),
style: UiTypography.footnote1r.textSecondary,
),
],
),
@@ -603,18 +562,15 @@ class _MyShiftsTabState extends State<MyShiftsTab> {
Row(
children: [
const Icon(
LucideIcons.mapPin,
UiIcons.mapPin,
size: 12,
color: AppColors.krowMuted,
color: UiColors.textSecondary,
),
const SizedBox(width: 4),
Expanded(
child: Text(
address,
style: const TextStyle(
fontSize: 12,
color: AppColors.krowMuted,
),
style: UiTypography.footnote1r.textSecondary,
overflow: TextOverflow.ellipsis,
),
),