refactor: add optional subtitle to various widgets for enhanced context

This commit is contained in:
Achintha Isuru
2026-01-30 01:59:41 -05:00
parent 1e8d6ae65b
commit ed71d2b4a3
7 changed files with 109 additions and 18 deletions

View File

@@ -74,23 +74,32 @@ class ClientHomePage extends StatelessWidget {
/// Builds the widget list in normal mode with visibility filters. /// Builds the widget list in normal mode with visibility filters.
Widget _buildNormalModeList(ClientHomeState state) { Widget _buildNormalModeList(ClientHomeState state) {
return ListView( return ListView.separated(
padding: const EdgeInsets.fromLTRB( padding: const EdgeInsets.only(bottom: 100),
UiConstants.space4, separatorBuilder: (BuildContext context, int index) {
0, return const Divider(color: UiColors.border, height: 0.2);
UiConstants.space4, },
100, itemCount: state.widgetOrder.length,
itemBuilder: (BuildContext context, int index) {
final String id = state.widgetOrder[index];
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
if (index != 0) const SizedBox(height: UiConstants.space8),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: UiConstants.space4,
), ),
children: state.widgetOrder.map((String id) {
return Padding(
padding: const EdgeInsets.only(bottom: UiConstants.space4),
child: DashboardWidgetBuilder( child: DashboardWidgetBuilder(
id: id, id: id,
state: state, state: state,
isEditMode: false, isEditMode: false,
), ),
),
const SizedBox(height: UiConstants.space8),
],
); );
}).toList(), },
); );
} }
} }

View File

@@ -10,11 +10,15 @@ class ActionsWidget extends StatelessWidget {
/// Callback when Create Order is pressed. /// Callback when Create Order is pressed.
final VoidCallback onCreateOrderPressed; final VoidCallback onCreateOrderPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates an [ActionsWidget]. /// Creates an [ActionsWidget].
const ActionsWidget({ const ActionsWidget({
super.key, super.key,
required this.onRapidPressed, required this.onRapidPressed,
required this.onCreateOrderPressed, required this.onCreateOrderPressed,
this.subtitle,
}); });
@override @override
@@ -22,7 +26,10 @@ class ActionsWidget extends StatelessWidget {
// Check if client_home exists in t // Check if client_home exists in t
final TranslationsClientHomeActionsEn i18n = t.client_home.actions; final TranslationsClientHomeActionsEn i18n = t.client_home.actions;
return Row( return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[ children: <Widget>[
/// TODO: FEATURE_NOT_YET_IMPLEMENTED /// TODO: FEATURE_NOT_YET_IMPLEMENTED
// Expanded( // Expanded(
@@ -55,6 +62,8 @@ class ActionsWidget extends StatelessWidget {
), ),
), ),
], ],
),
],
); );
} }
} }

View File

@@ -12,17 +12,22 @@ class CoverageWidget extends StatelessWidget {
/// The percentage of coverage (0-100). /// The percentage of coverage (0-100).
final int coveragePercent; final int coveragePercent;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [CoverageWidget]. /// Creates a [CoverageWidget].
const CoverageWidget({ const CoverageWidget({
super.key, super.key,
this.totalNeeded = 0, this.totalNeeded = 0,
this.totalConfirmed = 0, this.totalConfirmed = 0,
this.coveragePercent = 0, this.coveragePercent = 0,
this.subtitle,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -53,6 +58,12 @@ class CoverageWidget extends StatelessWidget {
), ),
], ],
), ),
if (subtitle != null) ...<Widget>[
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
Row( Row(
children: <Widget>[ children: <Widget>[

View File

@@ -56,11 +56,15 @@ class DashboardWidgetBuilder extends StatelessWidget {
/// Builds the actual widget content based on the widget ID. /// Builds the actual widget content based on the widget ID.
Widget _buildWidgetContent(BuildContext context) { Widget _buildWidgetContent(BuildContext context) {
// Only show subtitle in normal mode
final String? subtitle = !isEditMode ? _getWidgetSubtitle(id) : null;
switch (id) { switch (id) {
case 'actions': case 'actions':
return ActionsWidget( return ActionsWidget(
onRapidPressed: () => Modular.to.pushRapidOrder(), onRapidPressed: () => Modular.to.pushRapidOrder(),
onCreateOrderPressed: () => Modular.to.pushCreateOrder(), onCreateOrderPressed: () => Modular.to.pushCreateOrder(),
subtitle: subtitle,
); );
case 'reorder': case 'reorder':
return ReorderWidget( return ReorderWidget(
@@ -88,6 +92,7 @@ class DashboardWidgetBuilder extends StatelessWidget {
}, },
); );
}, },
subtitle: subtitle,
); );
case 'spending': case 'spending':
return SpendingWidget( return SpendingWidget(
@@ -95,6 +100,7 @@ class DashboardWidgetBuilder extends StatelessWidget {
next7DaysSpending: state.dashboardData.next7DaysSpending, next7DaysSpending: state.dashboardData.next7DaysSpending,
weeklyShifts: state.dashboardData.weeklyShifts, weeklyShifts: state.dashboardData.weeklyShifts,
next7DaysScheduled: state.dashboardData.next7DaysScheduled, next7DaysScheduled: state.dashboardData.next7DaysScheduled,
subtitle: subtitle,
); );
case 'coverage': case 'coverage':
return CoverageWidget( return CoverageWidget(
@@ -106,10 +112,12 @@ class DashboardWidgetBuilder extends StatelessWidget {
100) 100)
.toInt() .toInt()
: 0, : 0,
subtitle: subtitle,
); );
case 'liveActivity': case 'liveActivity':
return LiveActivityWidget( return LiveActivityWidget(
onViewAllPressed: () => Modular.to.navigate('/client-main/coverage/'), onViewAllPressed: () => Modular.to.navigate('/client-main/coverage/'),
subtitle: subtitle,
); );
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
@@ -133,4 +141,21 @@ class DashboardWidgetBuilder extends StatelessWidget {
return ''; return '';
} }
} }
String _getWidgetSubtitle(String id) {
switch (id) {
case 'actions':
return 'Quick access to create and manage orders';
case 'reorder':
return 'Easily reorder from your past activity';
case 'spending':
return 'Track your spending and budget in real-time';
case 'coverage':
return 'Overview of your current shift coverage';
case 'liveActivity':
return 'Real-time updates on your active shifts';
default:
return '';
}
}
} }

