Refactor: Move detailed shift UI from card to ShiftDetailsPage
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.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';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_modular/flutter_modular.dart';
|
import 'package:flutter_modular/flutter_modular.dart';
|
||||||
@@ -10,6 +10,7 @@ import 'package:krow_domain/krow_domain.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 '../widgets/shift_location_map.dart';
|
||||||
|
|
||||||
class ShiftDetailsPage extends StatefulWidget {
|
class ShiftDetailsPage extends StatefulWidget {
|
||||||
final String shiftId;
|
final String shiftId;
|
||||||
@@ -65,10 +66,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
|
|
||||||
Widget _buildStatCard(IconData icon, String value, String label) {
|
Widget _buildStatCard(IconData icon, String value, String label) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
|
padding: const EdgeInsets.symmetric(vertical: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.background,
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
border: Border.all(color: UiColors.border),
|
border: Border.all(color: UiColors.border),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -80,12 +81,12 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Icon(icon, size: 20, color: UiColors.iconSecondary),
|
child: Icon(icon, size: 20, color: UiColors.textSecondary),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space2),
|
const SizedBox(height: UiConstants.space2),
|
||||||
Text(
|
Text(
|
||||||
value,
|
value,
|
||||||
style: UiTypography.title1m.textPrimary,
|
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
@@ -98,21 +99,22 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
|
|
||||||
Widget _buildTimeBox(String label, String time) {
|
Widget _buildTimeBox(String label, String time) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.all(UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.background,
|
color: UiColors.background,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: UiTypography.titleUppercase4b.textSecondary,
|
style: UiTypography.footnote2b.copyWith(
|
||||||
|
color: UiColors.textSecondary, letterSpacing: 0.5),
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space1),
|
const SizedBox(height: UiConstants.space1),
|
||||||
Text(
|
Text(
|
||||||
_formatTime(time),
|
_formatTime(time),
|
||||||
style: UiTypography.headline2m.textPrimary,
|
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -267,45 +269,49 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Worker Capacity / Open Slots
|
// Stats Row (New)
|
||||||
if ((displayShift.requiredSlots ?? 0) > 0)
|
Row(
|
||||||
Container(
|
children: [
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
Expanded(
|
||||||
decoration: BoxDecoration(
|
child: _buildStatCard(
|
||||||
color: UiColors.success.withValues(alpha: 0.1),
|
UiIcons.dollar,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
"\$${estimatedTotal.toStringAsFixed(0)}",
|
||||||
|
"Total",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Row(
|
const SizedBox(width: UiConstants.space4),
|
||||||
children: [
|
Expanded(
|
||||||
const Icon(
|
child: _buildStatCard(
|
||||||
UiIcons.users,
|
UiIcons.dollar,
|
||||||
size: 16,
|
"\$${displayShift.hourlyRate.toStringAsFixed(0)}",
|
||||||
color: UiColors.success,
|
"Hourly Rate",
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space2),
|
|
||||||
Text(
|
|
||||||
i18n.slots_remaining(count: openSlots),
|
|
||||||
style: UiTypography.footnote1m.textSuccess,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(width: UiConstants.space4),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatCard(
|
||||||
|
UiIcons.clock,
|
||||||
|
"${duration.toStringAsFixed(1)}",
|
||||||
|
"Hours",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Time Section
|
// Time Section (New)
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildTimeBox(
|
child: _buildTimeBox(
|
||||||
i18n.start_time,
|
"CLOCK IN TIME",
|
||||||
displayShift.startTime,
|
displayShift.startTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: UiConstants.space4),
|
const SizedBox(width: UiConstants.space4),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildTimeBox(
|
child: _buildTimeBox(
|
||||||
i18n.end_time,
|
"CLOCK OUT TIME",
|
||||||
displayShift.endTime,
|
displayShift.endTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -313,97 +319,79 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space6),
|
const SizedBox(height: UiConstants.space6),
|
||||||
|
|
||||||
// Quick Info Grid
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: _buildStatCard(
|
|
||||||
UiIcons.dollar,
|
|
||||||
"\$${displayShift.hourlyRate.toStringAsFixed(0)}/hr",
|
|
||||||
i18n.base_rate,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space4),
|
|
||||||
Expanded(
|
|
||||||
child: _buildStatCard(
|
|
||||||
UiIcons.clock,
|
|
||||||
i18n.hours_label(count: duration.toInt()),
|
|
||||||
i18n.duration,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space4),
|
|
||||||
Expanded(
|
|
||||||
child: _buildStatCard(
|
|
||||||
UiIcons.wallet,
|
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
|
||||||
i18n.est_total,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space8),
|
|
||||||
|
|
||||||
// Location Section
|
// Location Section (New with Map)
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
i18n.location,
|
"LOCATION",
|
||||||
style: UiTypography.titleUppercase4b.textSecondary,
|
style: UiTypography.titleUppercase4b.textSecondary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space3),
|
const SizedBox(height: UiConstants.space3),
|
||||||
Container(
|
Row(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
mainAxisAlignment:
|
||||||
decoration: BoxDecoration(
|
MainAxisAlignment.spaceBetween,
|
||||||
color: UiColors.white,
|
children: [
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
Expanded(
|
||||||
border: Border.all(color: UiColors.border),
|
child: Text(
|
||||||
),
|
displayShift.location.isEmpty
|
||||||
child: Column(
|
? "TBD"
|
||||||
children: [
|
: displayShift.location,
|
||||||
Row(
|
style: UiTypography.title1m.textPrimary,
|
||||||
children: [
|
overflow: TextOverflow.ellipsis,
|
||||||
const Icon(
|
),
|
||||||
UiIcons.mapPin,
|
),
|
||||||
color: UiColors.primary,
|
const SizedBox(width: UiConstants.space3),
|
||||||
size: 20,
|
OutlinedButton.icon(
|
||||||
),
|
onPressed: () {
|
||||||
const SizedBox(width: UiConstants.space3),
|
ScaffoldMessenger.of(
|
||||||
Expanded(
|
context,
|
||||||
child: Column(
|
).showSnackBar(
|
||||||
crossAxisAlignment:
|
SnackBar(
|
||||||
CrossAxisAlignment.start,
|
content: Text(
|
||||||
children: [
|
displayShift!.locationAddress.isNotEmpty
|
||||||
Text(
|
? displayShift!.locationAddress
|
||||||
displayShift.location,
|
: displayShift!.location,
|
||||||
style: UiTypography.body2b.textPrimary,
|
),
|
||||||
),
|
duration: const Duration(
|
||||||
Text(
|
seconds: 3,
|
||||||
displayShift.locationAddress,
|
|
||||||
style: UiTypography.body3r.textSecondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
UiIcons.navigation,
|
||||||
|
size: UiConstants.iconXs,
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space4),
|
label: const Text(
|
||||||
const Divider(),
|
"Get direction",
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
TextButton.icon(
|
|
||||||
onPressed: () {},
|
|
||||||
icon: const Icon(
|
|
||||||
UiIcons.arrowRight,
|
|
||||||
size: 16,
|
|
||||||
),
|
|
||||||
label: Text(i18n.open_in_maps),
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
foregroundColor: UiColors.primary,
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
style: OutlinedButton.styleFrom(
|
||||||
),
|
foregroundColor:
|
||||||
|
UiColors.textPrimary,
|
||||||
|
side: const BorderSide(
|
||||||
|
color: UiColors.border,
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(
|
||||||
|
UiConstants.radiusBase,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: UiConstants.space3,
|
||||||
|
vertical: 0,
|
||||||
|
),
|
||||||
|
minimumSize: const Size(0, 32),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space3),
|
||||||
|
ShiftLocationMap(
|
||||||
|
shift: displayShift,
|
||||||
|
height: 160,
|
||||||
|
borderRadius: UiConstants.radiusBase,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'package:intl/intl.dart';
|
|||||||
import 'package:krow_domain/krow_domain.dart';
|
import 'package:krow_domain/krow_domain.dart';
|
||||||
import 'package:design_system/design_system.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:core_localization/core_localization.dart';
|
import 'package:core_localization/core_localization.dart';
|
||||||
import 'shift_location_map.dart';
|
|
||||||
import 'package:krow_core/core.dart'; // For modular navigation
|
import 'package:krow_core/core.dart'; // For modular navigation
|
||||||
|
|
||||||
class MyShiftCard extends StatefulWidget {
|
class MyShiftCard extends StatefulWidget {
|
||||||
@@ -27,8 +26,7 @@ class MyShiftCard extends StatefulWidget {
|
|||||||
State<MyShiftCard> createState() => _MyShiftCardState();
|
State<MyShiftCard> createState() => _MyShiftCardState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyShiftCardState extends State<MyShiftCard> with TickerProviderStateMixin {
|
class _MyShiftCardState extends State<MyShiftCard> {
|
||||||
bool _isExpanded = false;
|
|
||||||
|
|
||||||
String _formatTime(String time) {
|
String _formatTime(String time) {
|
||||||
if (time.isEmpty) return '';
|
if (time.isEmpty) return '';
|
||||||
@@ -104,7 +102,6 @@ class _MyShiftCardState extends State<MyShiftCard> with TickerProviderStateMixin
|
|||||||
IconData? statusIcon;
|
IconData? statusIcon;
|
||||||
|
|
||||||
// Fallback localization if keys missing
|
// Fallback localization if keys missing
|
||||||
// Assuming t.staff_shifts.status.* exists as per previous file content
|
|
||||||
try {
|
try {
|
||||||
if (status == 'confirmed') {
|
if (status == 'confirmed') {
|
||||||
statusText = t.staff_shifts.status.confirmed;
|
statusText = t.staff_shifts.status.confirmed;
|
||||||
@@ -137,9 +134,13 @@ class _MyShiftCardState extends State<MyShiftCard> with TickerProviderStateMixin
|
|||||||
}
|
}
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () => setState(() => _isExpanded = !_isExpanded),
|
onTap: () {
|
||||||
child: AnimatedContainer(
|
Modular.to.pushNamed(
|
||||||
duration: const Duration(milliseconds: 300),
|
StaffPaths.shiftDetails(widget.shift.id),
|
||||||
|
arguments: widget.shift,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
margin: const EdgeInsets.only(bottom: UiConstants.space3),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: UiColors.white,
|
color: UiColors.white,
|
||||||
@@ -153,582 +154,246 @@ class _MyShiftCardState extends State<MyShiftCard> with TickerProviderStateMixin
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
child: Column(
|
child: Padding(
|
||||||
children: [
|
padding: const EdgeInsets.all(UiConstants.space4),
|
||||||
// Collapsed Content
|
child: Column(
|
||||||
Padding(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
children: [
|
||||||
child: Column(
|
// Status Badge
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
if (statusText.isNotEmpty)
|
||||||
children: [
|
Padding(
|
||||||
// Status Badge
|
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
||||||
if (statusText.isNotEmpty)
|
child: Row(
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: UiConstants.space2),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
if (statusIcon != null)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(right: UiConstants.space2),
|
|
||||||
child: Icon(
|
|
||||||
statusIcon,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
color: statusColor,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Container(
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
margin: const EdgeInsets.only(right: UiConstants.space2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: statusBg,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
statusText,
|
|
||||||
style: UiTypography.footnote2b.copyWith(
|
|
||||||
color: statusColor,
|
|
||||||
letterSpacing: 0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Shift Type Badge for available/pending shifts
|
|
||||||
if (status == 'open' || status == 'pending') ...[
|
|
||||||
const SizedBox(width: UiConstants.space2),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: UiConstants.space2,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.primary.withValues(alpha: 0.1),
|
|
||||||
borderRadius: UiConstants.radiusSm,
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
_getShiftType(),
|
|
||||||
style: UiTypography.footnote2m.copyWith(
|
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
children: [
|
||||||
// Logo
|
if (statusIcon != null)
|
||||||
Container(
|
|
||||||
width: 44,
|
|
||||||
height: 44,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
UiColors.primary.withValues(alpha: 0.09),
|
|
||||||
UiColors.primary.withValues(alpha: 0.03),
|
|
||||||
],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
border: Border.all(
|
|
||||||
color: UiColors.primary.withValues(alpha: 0.09),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: widget.shift.logoUrl != null
|
|
||||||
? ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
child: Image.network(
|
|
||||||
widget.shift.logoUrl!,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: const Center(
|
|
||||||
child: Icon(
|
|
||||||
UiIcons.briefcase,
|
|
||||||
color: UiColors.primary,
|
|
||||||
size: UiConstants.iconMd,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
|
|
||||||
// Details
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
widget.shift.title,
|
|
||||||
style: UiTypography.body2m.textPrimary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
widget.shift.clientName,
|
|
||||||
style: UiTypography.body3r.textSecondary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space2),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
|
||||||
style: UiTypography.title1m.textPrimary,
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"\$${widget.shift.hourlyRate.toInt()}/hr · ${duration.toInt()}h",
|
|
||||||
style: UiTypography.footnote2r.textSecondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
|
|
||||||
// Date & Time - Multi-Day or Single Day
|
|
||||||
if (widget.shift.durationDays != null &&
|
|
||||||
widget.shift.durationDays! > 1) ...[
|
|
||||||
// Multi-Day Schedule Display
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
UiIcons.clock,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space1),
|
|
||||||
Text(
|
|
||||||
t.staff_shifts.details.days(
|
|
||||||
days: widget.shift.durationDays!,
|
|
||||||
),
|
|
||||||
style: UiTypography.footnote2m.copyWith(
|
|
||||||
color: UiColors.primary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space1),
|
|
||||||
// Mock loop for demo purposes, as we don't have all schedule dates in the model
|
|
||||||
// In real app, we might need to fetch schedule or iterate if model changes
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 2),
|
|
||||||
child: Text(
|
|
||||||
'${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)} – ${_formatTime(widget.shift.endTime)}',
|
|
||||||
style: UiTypography.footnote2r.copyWith(color: UiColors.primary),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (widget.shift.durationDays! > 1)
|
|
||||||
Text(
|
|
||||||
'... +${widget.shift.durationDays! - 1} more days',
|
|
||||||
style: UiTypography.footnote2r.copyWith(color: UiColors.primary.withOpacity(0.7)),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
] else ...[
|
|
||||||
// Single Day Display
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
UiIcons.calendar,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
color: UiColors.iconSecondary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space1),
|
|
||||||
Text(
|
|
||||||
_formatDate(widget.shift.date),
|
|
||||||
style: UiTypography.footnote1r.textSecondary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
const Icon(
|
|
||||||
UiIcons.clock,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
color: UiColors.iconSecondary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space1),
|
|
||||||
Text(
|
|
||||||
"${_formatTime(widget.shift.startTime)} - ${_formatTime(widget.shift.endTime)}",
|
|
||||||
style: UiTypography.footnote1r.textSecondary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
const SizedBox(height: UiConstants.space1),
|
|
||||||
|
|
||||||
// Location
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
UiIcons.mapPin,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
color: UiColors.iconSecondary,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space1),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
widget.shift.locationAddress.isNotEmpty
|
|
||||||
? widget.shift.locationAddress
|
|
||||||
: widget.shift.location,
|
|
||||||
style: UiTypography.footnote1r.textSecondary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Expanded Content
|
|
||||||
AnimatedSize(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: _isExpanded
|
|
||||||
? Column(
|
|
||||||
children: [
|
|
||||||
const Divider(height: 1, color: UiColors.border),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(UiConstants.space4),
|
padding: const EdgeInsets.only(right: UiConstants.space2),
|
||||||
child: Column(
|
child: Icon(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
statusIcon,
|
||||||
children: [
|
size: UiConstants.iconXs,
|
||||||
// Stats Row
|
color: statusColor,
|
||||||
Row(
|
),
|
||||||
children: [
|
)
|
||||||
Expanded(
|
else
|
||||||
child: _buildStatCard(
|
Container(
|
||||||
UiIcons.dollar,
|
width: 8,
|
||||||
"\$${estimatedTotal.toStringAsFixed(0)}",
|
height: 8,
|
||||||
"Total",
|
margin: const EdgeInsets.only(right: UiConstants.space2),
|
||||||
),
|
decoration: BoxDecoration(
|
||||||
),
|
color: statusBg,
|
||||||
const SizedBox(width: UiConstants.space3),
|
shape: BoxShape.circle,
|
||||||
Expanded(
|
),
|
||||||
child: _buildStatCard(
|
),
|
||||||
UiIcons.dollar,
|
Text(
|
||||||
"\$${widget.shift.hourlyRate}",
|
statusText,
|
||||||
"Hourly Rate",
|
style: UiTypography.footnote2b.copyWith(
|
||||||
),
|
color: statusColor,
|
||||||
),
|
letterSpacing: 0.5,
|
||||||
const SizedBox(width: UiConstants.space3),
|
),
|
||||||
Expanded(
|
),
|
||||||
child: _buildStatCard(
|
// Shift Type Badge
|
||||||
UiIcons.clock,
|
if (status == 'open' || status == 'pending') ...[
|
||||||
"${duration}",
|
const SizedBox(width: UiConstants.space2),
|
||||||
"Hours",
|
Container(
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: UiConstants.space2,
|
||||||
],
|
vertical: 2,
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space5),
|
decoration: BoxDecoration(
|
||||||
|
color: UiColors.primary.withValues(alpha: 0.1),
|
||||||
// In/Out Time
|
borderRadius: UiConstants.radiusSm,
|
||||||
Row(
|
),
|
||||||
children: [
|
child: Text(
|
||||||
Expanded(
|
_getShiftType(),
|
||||||
child: _buildTimeBox(
|
style: UiTypography.footnote2m.copyWith(
|
||||||
"CLOCK IN TIME",
|
color: UiColors.primary,
|
||||||
widget.shift.startTime,
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
Expanded(
|
|
||||||
child: _buildTimeBox(
|
|
||||||
"CLOCK OUT TIME",
|
|
||||||
widget.shift.endTime,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space5),
|
|
||||||
|
|
||||||
// Location
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"LOCATION",
|
|
||||||
style: UiTypography.footnote2b.copyWith(
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
widget.shift.location.isEmpty
|
|
||||||
? "TBD"
|
|
||||||
: widget.shift.location,
|
|
||||||
style: UiTypography.title1m.textPrimary,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space3),
|
|
||||||
OutlinedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
ScaffoldMessenger.of(
|
|
||||||
context,
|
|
||||||
).showSnackBar(
|
|
||||||
SnackBar(
|
|
||||||
content: Text(
|
|
||||||
widget.shift.locationAddress ??
|
|
||||||
widget.shift.location,
|
|
||||||
),
|
|
||||||
duration: const Duration(
|
|
||||||
seconds: 3,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
UiIcons.navigation,
|
|
||||||
size: UiConstants.iconXs,
|
|
||||||
),
|
|
||||||
label: const Text(
|
|
||||||
"Get direction",
|
|
||||||
),
|
|
||||||
style: OutlinedButton.styleFrom(
|
|
||||||
foregroundColor:
|
|
||||||
UiColors.textPrimary,
|
|
||||||
side: const BorderSide(
|
|
||||||
color: UiColors.border,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(
|
|
||||||
UiConstants.radiusBase,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: UiConstants.space3,
|
|
||||||
vertical: 0,
|
|
||||||
),
|
|
||||||
minimumSize: const Size(0, 32),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space3),
|
|
||||||
ShiftLocationMap(
|
|
||||||
shift: widget.shift,
|
|
||||||
height: 128,
|
|
||||||
borderRadius: UiConstants.radiusBase,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space5),
|
|
||||||
|
|
||||||
// Additional Info
|
|
||||||
if (widget.shift.description != null) ...[
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment:
|
|
||||||
CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"ADDITIONAL INFO",
|
|
||||||
style: UiTypography.footnote2b.copyWith(
|
|
||||||
color: UiColors.textSecondary,
|
|
||||||
letterSpacing: 0.5),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space2),
|
|
||||||
Text(
|
|
||||||
widget.shift.description!,
|
|
||||||
style: UiTypography.body2m.textPrimary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: UiConstants.space5),
|
|
||||||
],
|
|
||||||
|
|
||||||
// Actions
|
|
||||||
if (!widget.historyMode)
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: UiConstants.space2),
|
|
||||||
child: _buildActions(status),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
],
|
||||||
: const SizedBox.shrink(),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildActions(String? status) {
|
Row(
|
||||||
if (status == 'confirmed') {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
return SizedBox(
|
children: [
|
||||||
width: double.infinity,
|
// Logo
|
||||||
height: 48,
|
Container(
|
||||||
child: OutlinedButton.icon(
|
width: 44,
|
||||||
onPressed: widget.onRequestSwap,
|
height: 44,
|
||||||
icon: const Icon(
|
decoration: BoxDecoration(
|
||||||
UiIcons.swap,
|
gradient: LinearGradient(
|
||||||
size: UiConstants.iconSm,
|
colors: [
|
||||||
),
|
UiColors.primary.withValues(alpha: 0.09),
|
||||||
label: const Text("Request Swap"),
|
UiColors.primary.withValues(alpha: 0.03),
|
||||||
style: OutlinedButton.styleFrom(
|
],
|
||||||
foregroundColor: UiColors.primary,
|
begin: Alignment.topLeft,
|
||||||
side: const BorderSide(
|
end: Alignment.bottomRight,
|
||||||
color: UiColors.primary,
|
),
|
||||||
),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
shape: RoundedRectangleBorder(
|
border: Border.all(
|
||||||
borderRadius: BorderRadius.circular(
|
color: UiColors.primary.withValues(alpha: 0.09),
|
||||||
UiConstants.radiusBase,
|
),
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (status == 'swap') {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 48,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: UiColors.tagPending,
|
|
||||||
border: Border.all(
|
|
||||||
color: UiColors.textWarning,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
UiIcons.swap,
|
|
||||||
size: UiConstants.iconSm,
|
|
||||||
color: UiColors.textWarning,
|
|
||||||
),
|
|
||||||
const SizedBox(width: UiConstants.space2),
|
|
||||||
Text(
|
|
||||||
"Swap Pending",
|
|
||||||
style: UiTypography.body2b.copyWith(
|
|
||||||
color: UiColors.textWarning,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// status == 'open' || status == 'pending' or others
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 48,
|
|
||||||
child: ElevatedButton(
|
|
||||||
onPressed: widget.onAccept,
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: UiColors.primary,
|
|
||||||
foregroundColor: UiColors.white,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.circular(UiConstants.radiusBase),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: widget.onAccept == null
|
|
||||||
? const SizedBox(
|
|
||||||
height: 20,
|
|
||||||
width: 20,
|
|
||||||
child: CircularProgressIndicator(color: Colors.white, strokeWidth: 2)
|
|
||||||
) // Loading state if callback null? or just Text
|
|
||||||
: const Text(
|
|
||||||
"Book Shift",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
),
|
||||||
),
|
child: widget.shift.logoUrl != null
|
||||||
),
|
? ClipRRect(
|
||||||
),
|
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
|
||||||
],
|
child: Image.network(
|
||||||
);
|
widget.shift.logoUrl!,
|
||||||
}
|
fit: BoxFit.contain,
|
||||||
}
|
),
|
||||||
|
)
|
||||||
|
: const Center(
|
||||||
|
child: Icon(
|
||||||
|
UiIcons.briefcase,
|
||||||
|
color: UiColors.primary,
|
||||||
|
size: UiConstants.iconMd,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
|
||||||
Widget _buildStatCard(IconData icon, String value, String label) {
|
// Consensed Details
|
||||||
return Container(
|
Expanded(
|
||||||
padding: const EdgeInsets.symmetric(vertical: UiConstants.space3),
|
child: Column(
|
||||||
decoration: BoxDecoration(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
color: UiColors.background,
|
children: [
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
Row(
|
||||||
border: Border.all(color: UiColors.border),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
),
|
children: [
|
||||||
child: Column(
|
Expanded(
|
||||||
children: [
|
child: Column(
|
||||||
Container(
|
crossAxisAlignment:
|
||||||
width: 40,
|
CrossAxisAlignment.start,
|
||||||
height: 40,
|
children: [
|
||||||
decoration: const BoxDecoration(
|
Text(
|
||||||
color: UiColors.white,
|
widget.shift.title,
|
||||||
shape: BoxShape.circle,
|
style: UiTypography.body2m.textPrimary,
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
child: Icon(icon, size: 20, color: UiColors.textSecondary),
|
),
|
||||||
),
|
Text(
|
||||||
const SizedBox(height: UiConstants.space2),
|
widget.shift.clientName,
|
||||||
Text(
|
style: UiTypography.body3r.textSecondary,
|
||||||
value,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
|
),
|
||||||
),
|
],
|
||||||
Text(
|
),
|
||||||
label,
|
),
|
||||||
style: UiTypography.footnote2r.textSecondary,
|
const SizedBox(width: UiConstants.space2),
|
||||||
),
|
Column(
|
||||||
],
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
),
|
children: [
|
||||||
);
|
Text(
|
||||||
}
|
"\$${estimatedTotal.toStringAsFixed(0)}",
|
||||||
|
style: UiTypography.title1m.textPrimary,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
"\$${widget.shift.hourlyRate.toInt()}/hr · ${duration.toInt()}h",
|
||||||
|
style: UiTypography.footnote2r.textSecondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space2),
|
||||||
|
|
||||||
Widget _buildTimeBox(String label, String time) {
|
// Date & Time
|
||||||
return Container(
|
if (widget.shift.durationDays != null &&
|
||||||
padding: const EdgeInsets.all(UiConstants.space3),
|
widget.shift.durationDays! > 1) ...[
|
||||||
decoration: BoxDecoration(
|
Column(
|
||||||
color: UiColors.background,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
borderRadius: BorderRadius.circular(UiConstants.radiusMdValue),
|
children: [
|
||||||
),
|
Row(
|
||||||
child: Column(
|
children: [
|
||||||
children: [
|
const Icon(
|
||||||
Text(
|
UiIcons.clock,
|
||||||
label,
|
size: UiConstants.iconXs,
|
||||||
style: UiTypography.footnote2b.copyWith(
|
color: UiColors.primary,
|
||||||
color: UiColors.textSecondary, letterSpacing: 0.5),
|
),
|
||||||
|
const SizedBox(width: UiConstants.space1),
|
||||||
|
Text(
|
||||||
|
t.staff_shifts.details.days(
|
||||||
|
days: widget.shift.durationDays!,
|
||||||
|
),
|
||||||
|
style: UiTypography.footnote2m.copyWith(
|
||||||
|
color: UiColors.primary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: UiConstants.space1),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 2),
|
||||||
|
child: Text(
|
||||||
|
'${_formatDate(widget.shift.date)}, ${_formatTime(widget.shift.startTime)} – ${_formatTime(widget.shift.endTime)}',
|
||||||
|
style: UiTypography.footnote2r.copyWith(color: UiColors.primary),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (widget.shift.durationDays! > 1)
|
||||||
|
Text(
|
||||||
|
'... +${widget.shift.durationDays! - 1} more days',
|
||||||
|
style: UiTypography.footnote2r.copyWith(color: UiColors.primary.withOpacity(0.7)),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
] else ...[
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
UiIcons.calendar,
|
||||||
|
size: UiConstants.iconXs,
|
||||||
|
color: UiColors.iconSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space1),
|
||||||
|
Text(
|
||||||
|
_formatDate(widget.shift.date),
|
||||||
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space3),
|
||||||
|
const Icon(
|
||||||
|
UiIcons.clock,
|
||||||
|
size: UiConstants.iconXs,
|
||||||
|
color: UiColors.iconSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space1),
|
||||||
|
Text(
|
||||||
|
"${_formatTime(widget.shift.startTime)} - ${_formatTime(widget.shift.endTime)}",
|
||||||
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
const SizedBox(height: UiConstants.space1),
|
||||||
|
|
||||||
|
// Location
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
UiIcons.mapPin,
|
||||||
|
size: UiConstants.iconXs,
|
||||||
|
color: UiColors.iconSecondary,
|
||||||
|
),
|
||||||
|
const SizedBox(width: UiConstants.space1),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
widget.shift.locationAddress.isNotEmpty
|
||||||
|
? widget.shift.locationAddress
|
||||||
|
: widget.shift.location,
|
||||||
|
style: UiTypography.footnote1r.textSecondary,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: UiConstants.space1),
|
),
|
||||||
Text(
|
|
||||||
_formatTime(time),
|
|
||||||
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user