feat: Enhance benefits section and layout for improved user experience

This commit is contained in:
Achintha Isuru
2026-03-03 20:47:15 -05:00
parent a7d66a1efe
commit 4474a732c2
8 changed files with 47 additions and 70 deletions

View File

@@ -1,6 +1,8 @@
import 'package:core_localization/core_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart';
import 'package:krow_core/core.dart';
import 'package:staff_home/src/presentation/blocs/home_cubit.dart';
import 'package:staff_home/src/presentation/widgets/home_page/section_layout.dart';
@@ -28,6 +30,8 @@ class BenefitsSection extends StatelessWidget {
return SectionLayout(
title: i18n.title,
action: i18n.view_all,
onAction: () => Modular.to.toBenefits(),
child: BenefitsWidget(benefits: state.benefits),
);
},

View File

@@ -17,7 +17,7 @@ class FullWidthDivider extends StatelessWidget {
children: [
const SizedBox(height: UiConstants.space10),
Transform.translate(
offset: const Offset(UiConstants.space4, 0),
offset: const Offset(-UiConstants.space4, 0),
child: SizedBox(width: screenWidth, child: const Divider()),
),
const SizedBox(height: UiConstants.space10),

View File

@@ -13,13 +13,14 @@ class RecommendedShiftCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
final recI18n = t.staff.home.recommended_card;
final size = MediaQuery.sizeOf(context);
return GestureDetector(
onTap: () {
Modular.to.toShiftDetails(shift);
},
child: Container(
width: 300,
width: size.width * 0.8,
padding: const EdgeInsets.all(UiConstants.space4),
decoration: BoxDecoration(
color: UiColors.white,
@@ -28,11 +29,11 @@ class RecommendedShiftCard extends StatelessWidget {
),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: UiConstants.space10,
@@ -50,7 +51,7 @@ class RecommendedShiftCard extends StatelessWidget {
const SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@@ -21,26 +21,23 @@ class RecommendedShiftsSection extends StatelessWidget {
final t = Translations.of(context);
final sectionsI18n = t.staff.home.sections;
final emptyI18n = t.staff.home.empty_states;
final size = MediaQuery.sizeOf(context);
return SectionLayout(
title: sectionsI18n.recommended_for_you,
child: BlocBuilder<HomeCubit, HomeState>(
builder: (context, state) {
if (state.recommendedShifts.isEmpty) {
return EmptyStateWidget(
message: emptyI18n.no_recommended_shifts,
);
return EmptyStateWidget(message: emptyI18n.no_recommended_shifts);
}
return SizedBox(
height: 160,
height: size.height * 0.15,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: state.recommendedShifts.length,
clipBehavior: Clip.none,
itemBuilder: (context, index) => Padding(
padding: const EdgeInsets.only(
right: UiConstants.space3,
),
padding: const EdgeInsets.only(right: UiConstants.space3),
child: RecommendedShiftCard(
shift: state.recommendedShifts[index],
),

View File

@@ -39,11 +39,14 @@ class SectionHeader extends StatelessWidget {
onTap: onAction,
child: Row(
children: [
Text(action ?? '', style: UiTypography.body3r),
Text(
action ?? '',
style: UiTypography.body3r.textSecondary,
),
const Icon(
UiIcons.chevronRight,
size: UiConstants.space4,
color: UiColors.primary,
color: UiColors.iconSecondary,
),
],
),

View File

@@ -14,6 +14,9 @@ class SectionLayout extends StatelessWidget {
/// Optional action text/widget to display on the right side of the header.
final String? action;
/// Optional callback when action is tapped.
final VoidCallback? onAction;
/// The main content of the section.
final Widget child;
@@ -25,6 +28,7 @@ class SectionLayout extends StatelessWidget {
const SectionLayout({
this.title,
this.action,
this.onAction,
required this.child,
this.contentPadding,
super.key,
@@ -41,6 +45,7 @@ class SectionLayout extends StatelessWidget {
child: SectionHeader(
title: title!,
action: action,
onAction: onAction,
),
),
const SizedBox(height: UiConstants.space2),

View File

@@ -1,7 +1,6 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'circular_progress_painter.dart';
import 'package:staff_home/src/presentation/widgets/worker/worker_benefits/circular_progress_painter.dart';
/// A widget that displays a single benefit item with circular progress.
///
@@ -19,16 +18,12 @@ class BenefitItem extends StatelessWidget {
/// The hours already used.
final double used;
/// The color for the progress indicator.
final Color color;
/// Creates a [BenefitItem].
const BenefitItem({
required this.label,
required this.remaining,
required this.total,
required this.used,
required this.color,
super.key,
});
@@ -44,8 +39,8 @@ class BenefitItem extends StatelessWidget {
child: CustomPaint(
painter: CircularProgressPainter(
progress: progress,
color: color,
backgroundColor: const Color(0xFFE2E8F0),
color: UiColors.primary,
backgroundColor: UiColors.primaryInverse,
strokeWidth: 5,
),
child: Center(
@@ -54,28 +49,21 @@ class BenefitItem extends StatelessWidget {
children: <Widget>[
Text(
'${remaining.toInt()}/${total.toInt()}',
style: UiTypography.body2b.textPrimary.copyWith(
fontSize: 12,
letterSpacing: -0.5,
),
style: UiTypography.body2b
),
Text(
'hours',
style: UiTypography.footnote2r.textTertiary.copyWith(
fontSize: 8,
),
style: UiTypography.footnote2r.textSecondary,
),
],
),
),
),
),
const SizedBox(height: UiConstants.space3),
const SizedBox(height: UiConstants.space2),
Text(
label,
style: UiTypography.footnote2r.textSecondary.copyWith(
fontWeight: FontWeight.w500,
),
style: UiTypography.body2r.textSecondary,
textAlign: TextAlign.center,
),
],

View File

@@ -1,14 +1,10 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart';
import 'package:krow_domain/krow_domain.dart';
import 'benefit_item.dart';
import 'benefits_view_all_link.dart';
import 'package:staff_home/src/presentation/widgets/worker/worker_benefits/benefit_item.dart';
/// Widget for displaying staff benefits, using design system tokens.
///
/// Shows a list of benefits with circular progress indicators
/// and a link to view all benefits.
/// Shows a list of benefits with circular progress indicators.
class BenefitsWidget extends StatelessWidget {
/// The list of benefits to display.
final List<Benefit> benefits;
@@ -22,37 +18,20 @@ class BenefitsWidget extends StatelessWidget {
return const SizedBox.shrink();
}
return Container(
padding: const EdgeInsets.all(UiConstants.space5),
decoration: BoxDecoration(
color: UiColors.white,
borderRadius: UiConstants.radiusLg,
border: Border.all(color: UiColors.border, width: 0.5),
),
child: Column(
children: <Widget>[
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
BenefitsViewAllLink(),
],
),
const SizedBox(height: UiConstants.space6),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: benefits.map((Benefit benefit) {
return Expanded(
child: BenefitItem(
label: benefit.title,
remaining: benefit.remainingHours,
total: benefit.entitlementHours,
used: benefit.usedHours,
color: const Color(0xFF2563EB),
),
);
}).toList(),
),
],
return Padding(
padding: const EdgeInsets.only(top: UiConstants.space4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: benefits.map((Benefit benefit) {
return Expanded(
child: BenefitItem(
label: benefit.title,
remaining: benefit.remainingHours,
total: benefit.entitlementHours,
used: benefit.usedHours,
),
);
}).toList(),
),
);
}