auto-claude: subtask-2-2 - Create NotesDialog component for displaying delivery notes

- Add reusable NotesDialog component that extracts and displays notes from delivery orders
- Add static show() method for convenient dialog display with empty notes handling
- Add localization strings for notes dialog (EN/FR): notesTitle, noNotesMessage, close
- Follow existing dialog pattern from NavigationTermsAndConditionsDialog

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mathias Beaulieu-Duncan 2026-01-20 11:28:31 -05:00
parent f3a05099ab
commit bcc938fde1
6 changed files with 162 additions and 2 deletions

View File

@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:planb_logistic/l10n/app_localizations.dart';
import '../models/delivery.dart';
/// A dialog component for displaying delivery notes.
///
/// This dialog extracts and displays all non-empty notes from the
/// orders associated with a delivery.
class NotesDialog extends StatelessWidget {
/// The delivery whose notes should be displayed.
final Delivery delivery;
const NotesDialog({
super.key,
required this.delivery,
});
/// Extracts non-empty notes from the delivery's orders.
List<String> _extractNotes() {
return delivery.orders
.where((order) => order.note != null && order.note!.isNotEmpty)
.map((order) => order.note!)
.toList();
}
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context);
final colorScheme = Theme.of(context).colorScheme;
final notes = _extractNotes();
return AlertDialog(
title: Text(
l10n.notesTitle(delivery.name),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: colorScheme.onSurface,
),
),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: notes
.map(
(note) => Padding(
padding: const EdgeInsets.only(bottom: 16.0),
child: Text(
note,
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
color: colorScheme.onSurface,
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
)
.toList(),
),
),
actionsAlignment: MainAxisAlignment.center,
actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
actions: [
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
l10n.close,
style: TextStyle(color: colorScheme.onPrimary),
),
),
),
],
);
}
/// Shows the notes dialog for a delivery.
///
/// Returns `true` if the dialog was shown (i.e., the delivery has notes),
/// `false` otherwise.
///
/// If the delivery has no notes, this method returns `false` without
/// showing the dialog. The caller is responsible for handling this case,
/// typically by showing an info message.
static Future<bool> show(BuildContext context, Delivery delivery) async {
final notes = delivery.orders
.where((order) => order.note != null && order.note!.isNotEmpty)
.map((order) => order.note!)
.toList();
if (notes.isEmpty) {
return false;
}
await showDialog<void>(
context: context,
builder: (BuildContext dialogContext) {
return NotesDialog(delivery: delivery);
},
);
return true;
}
}

View File

@ -109,5 +109,13 @@
"passwordRequired": "Password is required", "passwordRequired": "Password is required",
"loginButton": "Login", "loginButton": "Login",
"navigate": "Navigate", "navigate": "Navigate",
"upload": "Upload" "upload": "Upload",
"notesTitle": "Notes for {name}",
"@notesTitle": {
"placeholders": {
"name": {"type": "String"}
}
},
"noNotesMessage": "No notes attached to this delivery",
"close": "Close"
} }

View File

@ -109,5 +109,13 @@
"passwordRequired": "Le mot de passe est requis", "passwordRequired": "Le mot de passe est requis",
"loginButton": "Connexion", "loginButton": "Connexion",
"navigate": "Naviguer", "navigate": "Naviguer",
"upload": "Téléverser" "upload": "Téléverser",
"notesTitle": "Notes pour {name}",
"@notesTitle": {
"placeholders": {
"name": {"type": "String"}
}
},
"noNotesMessage": "Aucune note associée à cette livraison",
"close": "Fermer"
} }

View File

@ -559,6 +559,24 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Upload'** /// **'Upload'**
String get upload; String get upload;
/// No description provided for @notesTitle.
///
/// In en, this message translates to:
/// **'Notes for {name}'**
String notesTitle(String name);
/// No description provided for @noNotesMessage.
///
/// In en, this message translates to:
/// **'No notes attached to this delivery'**
String get noNotesMessage;
/// No description provided for @close.
///
/// In en, this message translates to:
/// **'Close'**
String get close;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View File

@ -256,4 +256,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get upload => 'Upload'; String get upload => 'Upload';
@override
String notesTitle(String name) {
return 'Notes for $name';
}
@override
String get noNotesMessage => 'No notes attached to this delivery';
@override
String get close => 'Close';
} }

View File

@ -256,4 +256,15 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get upload => 'Téléverser'; String get upload => 'Téléverser';
@override
String notesTitle(String name) {
return 'Notes pour $name';
}
@override
String get noNotesMessage => 'Aucune note associée à cette livraison';
@override
String get close => 'Fermer';
} }