Merge branch 'dev' into feature/centralized-data-error-handling and resolve conflicts
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user