feat: Add Headline 1 Bold style and refactor ShiftDetailsPage and FindShiftsTab layout

This commit is contained in:
Achintha Isuru
2026-02-16 12:58:05 -05:00
parent b3b84b6293
commit e6b512ee84
8 changed files with 151 additions and 146 deletions

View File

@@ -173,6 +173,14 @@ class UiTypography {
color: UiColors.textPrimary,
);
/// Headline 1 Bold - Font: Instrument Sans, Size: 26, Height: 1.5 (#121826)
static final TextStyle headline1b = _primaryBase.copyWith(
fontWeight: FontWeight.w600,
fontSize: 26,
height: 1.5,
color: UiColors.textPrimary,
);
/// Headline 2 Medium - Font: Instrument Sans, Size: 20, Height: 1.5 (#121826)
static final TextStyle headline2m = _primaryBase.copyWith(
fontWeight: FontWeight.w500,

View File

@@ -84,13 +84,6 @@ class _ShiftCardState extends State<ShiftCard> {
color: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(color: UiColors.border),
boxShadow: [
BoxShadow(
color: UiColors.black.withValues(alpha: 0.05),
blurRadius: 2,
offset: const Offset(0, 1),
),
],
),
child: Row(
children: [

View File

@@ -14,9 +14,13 @@ import '../widgets/shift_location_map.dart';
class ShiftDetailsPage extends StatefulWidget {
final String shiftId;
final Shift? shift;
final Shift shift;
const ShiftDetailsPage({super.key, required this.shiftId, this.shift});
const ShiftDetailsPage({
super.key,
required this.shiftId,
required this.shift,
});
@override
State<ShiftDetailsPage> createState() => _ShiftDetailsPageState();
@@ -86,12 +90,11 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
const SizedBox(height: UiConstants.space2),
Text(
value,
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
),
Text(
label,
style: UiTypography.footnote2r.textSecondary,
style: UiTypography.title1m
.copyWith(fontWeight: FontWeight.w700)
.textPrimary,
),
Text(label, style: UiTypography.footnote2r.textSecondary),
],
),
);
@@ -109,12 +112,16 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Text(
label,
style: UiTypography.footnote2b.copyWith(
color: UiColors.textSecondary, letterSpacing: 0.5),
color: UiColors.textSecondary,
letterSpacing: 0.5,
),
),
const SizedBox(height: UiConstants.space1),
Text(
_formatTime(time),
style: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary,
style: UiTypography.title1m
.copyWith(fontWeight: FontWeight.w700)
.textPrimary,
),
],
),
@@ -124,14 +131,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
@override
Widget build(BuildContext context) {
return BlocProvider<ShiftDetailsBloc>(
create: (_) =>
Modular.get<ShiftDetailsBloc>()
..add(
LoadShiftDetailsEvent(
widget.shiftId,
roleId: widget.shift?.roleId,
),
),
create: (_) => Modular.get<ShiftDetailsBloc>()
..add(
LoadShiftDetailsEvent(widget.shiftId, roleId: widget.shift?.roleId),
),
child: BlocListener<ShiftDetailsBloc, ShiftDetailsState>(
listener: (context, state) {
if (state is ShiftActionSuccess || state is ShiftDetailsError) {
@@ -164,30 +167,16 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
);
}
Shift? displayShift;
if (state is ShiftDetailsLoaded) {
displayShift = state.shift;
} else {
displayShift = widget.shift;
}
Shift? displayShift = widget.shift;
final i18n = Translations.of(context).staff_shifts.shift_details;
if (displayShift == null) {
return Scaffold(
body: Center(child: Text(Translations.of(context).staff_shifts.list.no_shifts)),
);
}
final duration = _calculateDuration(displayShift);
final estimatedTotal =
displayShift.totalValue ?? (displayShift.hourlyRate * duration);
final openSlots =
(displayShift.requiredSlots ?? 0) -
(displayShift.filledSlots ?? 0);
return Scaffold(
appBar: UiAppBar(
title: displayShift.title,
centerTitle: false,
onLeadingPressed: () => Modular.to.toShifts(),
),
@@ -199,44 +188,49 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Vendor Section
Column(
// Role & Client Section
Row(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: UiConstants.space4,
children: [
Text(
i18n.vendor,
style: UiTypography.titleUppercase4b.textSecondary,
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: UiColors.background,
borderRadius: BorderRadius.circular(
UiConstants.radiusBase,
),
border: Border.all(color: UiColors.border),
),
child: const Center(
child: Icon(
UiIcons.briefcase,
color: UiColors.primary,
size: 24,
),
),
),
const SizedBox(height: UiConstants.space2),
Row(
children: [
SizedBox(
width: 24,
height: 24,
child: displayShift.logoUrl != null
? ClipRRect(
borderRadius: BorderRadius.circular(
UiConstants.radiusMdValue,
),
child: Image.network(
displayShift.logoUrl!,
fit: BoxFit.cover,
),
)
: const Center(
child: Icon(
UiIcons.briefcase,
color: UiColors.primary,
size: 20,
),
),
),
const SizedBox(width: UiConstants.space2),
Text(
displayShift.clientName,
style: UiTypography.headline5m.textPrimary,
),
],
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
displayShift.title,
style:
UiTypography.headline1b.textPrimary,
),
Text(
displayShift.clientName,
style: UiTypography.body1m.textSecondary,
),
Text(
displayShift.locationAddress,
style: UiTypography.body2r.textSecondary,
),
],
),
),
],
),
@@ -248,7 +242,8 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
children: [
Text(
i18n.shift_date,
style: UiTypography.titleUppercase4b.textSecondary,
style:
UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Row(
@@ -276,7 +271,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
child: _buildStatCard(
UiIcons.dollar,
"\$${estimatedTotal.toStringAsFixed(0)}",
"Total",
"Total",
),
),
const SizedBox(width: UiConstants.space4),
@@ -291,7 +286,7 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
Expanded(
child: _buildStatCard(
UiIcons.clock,
"${duration.toStringAsFixed(1)}",
"${duration.toStringAsFixed(1)}",
"Hours",
),
),
@@ -319,14 +314,14 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
),
const SizedBox(height: UiConstants.space6),
// Location Section (New with Map)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"LOCATION",
style: UiTypography.titleUppercase4b.textSecondary,
style:
UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space3),
Row(
@@ -350,13 +345,13 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
).showSnackBar(
SnackBar(
content: Text(
displayShift!.locationAddress.isNotEmpty
? displayShift!.locationAddress
: displayShift!.location,
),
duration: const Duration(
seconds: 3,
displayShift!
.locationAddress
.isNotEmpty
? displayShift!.locationAddress
: displayShift!.location,
),
duration: const Duration(seconds: 3),
),
);
},
@@ -364,12 +359,9 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
UiIcons.navigation,
size: UiConstants.iconXs,
),
label: const Text(
"Get direction",
),
label: const Text("Get direction"),
style: OutlinedButton.styleFrom(
foregroundColor:
UiColors.textPrimary,
foregroundColor: UiColors.textPrimary,
side: const BorderSide(
color: UiColors.border,
),
@@ -401,7 +393,8 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
if ((displayShift.description ?? '').isNotEmpty) ...[
Text(
i18n.job_description,
style: UiTypography.titleUppercase4b.textSecondary,
style:
UiTypography.titleUppercase4b.textSecondary,
),
const SizedBox(height: UiConstants.space2),
Text(
@@ -420,7 +413,8 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
UiConstants.space5,
UiConstants.space4,
UiConstants.space5,
MediaQuery.of(context).padding.bottom + UiConstants.space4,
MediaQuery.of(context).padding.bottom +
UiConstants.space4,
),
decoration: BoxDecoration(
color: UiColors.white,
@@ -444,11 +438,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
);
}
void _bookShift(
BuildContext context,
Shift shift,
) {
final i18n = Translations.of(context).staff_shifts.shift_details.book_dialog;
void _bookShift(BuildContext context, Shift shift) {
final i18n = Translations.of(
context,
).staff_shifts.shift_details.book_dialog;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
@@ -471,10 +464,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
),
);
},
style: TextButton.styleFrom(
foregroundColor: UiColors.success,
style: TextButton.styleFrom(foregroundColor: UiColors.success),
child: Text(
Translations.of(context).staff_shifts.shift_details.apply_now,
),
child: Text(Translations.of(context).staff_shifts.shift_details.apply_now),
),
],
),
@@ -482,7 +475,9 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
}
void _declineShift(BuildContext context, String id) {
final i18n = Translations.of(context).staff_shifts.shift_details.decline_dialog;
final i18n = Translations.of(
context,
).staff_shifts.shift_details.decline_dialog;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
@@ -499,10 +494,10 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
context,
).add(DeclineShiftDetailsEvent(id));
},
style: TextButton.styleFrom(
foregroundColor: UiColors.destructive,
style: TextButton.styleFrom(foregroundColor: UiColors.destructive),
child: Text(
Translations.of(context).staff_shifts.shift_details.decline,
),
child: Text(Translations.of(context).staff_shifts.shift_details.decline),
),
],
),
@@ -513,7 +508,9 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
if (_actionDialogOpen) return;
_actionDialogOpen = true;
_isApplying = true;
final i18n = Translations.of(context).staff_shifts.shift_details.applying_dialog;
final i18n = Translations.of(
context,
).staff_shifts.shift_details.applying_dialog;
showDialog(
context: context,
useRootNavigator: true,
@@ -590,10 +587,14 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
children: [
Expanded(
child: OutlinedButton(
onPressed: () => BlocProvider.of<ShiftDetailsBloc>(context).add(DeclineShiftDetailsEvent(shift.id)),
onPressed: () => BlocProvider.of<ShiftDetailsBloc>(
context,
).add(DeclineShiftDetailsEvent(shift.id)),
style: OutlinedButton.styleFrom(
foregroundColor: UiColors.destructive,
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
),
side: const BorderSide(color: UiColors.destructive),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
@@ -605,11 +606,15 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
const SizedBox(width: UiConstants.space4),
Expanded(
child: ElevatedButton(
onPressed: () => BlocProvider.of<ShiftDetailsBloc>(context).add(BookShiftDetailsEvent(shift.id, roleId: shift.roleId)),
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),
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
@@ -630,13 +635,18 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
onPressed: () => _declineShift(context, shift.id),
style: OutlinedButton.styleFrom(
foregroundColor: UiColors.textSecondary,
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
),
side: const BorderSide(color: UiColors.border),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
),
child: Text(i18n.decline, style: UiTypography.body2b.textSecondary),
child: Text(
i18n.decline,
style: UiTypography.body2b.textSecondary,
),
),
),
const SizedBox(width: UiConstants.space4),
@@ -646,7 +656,9 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
style: ElevatedButton.styleFrom(
backgroundColor: UiColors.primary,
foregroundColor: UiColors.white,
padding: const EdgeInsets.symmetric(vertical: UiConstants.space4),
padding: const EdgeInsets.symmetric(
vertical: UiConstants.space4,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
),
@@ -661,5 +673,4 @@ class _ShiftDetailsPageState extends State<ShiftDetailsPage> {
return const SizedBox();
}
}

View File

@@ -29,7 +29,6 @@ class _ShiftsPageState extends State<ShiftsPage> {
super.initState();
_activeTab = widget.initialTab ?? 'myshifts';
_selectedDate = widget.selectedDate;
print('ShiftsPage init: initialTab=$_activeTab');
_prioritizeFind = widget.initialTab == 'find';
if (_prioritizeFind) {
_bloc.add(LoadFindFirstEvent());
@@ -37,11 +36,9 @@ class _ShiftsPageState extends State<ShiftsPage> {
_bloc.add(LoadShiftsEvent());
}
if (_activeTab == 'history') {
print('ShiftsPage init: loading history tab');
_bloc.add(LoadHistoryShiftsEvent());
}
if (_activeTab == 'find') {
print('ShiftsPage init: entering find tab (not loaded yet)');
if (!_prioritizeFind) {
_bloc.add(LoadAvailableShiftsEvent());
}

View File

@@ -1,13 +0,0 @@
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
class AppColors {
static const Color krowBlue = UiColors.primary;
static const Color krowYellow = UiColors.accent;
static const Color krowCharcoal = UiColors.textPrimary;
static const Color krowMuted = UiColors.textSecondary;
static const Color krowBorder = UiColors.border;
static const Color krowBackground = UiColors.background;
static const Color white = UiColors.white;
static const Color black = UiColors.black;
}

View File

@@ -1,6 +1,5 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import '../../styles/shifts_styles.dart';
class EmptyStateView extends StatelessWidget {
final IconData icon;

View File

@@ -10,10 +10,7 @@ import '../shared/empty_state_view.dart';
class FindShiftsTab extends StatefulWidget {
final List<Shift> availableJobs;
const FindShiftsTab({
super.key,
required this.availableJobs,
});
const FindShiftsTab({super.key, required this.availableJobs});
@override
State<FindShiftsTab> createState() => _FindShiftsTabState();
@@ -42,7 +39,9 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
child: Text(
label,
textAlign: TextAlign.center,
style: (isSelected ? UiTypography.footnote2m.white : UiTypography.footnote2m.textSecondary),
style: (isSelected
? UiTypography.footnote2m.white
: UiTypography.footnote2m.textSecondary),
),
),
);
@@ -86,13 +85,15 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
Expanded(
child: Container(
height: 48,
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space3,
),
decoration: BoxDecoration(
color: UiColors.background,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: UiColors.border,
borderRadius: BorderRadius.circular(
UiConstants.radiusBase,
),
border: Border.all(color: UiColors.border),
),
child: Row(
children: [
@@ -123,10 +124,10 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
width: 48,
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: BorderRadius.circular(UiConstants.radiusBase),
border: Border.all(
color: UiColors.border,
borderRadius: BorderRadius.circular(
UiConstants.radiusBase,
),
border: Border.all(color: UiColors.border),
),
child: const Icon(
UiIcons.filter,
@@ -164,20 +165,27 @@ class _FindShiftsTabState extends State<FindShiftsTab> {
subtitle: "Check back later",
)
: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space5),
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space5,
),
child: Column(
children: [
const SizedBox(height: UiConstants.space5),
...filteredJobs.map(
(shift) => Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space3),
padding: const EdgeInsets.only(
bottom: UiConstants.space3,
),
child: MyShiftCard(
shift: shift,
onAccept: () {
context.read<ShiftsBloc>().add(AcceptShiftEvent(shift.id));
context.read<ShiftsBloc>().add(
AcceptShiftEvent(shift.id),
);
UiSnackbar.show(
context,
message: "Shift application submitted!", // Todo: Localization
message:
"Shift application submitted!", // Todo: Localization
type: UiSnackbarType.success,
);
},

View File

@@ -27,6 +27,8 @@ dependencies:
path: ../../../data_connect
core_localization:
path: ../../../core_localization
firebase_auth: ^6.1.4
firebase_data_connect: ^0.2.2+2
dev_dependencies:
flutter_test: