diff --git a/apps/librarian/lib/dashboard/pages/dashboard_page.dart b/apps/librarian/lib/dashboard/pages/dashboard_page.dart index 00a589f..b8bdf71 100644 --- a/apps/librarian/lib/dashboard/pages/dashboard_page.dart +++ b/apps/librarian/lib/dashboard/pages/dashboard_page.dart @@ -4,12 +4,12 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:librarian_app/dashboard/providers/create_loan_controller.dart'; import 'package:librarian_app/dashboard/providers/invalidate_module.dart'; import 'package:librarian_app/dashboard/providers/workspace.dart'; +import 'package:librarian_app/dashboard/widgets/end_drawer.dart'; import 'package:librarian_app/modules/authentication/providers/auth_service_provider.dart'; import 'package:librarian_app/modules/authentication/providers/user_tray.dart'; import 'package:librarian_app/modules/members/details/needs_attention_page.dart'; import 'package:librarian_app/dashboard/layouts/members_desktop_layout.dart'; import 'package:librarian_app/modules/members/list/searchable_members_list.dart'; -import 'package:librarian_app/dashboard/providers/end_drawer_provider.dart'; import 'package:librarian_app/dashboard/widgets/create_menu_item.dart'; import 'package:librarian_app/dashboard/layouts/inventory_desktop_layout.dart'; import 'package:librarian_app/modules/things/maintenance/view.dart'; @@ -232,7 +232,7 @@ class _DashboardPageState extends ConsumerState { ], ) : null, - endDrawer: ref.watch(endDrawerProvider).drawer, + endDrawer: const EndDrawer(), floatingActionButton: mobile ? menuAnchor : null, ); }, diff --git a/apps/librarian/lib/dashboard/providers/end_drawer_provider.dart b/apps/librarian/lib/dashboard/providers/end_drawer_provider.dart index a436530..f38c55b 100644 --- a/apps/librarian/lib/dashboard/providers/end_drawer_provider.dart +++ b/apps/librarian/lib/dashboard/providers/end_drawer_provider.dart @@ -5,12 +5,12 @@ class EndDrawerController { EndDrawerController(this.ref); final Ref ref; - Widget? drawer; + Widget child = const SizedBox.expand(); - openEndDrawer(BuildContext context, Widget drawer) { - this.drawer = drawer; + openEndDrawer(BuildContext context, Widget child) { + this.child = child; ref.notifyListeners(); - Future.delayed(const Duration(milliseconds: 500), () { + Future.delayed(const Duration(milliseconds: 750), () { Scaffold.of(context).openEndDrawer(); }); } diff --git a/apps/librarian/lib/dashboard/widgets/end_drawer.dart b/apps/librarian/lib/dashboard/widgets/end_drawer.dart new file mode 100644 index 0000000..efcabf7 --- /dev/null +++ b/apps/librarian/lib/dashboard/widgets/end_drawer.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../providers/end_drawer_provider.dart'; + +class EndDrawer extends ConsumerWidget { + const EndDrawer({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Drawer( + width: 500, + child: ref.watch(endDrawerProvider).child, + ); + } +} diff --git a/apps/librarian/lib/modules/loans/checkout/stepper/items/connected_thing_search_field.dart b/apps/librarian/lib/modules/loans/checkout/stepper/items/connected_thing_search_field.dart index d8ce017..5f2e230 100644 --- a/apps/librarian/lib/modules/loans/checkout/stepper/items/connected_thing_search_field.dart +++ b/apps/librarian/lib/modules/loans/checkout/stepper/items/connected_thing_search_field.dart @@ -26,88 +26,72 @@ class ConnectedThingSearchField extends StatelessWidget { @override Widget build(BuildContext context) { - return TextField( - controller: _textController, - onSubmitted: (_) => _submit(), - decoration: InputDecoration( - hintText: 'Enter Item Number', - prefixIcon: const Icon(Icons.numbers), - suffixIcon: IconButton( - tooltip: 'Add Item', - onPressed: () => _submit(), - icon: const Icon(Icons.add_rounded), - ), - ), - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - ], - ); + return ListenableBuilder( + listenable: controller, + builder: (context, child) { + return TextField( + controller: _textController, + onSubmitted: (_) => _submit(), + decoration: InputDecoration( + errorText: controller.errorText, + hintText: 'Enter Item Number', + prefixIcon: const Icon(Icons.numbers), + suffixIcon: IconButton( + tooltip: 'Add Item', + onPressed: () => _submit(), + icon: const Icon(Icons.add_rounded), + ), + ), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + ], + ); + }); } } -class ThingSearchController { +class ThingSearchController extends ChangeNotifier { final BuildContext context; + final List items; final InventoryRepository repository; final void Function(ItemModel) onMatchFound; bool isLoading = false; + String? errorText; + ThingSearchController({ required this.context, + required this.items, required this.repository, required this.onMatchFound, }); Future search(String value) async { + final itemNumber = int.parse(value); + + if (items.any((t) => t.number == itemNumber)) { + errorText = '#$value is already added to this loan.'; + notifyListeners(); + return; + } + isLoading = true; - final match = await repository.getItem(number: int.parse(value)); + final match = await repository.getItem(number: itemNumber); isLoading = false; - if (match != null) { - if (!match.available) { - _showThingCheckedOutDialog(match); - } else { - onMatchFound(match); - } - } else { - _showUnknownThingDialog(value); + if (match == null) { + errorText = '#$value could not be found.'; + notifyListeners(); + return; } - } - void _showThingCheckedOutDialog(ItemModel thing) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text("Item Unavailable"), - content: Text( - "Item #${thing.number} is checked out or not available for lending."), - actions: [ - TextButton( - child: const Text("OK"), - onPressed: () => Navigator.pop(context), - ) - ], - ); - }, - ); - } + if (!match.available) { + errorText = '#$value is unavailable.'; + notifyListeners(); + return; + } - void _showUnknownThingDialog(String searchValue) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: Text("Item #$searchValue does not exist."), - content: const Text("Try another number."), - actions: [ - TextButton( - child: const Text("OK"), - onPressed: () => Navigator.pop(context), - ) - ], - ); - }, - ); + onMatchFound(match); } } diff --git a/apps/librarian/lib/modules/loans/checkout/stepper/items/existing_item_dialog.dart b/apps/librarian/lib/modules/loans/checkout/stepper/items/existing_item_dialog.dart deleted file mode 100644 index e1d99ad..0000000 --- a/apps/librarian/lib/modules/loans/checkout/stepper/items/existing_item_dialog.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; - -class ExistingItemDialog extends StatelessWidget { - const ExistingItemDialog({super.key, required this.number}); - - final int number; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text('Item #$number Already Added'), - content: const Text("The item can't be added again."), - actions: [ - FilledButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('OK'), - ), - ], - ); - } -} diff --git a/apps/librarian/lib/modules/loans/checkout/stepper/items/items_step.dart b/apps/librarian/lib/modules/loans/checkout/stepper/items/items_step.dart index 8cd06ae..9715eb4 100644 --- a/apps/librarian/lib/modules/loans/checkout/stepper/items/items_step.dart +++ b/apps/librarian/lib/modules/loans/checkout/stepper/items/items_step.dart @@ -5,7 +5,6 @@ import 'package:librarian_app/modules/things/providers/things_repository_provide import 'package:librarian_app/widgets/item_card.dart'; import 'connected_thing_search_field.dart'; -import 'existing_item_dialog.dart'; import 'eye_protection_dialog.dart'; import 'suggested_things_dialog.dart'; @@ -27,20 +26,11 @@ Step buildItemsStep({ ConnectedThingSearchField( controller: ThingSearchController( context: context, - onMatchFound: (thing) { - if (items.any((t) => t.id == thing.id)) { - showDialog( - context: context, - builder: (context) { - return ExistingItemDialog(number: thing.number); - }, - ); - return; - } - - onAddItem(thing); + items: items, + onMatchFound: (item) { + onAddItem(item); - if (thing.eyeProtection && !didPromptForEyeProtection) { + if (item.eyeProtection && !didPromptForEyeProtection) { showDialog( context: context, builder: (_) => const EyeProtectionDialog(), @@ -48,12 +38,12 @@ Step buildItemsStep({ onPromptForEyeProtection(); } - if (thing.linkedThingIds.isNotEmpty) { + if (item.linkedThingIds.isNotEmpty) { showDialog( context: context, builder: (_) => SuggestedThingsDialog( - thingName: thing.name, - thingIds: thing.linkedThingIds, + thingName: item.name, + thingIds: item.linkedThingIds, ), ); } diff --git a/apps/librarian/lib/modules/things/details/inventory/item_details/drawer.dart b/apps/librarian/lib/modules/things/details/inventory/item_details/drawer.dart index 832271d..55e4638 100644 --- a/apps/librarian/lib/modules/things/details/inventory/item_details/drawer.dart +++ b/apps/librarian/lib/modules/things/details/inventory/item_details/drawer.dart @@ -24,115 +24,117 @@ class _ItemDetailsDrawerState extends State { bool isLoading = false; - convert() { + void convert() { widget.controller.convertThing(context); } @override Widget build(BuildContext context) { - return Drawer( - width: 500, - child: Stack( - children: [ - Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: Row( - children: [ - getIcon(widget.controller.item!), - const SizedBox(width: 8.0), - Expanded( - child: Text( - '#${widget.controller.item!.number}', - style: Theme.of(context).textTheme.titleLarge, - ), + return Stack( + children: [ + Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: Row( + children: [ + ListenableBuilder( + listenable: widget.controller, + builder: (context, child) { + return getIcon(widget.controller.item!); + }, + ), + const SizedBox(width: 8.0), + Expanded( + child: Text( + '#${widget.controller.item!.number}', + style: Theme.of(context).textTheme.titleLarge, ), - MenuAnchor( - controller: menuController, - menuChildren: [ - MenuItemButton( - onPressed: convert, - leadingIcon: const Icon(Icons.transform), - child: const Text('Convert'), - ), - ], - child: IconButton.filled( - onPressed: () => menuController.open(), - icon: const Icon(Icons.more_vert), + ), + MenuAnchor( + controller: menuController, + menuChildren: [ + MenuItemButton( + onPressed: convert, + leadingIcon: const Icon(Icons.transform), + child: const Text('Convert'), ), + ], + child: IconButton.filled( + onPressed: () => menuController.open(), + icon: const Icon(Icons.more_vert), ), - ], - ), + ), + ], ), - const Divider(height: 1), - Expanded( - child: SingleChildScrollView( - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - ItemDetails( - controller: widget.controller, - item: widget.controller.item!, - isThingHidden: widget.isHiddenLocked, - ), - const SizedBox(height: 80), - ], - ), + ), + const Divider(height: 1), + Expanded( + child: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + children: [ + ItemDetails( + controller: widget.controller, + item: widget.controller.item!, + isThingHidden: widget.isHiddenLocked, + ), + const SizedBox(height: 80), + ], ), ), ), - ], - ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: Container( - color: Theme.of(context).colorScheme.surface.withAlpha(180), - child: Padding( - padding: const EdgeInsets.all(16), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ListenableBuilder( - listenable: widget.controller, - builder: (context, child) { - if (!widget.controller.hasUnsavedChanges) { - return child!; - } + ), + ], + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + color: Theme.of(context).colorScheme.surface.withAlpha(180), + child: Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + ListenableBuilder( + listenable: widget.controller, + builder: (context, child) { + if (!widget.controller.hasUnsavedChanges) { + return child!; + } - return OutlinedButton( - onPressed: () { - widget.controller.discardChanges(); - }, - child: const Text('Discard'), - ); - }, - child: OutlinedButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Cancel'), - ), - ), - const SizedBox(width: 8), - ListenableBuilder( - listenable: widget.controller, - builder: (_, __) { - return FilledProgressButton( - onPressed: widget.controller.saveChanges, - isLoading: isLoading, - child: const Text('Save'), - ); - }, + return OutlinedButton( + onPressed: () { + widget.controller.discardChanges(); + }, + child: const Text('Discard'), + ); + }, + child: OutlinedButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Cancel'), ), - ], - ), + ), + const SizedBox(width: 8), + ListenableBuilder( + listenable: widget.controller, + builder: (_, __) { + return FilledProgressButton( + onPressed: widget.controller.saveChanges, + isLoading: isLoading, + child: const Text('Save'), + ); + }, + ), + ], ), ), ), - ], - ), + ), + ], ); } } diff --git a/apps/librarian/pubspec.yaml b/apps/librarian/pubspec.yaml index f1a1106..c5a8474 100644 --- a/apps/librarian/pubspec.yaml +++ b/apps/librarian/pubspec.yaml @@ -10,7 +10,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # followed by an optional build number separated by a +. # Both the version and the builder number may be overridden in flutter # build by specifying --build-name and --build-number, respectively. -version: 1.0.0+23 +version: 1.0.0+24 environment: sdk: '>=3.0.0'