feat(notifications): day sheet notification types

Add daySheetId field to NotificationItem model (Brick-managed), extract
notificationTitle/notificationIcon to top-level testable functions, add
five day-sheet notification cases, and wire tap/banner routing for
day-sheet detail and approvals tab.
Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-09-28 14:54:19 +08:00
parent 54ce528b02
commit 5af0ce62d9
8 changed files with 219 additions and 38 deletions
@@ -16,6 +16,48 @@ import '../../widgets/responsive_body.dart';
import '../../theme/app_surfaces.dart';
import '../../theme/m3_motion.dart';
String notificationTitle(String type, String actorName) {
return switch (type) {
'assignment' => '$actorName assigned you',
'created' => '$actorName created a new item',
'swap_request' => '$actorName requested a shift swap',
'swap_update' => '$actorName updated a swap request',
'announcement' => '$actorName posted an announcement',
'announcement_comment' => '$actorName commented on an announcement',
'it_job_reminder' => 'IT Job submission reminder',
'isr_approved' => 'IT Service Request approved',
'isr_status_changed' => 'IT Service Request status updated',
'isr_assigned' => '$actorName assigned you an IT Service Request',
'day_sheet_submitted' => '$actorName submitted a day sheet',
'day_sheet_disapproved' => 'Day sheet disapproved',
'day_sheet_justified' => '$actorName justified a day sheet',
'day_sheet_approved' => 'Day sheet approved',
'day_sheet_digest' => 'Day sheets awaiting review',
_ => '$actorName mentioned you',
};
}
IconData notificationIcon(String type) {
return switch (type) {
'assignment' => Icons.assignment_ind_outlined,
'created' => Icons.campaign_outlined,
'swap_request' => Icons.swap_horiz,
'swap_update' => Icons.update,
'announcement' => Icons.campaign,
'announcement_comment' => Icons.comment_outlined,
'it_job_reminder' => Icons.print,
'isr_approved' => Icons.check_circle_outline,
'isr_status_changed' => Icons.sync_alt_outlined,
'isr_assigned' => Icons.engineering_outlined,
'day_sheet_submitted' => Icons.assignment_turned_in_outlined,
'day_sheet_disapproved' => Icons.report_outlined,
'day_sheet_justified' => Icons.reply_outlined,
'day_sheet_approved' => Icons.verified_outlined,
'day_sheet_digest' => Icons.inbox_outlined,
_ => Icons.alternate_email,
};
}
class NotificationsScreen extends ConsumerStatefulWidget {
const NotificationsScreen({super.key});
@@ -80,42 +122,11 @@ class _NotificationsScreenState extends ConsumerState<NotificationsScreen> {
};
}
String _notificationTitle(String type, String actorName) {
return switch (type) {
'assignment' => '$actorName assigned you',
'created' => '$actorName created a new item',
'swap_request' => '$actorName requested a shift swap',
'swap_update' => '$actorName updated a swap request',
'announcement' => '$actorName posted an announcement',
'announcement_comment' => '$actorName commented on an announcement',
'it_job_reminder' => 'IT Job submission reminder',
'isr_approved' => 'IT Service Request approved',
'isr_status_changed' => 'IT Service Request status updated',
'isr_assigned' => '$actorName assigned you an IT Service Request',
_ => '$actorName mentioned you',
};
}
IconData _notificationIcon(String type) {
return switch (type) {
'assignment' => Icons.assignment_ind_outlined,
'created' => Icons.campaign_outlined,
'swap_request' => Icons.swap_horiz,
'swap_update' => Icons.update,
'announcement' => Icons.campaign,
'announcement_comment' => Icons.comment_outlined,
'it_job_reminder' => Icons.print,
'isr_approved' => Icons.check_circle_outline,
'isr_status_changed' => Icons.sync_alt_outlined,
'isr_assigned' => Icons.engineering_outlined,
_ => Icons.alternate_email,
};
}
Future<void> _handleTap(BuildContext context, NotificationItem item) async {
final ticketId = item.ticketId;
final taskId = item.taskId;
final isrId = item.itServiceRequestId;
final daySheetId = item.daySheetId;
if (ticketId != null) {
await ref
@@ -136,6 +147,10 @@ class _NotificationsScreenState extends ConsumerState<NotificationsScreen> {
context.go('/tickets/$ticketId');
} else if (isrId != null) {
context.go('/it-service-requests/$isrId');
} else if (item.type == 'day_sheet_digest') {
context.go('/programmer-tasks?tab=approvals');
} else if (daySheetId != null) {
context.go('/programmer-tasks/day-sheets/$daySheetId');
}
}
@@ -248,8 +263,8 @@ class _NotificationsScreenState extends ConsumerState<NotificationsScreen> {
animDelay: Duration(
milliseconds: animIndexes[index].clamp(0, 6) * 50,
),
title: _notificationTitle(item.type, actorName),
icon: _notificationIcon(item.type),
title: notificationTitle(item.type, actorName),
icon: notificationIcon(item.type),
iconContainerColor:
_iconContainerColor(context, item.type),
iconForegroundColor:
@@ -331,6 +346,7 @@ class _NotificationCard extends StatelessWidget {
if (item.announcementId != null) return 'Announcement';
if (item.taskId != null) return taskTitle ?? 'Task';
if (item.itServiceRequestId != null) return 'IT Service Request';
if (item.daySheetId != null) return 'Day Sheet';
return ticketSubject ?? 'Ticket';
}