feat: Add required and filled slots to Shift entity and update ShiftDetailsPage for capacity display
This commit is contained in:
@@ -24,6 +24,8 @@ class Shift extends Equatable {
|
|||||||
final double? longitude;
|
final double? longitude;
|
||||||
final String? status;
|
final String? status;
|
||||||
final int? durationDays; // For multi-day shifts
|
final int? durationDays; // For multi-day shifts
|
||||||
|
final int? requiredSlots;
|
||||||
|
final int? filledSlots;
|
||||||
|
|
||||||
const Shift({
|
const Shift({
|
||||||
required this.id,
|
required this.id,
|
||||||
@@ -49,6 +51,8 @@ class Shift extends Equatable {
|
|||||||
this.longitude,
|
this.longitude,
|
||||||
this.status,
|
this.status,
|
||||||
this.durationDays,
|
this.durationDays,
|
||||||
|
this.requiredSlots,
|
||||||
|
this.filledSlots,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -76,6 +80,8 @@ class Shift extends Equatable {
|
|||||||
longitude,
|
longitude,
|
||||||
status,
|
status,
|
||||||
durationDays,
|
durationDays,
|
||||||
|
requiredSlots,
|
||||||
|
filledSlots,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|||||||
status: _mapStatus(status),
|
status: _mapStatus(status),
|
||||||
description: shift.description,
|
description: shift.description,
|
||||||
durationDays: shift.durationDays,
|
durationDays: shift.durationDays,
|
||||||
|
requiredSlots: shift.requiredSlots,
|
||||||
|
filledSlots: shift.filledSlots,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -182,6 +184,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|||||||
status: s.status?.stringValue.toLowerCase() ?? 'open',
|
status: s.status?.stringValue.toLowerCase() ?? 'open',
|
||||||
description: s.description,
|
description: s.description,
|
||||||
durationDays: s.durationDays,
|
durationDays: s.durationDays,
|
||||||
|
requiredSlots: null, // Basic list doesn't fetch detailed role stats yet
|
||||||
|
filledSlots: null,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +214,20 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|||||||
final s = result.data.shift;
|
final s = result.data.shift;
|
||||||
if (s == null) return null;
|
if (s == null) return null;
|
||||||
|
|
||||||
|
int? required;
|
||||||
|
int? filled;
|
||||||
|
try {
|
||||||
|
final rolesRes = await _dataConnect.listShiftRolesByShiftId(shiftId: shiftId).execute();
|
||||||
|
if (rolesRes.data.shiftRoles.isNotEmpty) {
|
||||||
|
required = 0;
|
||||||
|
filled = 0;
|
||||||
|
for(var r in rolesRes.data.shiftRoles) {
|
||||||
|
required = (required ?? 0) + r.count;
|
||||||
|
filled = (filled ?? 0) + (r.assigned ?? 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
final startDt = _toDateTime(s.startTime);
|
final startDt = _toDateTime(s.startTime);
|
||||||
final endDt = _toDateTime(s.endTime);
|
final endDt = _toDateTime(s.endTime);
|
||||||
final createdDt = _toDateTime(s.createdAt);
|
final createdDt = _toDateTime(s.createdAt);
|
||||||
@@ -229,6 +247,8 @@ class ShiftsRepositoryImpl implements ShiftsRepositoryInterface {
|
|||||||
status: s.status?.stringValue ?? 'OPEN',
|
status: s.status?.stringValue ?? 'OPEN',
|
||||||
description: s.description,
|
description: s.description,
|
||||||
durationDays: s.durationDays,
|
durationDays: s.durationDays,
|
||||||
|
requiredSlots: required,
|
||||||
|
filledSlots: filled,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,22 +3,16 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:design_system/design_system.dart'; // Re-added for UiIcons/Colors as they are used in expanded logic
|
import 'package:design_system/design_system.dart'; // Re-added for UiIcons/Colors as they are used in expanded logic
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import '../blocs/shift_details/shift_details_bloc.dart';
|
import '../blocs/shift_details/shift_details_bloc.dart';
|
||||||
import '../blocs/shift_details/shift_details_event.dart';
|
import '../blocs/shift_details/shift_details_event.dart';
|
||||||
import '../blocs/shift_details/shift_details_state.dart';
|
import '../blocs/shift_details/shift_details_state.dart';
|
||||||
import '../styles/shifts_styles.dart';
|
|
||||||
import '../widgets/my_shift_card.dart';
|
|
||||||
|
|
||||||
class ShiftDetailsPage extends StatelessWidget {
|
class ShiftDetailsPage extends StatelessWidget {
|
||||||
final String shiftId;
|
final String shiftId;
|
||||||
final Shift? shift;
|
final Shift? shift;
|
||||||
|
|
||||||
const ShiftDetailsPage({
|
const ShiftDetailsPage({super.key, required this.shiftId, this.shift});
|
||||||
super.key,
|
|
||||||
required this.shiftId,
|
|
||||||
this.shift,
|
|
||||||
});
|
|
||||||
|
|
||||||
String _formatTime(String time) {
|
String _formatTime(String time) {
|
||||||
if (time.isEmpty) return '';
|
if (time.isEmpty) return '';
|
||||||
@@ -33,6 +27,16 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatDate(String dateStr) {
|
||||||
|
if (dateStr.isEmpty) return '';
|
||||||
|
try {
|
||||||
|
final date = DateTime.parse(dateStr);
|
||||||
|
return DateFormat('EEEE, MMMM d, y').format(date);
|
||||||
|
} catch (e) {
|
||||||
|
return dateStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double _calculateDuration(Shift shift) {
|
double _calculateDuration(Shift shift) {
|
||||||
if (shift.startTime.isEmpty || shift.endTime.isEmpty) {
|
if (shift.startTime.isEmpty || shift.endTime.isEmpty) {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -70,9 +74,7 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.title1m.copyWith(
|
style: UiTypography.title1m.copyWith(color: UiColors.textPrimary),
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
@@ -118,9 +120,9 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider<ShiftDetailsBloc>(
|
||||||
create: (_) => Modular.get<ShiftDetailsBloc>()
|
create: (_) =>
|
||||||
..add(LoadShiftDetailsEvent(shiftId)),
|
Modular.get<ShiftDetailsBloc>()..add(LoadShiftDetailsEvent(shiftId)),
|
||||||
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
|
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
|
||||||
listener: (context, state) {
|
listener: (context, state) {
|
||||||
if (state is ShiftActionSuccess) {
|
if (state is ShiftActionSuccess) {
|
||||||
@@ -163,213 +165,322 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
|
|
||||||
final duration = _calculateDuration(displayShift);
|
final duration = _calculateDuration(displayShift);
|
||||||
final estimatedTotal = (displayShift.hourlyRate) * duration;
|
final estimatedTotal = (displayShift.hourlyRate) * duration;
|
||||||
|
final openSlots =
|
||||||
|
(displayShift.requiredSlots ?? 0) -
|
||||||
|
(displayShift.filledSlots ?? 0);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: AppColors.krowBackground,
|
appBar: UiAppBar(
|
||||||
appBar: AppBar(
|
title: displayShift.title,
|
||||||
title: const Text("Shift Details"),
|
showBackButton: true,
|
||||||
backgroundColor: Colors.white,
|
centerTitle: false,
|
||||||
foregroundColor: AppColors.krowCharcoal,
|
|
||||||
elevation: 0.5,
|
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Column(
|
||||||
padding: const EdgeInsets.all(20.0),
|
children: [
|
||||||
child: Column(
|
Expanded(
|
||||||
children: [
|
child: SingleChildScrollView(
|
||||||
Expanded(
|
padding: const EdgeInsets.all(20.0),
|
||||||
child: SingleChildScrollView(
|
child: Column(
|
||||||
child: Column(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
MyShiftCard(
|
// Vendor Section
|
||||||
shift: displayShift,
|
Column(
|
||||||
// No direct actions on the card, handled by page buttons
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
),
|
children: [
|
||||||
const SizedBox(height: 24),
|
const Text(
|
||||||
|
"VENDOR",
|
||||||
// Stats Row
|
style: TextStyle(
|
||||||
Row(
|
fontSize: 10,
|
||||||
children: [
|
fontWeight: FontWeight.bold,
|
||||||
Expanded(
|
color: UiColors.textSecondary,
|
||||||
child: _buildStatCard(
|
letterSpacing: 0.5,
|
||||||
UiIcons.dollar,
|
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
|
||||||
"Total",
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
),
|
||||||
Expanded(
|
const SizedBox(height: 8),
|
||||||
child: _buildStatCard(
|
Row(
|
||||||
UiIcons.dollar,
|
children: [
|
||||||
"\$${displayShift.hourlyRate.toInt()}",
|
Container(
|
||||||
"Hourly Rate",
|
width: 24,
|
||||||
),
|
height: 24,
|
||||||
),
|
child: displayShift.logoUrl != null
|
||||||
const SizedBox(width: 12),
|
? ClipRRect(
|
||||||
Expanded(
|
borderRadius: BorderRadius.circular(
|
||||||
child: _buildStatCard(
|
6,
|
||||||
UiIcons.clock,
|
),
|
||||||
"${duration.toInt()}",
|
child: Image.network(
|
||||||
"Hours",
|
displayShift.logoUrl!,
|
||||||
),
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
),
|
: const Center(
|
||||||
const SizedBox(height: 24),
|
child: Icon(
|
||||||
|
UiIcons.briefcase,
|
||||||
// In/Out Time
|
color: UiColors.primary,
|
||||||
Row(
|
size: 20,
|
||||||
children: [
|
),
|
||||||
Expanded(
|
|
||||||
child: _buildTimeBox(
|
|
||||||
"CLOCK IN TIME",
|
|
||||||
displayShift.startTime,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: _buildTimeBox(
|
|
||||||
"CLOCK OUT TIME",
|
|
||||||
displayShift.endTime,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
|
|
||||||
// Location
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
"LOCATION",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 10,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
displayShift.location.isEmpty
|
|
||||||
? "TBD"
|
|
||||||
: displayShift.location,
|
|
||||||
style: UiTypography.title1m.copyWith(
|
|
||||||
color: UiColors.textPrimary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(displayShift!.locationAddress),
|
|
||||||
duration: const Duration(seconds: 3),
|
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(UiIcons.navigation, size: 14),
|
|
||||||
label: const Text(
|
|
||||||
"Get direction",
|
|
||||||
style: TextStyle(fontSize: 12),
|
|
||||||
),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: UiColors.textPrimary,
|
|
||||||
side: const BorderSide(color: UiColors.border),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 0),
|
|
||||||
minimumSize: const Size(0, 32),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Container(
|
|
||||||
height: 128,
|
|
||||||
width: double.infinity,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey.shade100,
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
),
|
||||||
child: const Center(
|
const SizedBox(width: 8),
|
||||||
child: Icon(
|
Text(
|
||||||
UiIcons.mapPin,
|
displayShift.clientName,
|
||||||
color: UiColors.iconSecondary,
|
style: UiTypography.headline5m.copyWith(
|
||||||
size: 32,
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Placeholder for Map
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
// Additional Info
|
// Date Section
|
||||||
if (displayShift.description != null) ...[
|
Column(
|
||||||
SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
width: double.infinity,
|
children: [
|
||||||
child: Column(
|
const Text(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
"SHIFT DATE",
|
||||||
children: [
|
style: TextStyle(
|
||||||
const Text(
|
fontSize: 10,
|
||||||
"ADDITIONAL INFO",
|
fontWeight: FontWeight.bold,
|
||||||
style: TextStyle(
|
color: UiColors.textSecondary,
|
||||||
fontSize: 10,
|
letterSpacing: 0.5,
|
||||||
fontWeight: FontWeight.bold,
|
),
|
||||||
color: UiColors.textSecondary,
|
),
|
||||||
letterSpacing: 0.5,
|
const SizedBox(height: 8),
|
||||||
),
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
UiIcons.calendar,
|
||||||
|
size: 20,
|
||||||
|
color: UiColors.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
_formatDate(displayShift.date),
|
||||||
|
style: UiTypography.headline5m.copyWith(
|
||||||
|
color: UiColors.textPrimary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
),
|
||||||
Text(
|
],
|
||||||
displayShift.description!,
|
),
|
||||||
style: UiTypography.body2m.copyWith(
|
],
|
||||||
color: UiColors.textPrimary,
|
),
|
||||||
),
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Worker Capacity / Open Slots
|
||||||
|
if ((displayShift.requiredSlots ?? 0) > 0)
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color(0xFFF0FDF4), // green-50
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(
|
||||||
|
color: const Color(0xFFBBF7D0),
|
||||||
|
), // green-200
|
||||||
|
),
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
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(height: 24),
|
||||||
|
|
||||||
|
// Stats Grid
|
||||||
|
GridView.count(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
crossAxisCount: 3,
|
||||||
|
crossAxisSpacing: 12,
|
||||||
|
childAspectRatio: 0.85,
|
||||||
|
children: [
|
||||||
|
_buildStatCard(
|
||||||
|
UiIcons.dollar,
|
||||||
|
"\$${estimatedTotal.toStringAsFixed(0)}",
|
||||||
|
"Total Pay",
|
||||||
|
),
|
||||||
|
_buildStatCard(
|
||||||
|
UiIcons.dollar,
|
||||||
|
"\$${displayShift.hourlyRate.toInt()}",
|
||||||
|
"Per Hour",
|
||||||
|
),
|
||||||
|
_buildStatCard(
|
||||||
|
UiIcons.clock,
|
||||||
|
"${duration.toInt()}h",
|
||||||
|
"Duration",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Shift Timing
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildTimeBox(
|
||||||
|
"START TIME",
|
||||||
|
displayShift.startTime,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: _buildTimeBox(
|
||||||
|
"END TIME",
|
||||||
|
displayShift.endTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
|
||||||
|
// Location
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
"LOCATION",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: UiColors.textSecondary,
|
||||||
|
letterSpacing: 0.5,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(height: 24),
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
_declineShift(context, displayShift!.id),
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
foregroundColor: const Color(0xFFEF4444),
|
||||||
|
side: const BorderSide(
|
||||||
|
color: Color(0xFFEF4444),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text("Decline"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () =>
|
||||||
|
_bookShift(context, displayShift!.id),
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: const Color(0xFF10B981),
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
vertical: 16,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text("Book Shift"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: MediaQuery.of(context).padding.bottom + 10,
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
),
|
||||||
Row(
|
],
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: OutlinedButton(
|
|
||||||
onPressed: () => _declineShift(context, displayShift!.id),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
|
||||||
side: const BorderSide(color: Color(0xFFEF4444)),
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
child: const Text("Decline"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: () => _bookShift(context, displayShift!.id),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: const Color(0xFF10B981),
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
|
||||||
),
|
|
||||||
child: const Text("Book Shift"),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SizedBox(height: MediaQuery.of(context).padding.bottom + 10),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -392,7 +503,9 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
BlocProvider.of<ShiftDetailsBloc>(context).add(BookShiftDetailsEvent(id));
|
BlocProvider.of<ShiftDetailsBloc>(
|
||||||
|
context,
|
||||||
|
).add(BookShiftDetailsEvent(id));
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFF10B981),
|
foregroundColor: const Color(0xFF10B981),
|
||||||
@@ -410,7 +523,8 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
builder: (ctx) => AlertDialog(
|
builder: (ctx) => AlertDialog(
|
||||||
title: const Text('Decline Shift'),
|
title: const Text('Decline Shift'),
|
||||||
content: const Text(
|
content: const Text(
|
||||||
'Are you sure you want to decline this shift? It will be hidden from your available jobs.'),
|
'Are you sure you want to decline this shift? It will be hidden from your available jobs.',
|
||||||
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(ctx).pop(),
|
onPressed: () => Navigator.of(ctx).pop(),
|
||||||
@@ -419,7 +533,9 @@ class ShiftDetailsPage extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(ctx).pop();
|
Navigator.of(ctx).pop();
|
||||||
BlocProvider.of<ShiftDetailsBloc>(context).add(DeclineShiftDetailsEvent(id));
|
BlocProvider.of<ShiftDetailsBloc>(
|
||||||
|
context,
|
||||||
|
).add(DeclineShiftDetailsEvent(id));
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
foregroundColor: const Color(0xFFEF4444),
|
foregroundColor: const Color(0xFFEF4444),
|
||||||
|
|||||||
Reference in New Issue
Block a user