feat: Enhance benefits section and layout for improved user experience
This commit is contained in:
@@ -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),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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],
|
||||
),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user