View File

@@ -10,8 +10,15 @@ class LiveActivityWidget extends StatefulWidget {
/// Callback when "View all" is pressed. /// Callback when "View all" is pressed.
final VoidCallback onViewAllPressed; final VoidCallback onViewAllPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [LiveActivityWidget]. /// Creates a [LiveActivityWidget].
const LiveActivityWidget({super.key, required this.onViewAllPressed}); const LiveActivityWidget({
super.key,
required this.onViewAllPressed,
this.subtitle
});
@override @override
State<LiveActivityWidget> createState() => _LiveActivityWidgetState(); State<LiveActivityWidget> createState() => _LiveActivityWidgetState();
@@ -100,6 +107,7 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
final TranslationsClientHomeEn i18n = t.client_home; final TranslationsClientHomeEn i18n = t.client_home;
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -121,6 +129,13 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
), ),
], ],
), ),
if (widget.subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
widget.subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
FutureBuilder<_LiveActivityData>( FutureBuilder<_LiveActivityData>(
future: _liveActivityFuture, future: _liveActivityFuture,

View File

@@ -11,11 +11,15 @@ class ReorderWidget extends StatelessWidget {
/// Callback when a reorder button is pressed. /// Callback when a reorder button is pressed.
final Function(Map<String, dynamic> shiftData) onReorderPressed; final Function(Map<String, dynamic> shiftData) onReorderPressed;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [ReorderWidget]. /// Creates a [ReorderWidget].
const ReorderWidget({ const ReorderWidget({
super.key, super.key,
required this.orders, required this.orders,
required this.onReorderPressed, required this.onReorderPressed,
this.subtitle,
}); });
@override @override
@@ -33,6 +37,13 @@ class ReorderWidget extends StatelessWidget {
letterSpacing: 0.5, letterSpacing: 0.5,
), ),
), ),
if (subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
SizedBox( SizedBox(
height: 140, height: 140,

View File

@@ -16,6 +16,9 @@ class SpendingWidget extends StatelessWidget {
/// The number of scheduled shifts for next 7 days. /// The number of scheduled shifts for next 7 days.
final int next7DaysScheduled; final int next7DaysScheduled;
/// Optional subtitle for the section.
final String? subtitle;
/// Creates a [SpendingWidget]. /// Creates a [SpendingWidget].
const SpendingWidget({ const SpendingWidget({
super.key, super.key,
@@ -23,6 +26,7 @@ class SpendingWidget extends StatelessWidget {
required this.next7DaysSpending, required this.next7DaysSpending,
required this.weeklyShifts, required this.weeklyShifts,
required this.next7DaysScheduled, required this.next7DaysScheduled,
this.subtitle,
}); });
@override @override
@@ -38,6 +42,13 @@ class SpendingWidget extends StatelessWidget {
letterSpacing: 0.5, letterSpacing: 0.5,
), ),
), ),
if (subtitle != null) ...<Widget>[
const SizedBox(height: UiConstants.space1),
Text(
subtitle!,
style: UiTypography.body2r.textSecondary,
),
],
const SizedBox(height: UiConstants.space2), const SizedBox(height: UiConstants.space2),
Container( Container(
padding: const EdgeInsets.all(UiConstants.space3), padding: const EdgeInsets.all(UiConstants.space3),