Skip to content

Commit

Permalink
Merge pull request #69 from pvdthings/search-by-item-number
Browse files Browse the repository at this point in the history
Librarian - Search by item number
  • Loading branch information
dillonfagan authored Sep 26, 2024
2 parents 66b2f8e + 51f1863 commit 3ba4bb4
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 73 deletions.
21 changes: 5 additions & 16 deletions apps/librarian/lib/dashboard/layouts/inventory_desktop_layout.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/modules/things/search/search_field.dart';
import 'package:librarian_app/widgets/panes/list_pane.dart';
import 'package:librarian_app/widgets/panes/pane_header.dart';
import 'package:librarian_app/widgets/fields/search_field.dart';
import 'package:librarian_app/modules/things/providers/selected_thing_provider.dart';
import 'package:librarian_app/modules/things/providers/things_filter_provider.dart';

import '../../modules/things/details/inventory_details_pane.dart';
import '../../modules/things/details/inventory/inventory_list/inventory_list_view.dart';
Expand All @@ -14,24 +12,15 @@ class InventoryDesktopLayout extends ConsumerWidget {

@override
Widget build(BuildContext context, WidgetRef ref) {
return Row(
return const Row(
children: [
ListPane(
header: PaneHeader(
child: SearchField(
text: ref.watch(thingsFilterProvider),
onChanged: (value) {
ref.read(thingsFilterProvider.notifier).state = value;
},
onClearPressed: () {
ref.read(thingsFilterProvider.notifier).state = null;
ref.read(selectedThingProvider.notifier).state = null;
},
),
child: ThingsSearchField(),
),
child: const InventoryListView(),
child: InventoryListView(),
),
const Expanded(
Expanded(
child: InventoryDetailsPane(),
),
],
Expand Down
40 changes: 4 additions & 36 deletions apps/librarian/lib/modules/things/details/inventory_details.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,18 @@ import 'package:file_picker/_internal/file_picker_web.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/dashboard/providers/end_drawer_provider.dart';
import 'package:librarian_app/modules/things/details/inventory/create_items/create_items_dialog.dart';
import 'package:librarian_app/modules/things/details/inventory/item_details/drawer.dart';
import 'package:librarian_app/modules/things/details/thing_details/thing_details_card.dart';
import 'package:librarian_app/core/api/models/updated_image_model.dart';
import 'package:librarian_app/modules/things/details/inventory/item_details_page.dart';
import 'package:librarian_app/modules/things/providers/edited_thing_details_providers.dart';
import 'package:librarian_app/modules/things/providers/item_details_orchestrator.dart';
import 'package:librarian_app/modules/things/providers/selected_thing_provider.dart';
import 'package:librarian_app/modules/things/providers/thing_details_provider.dart';
import 'package:librarian_app/modules/things/providers/things_repository_provider.dart';
import 'package:librarian_app/modules/things/details/categories/categories_card.dart';
import 'package:librarian_app/modules/things/details/inventory/items_card.dart';
import 'package:librarian_app/modules/things/details/image/thing_image_card.dart';
import 'package:librarian_app/utils/media_query.dart';

import 'inventory/item_details/item_details_controller.dart';
import 'linked_things/card.dart';

class InventoryDetails extends ConsumerWidget {
Expand Down Expand Up @@ -82,37 +78,9 @@ class InventoryDetails extends ConsumerWidget {
ItemsCard(
items: details.items,
availableItemsCount: details.available,
onTap: (item) {
if (isMobile(context)) {
Navigator.of(context)
.push(MaterialPageRoute(builder: (context) {
return ItemDetailsPage(
item: item,
hiddenLocked: details.hidden,
);
}));
return;
}

final detailsController = ItemDetailsController(
item: item,
repository: ref.read(thingsRepositoryProvider.notifier),
onSave: () {
// setState(() => _isLoading = true);
},
onSaveComplete: () {
// setState(() => _isLoading = false);
},
);

ref.read(endDrawerProvider).openEndDrawer(
context,
ItemDetailsDrawer(
controller: detailsController,
isHiddenLocked: details.hidden,
),
);
},
onTap: (item) => ref
.read(itemDetailsOrchestrator)
.openItem(context, item: item, hiddenLocked: details.hidden),
onAddItemsPressed: () {
showDialog(
context: context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/modules/things/providers/thing_details_controller_provider.dart';
import 'package:librarian_app/widgets/dialogs/save_dialog.dart';
import 'package:librarian_app/widgets/panes/header_divider.dart';
import 'package:librarian_app/widgets/panes/pane_header.dart';
import 'package:librarian_app/core/api/models/detailed_thing_model.dart';
import 'package:librarian_app/modules/things/providers/edited_thing_details_providers.dart';
Expand Down Expand Up @@ -92,13 +93,7 @@ class InventoryDetailsPane extends ConsumerWidget {
icon: const Icon(Icons.cancel),
tooltip: 'Discard Changes',
),
SizedBox(
height: 24,
width: 24,
child: VerticalDivider(
color: Colors.white.withOpacity(0.3),
),
),
const HeaderDivider(),
IconButton(
onPressed: delete,
icon: const Icon(Icons.delete_forever),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/core/api/models/item_model.dart';
import 'package:librarian_app/dashboard/providers/end_drawer_provider.dart';
import 'package:librarian_app/utils/media_query.dart';

import '../details/inventory/item_details/drawer.dart';
import '../details/inventory/item_details/item_details_controller.dart';
import '../details/inventory/item_details_page.dart';
import 'things_repository_provider.dart';

class ItemDetailsOrchestrator {
ItemDetailsOrchestrator(this.ref);

final Ref ref;

void openItem(
BuildContext context, {
required ItemModel item,
required bool hiddenLocked,
}) {
if (isMobile(context)) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return ItemDetailsPage(
item: item,
hiddenLocked: hiddenLocked,
);
}));
return;
}

final detailsController = ItemDetailsController(
item: item,
repository: ref.read(thingsRepositoryProvider.notifier),
onSave: () {
// setState(() => _isLoading = true);
},
onSaveComplete: () {
// setState(() => _isLoading = false);
},
);

ref.read(endDrawerProvider).openEndDrawer(
context,
ItemDetailsDrawer(
controller: detailsController,
isHiddenLocked: hiddenLocked,
),
);
}
}

final itemDetailsOrchestrator = Provider((ref) => ItemDetailsOrchestrator(ref));
141 changes: 141 additions & 0 deletions apps/librarian/lib/modules/things/search/item_lookup_button.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/core/api/models/item_model.dart';
import 'package:librarian_app/core/api/models/thing_model.dart';
import 'package:librarian_app/modules/things/providers/find_things.dart';
import 'package:librarian_app/modules/things/providers/item_details_orchestrator.dart';
import 'package:librarian_app/modules/things/providers/selected_thing_provider.dart';
import 'package:librarian_app/modules/things/providers/things_repository_provider.dart';
import 'package:librarian_app/widgets/input_decoration.dart';

class ItemLookupButton extends ConsumerWidget {
const ItemLookupButton({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return IconButton(
onPressed: () async {
final result = await showDialog<LookupResult?>(
context: context,
builder: (context) => const ItemLookupDialog(),
);

// Load Thing and Item Details
if (result != null) {
ref.read(selectedThingProvider.notifier).state = result.thing;

Future.delayed(Duration.zero, () {
ref.read(itemDetailsOrchestrator).openItem(context,
item: result.item, hiddenLocked: result.thing.hidden);
});
}
},
icon: const Icon(Icons.numbers),
);
}
}

class ItemLookupDialog extends ConsumerStatefulWidget {
const ItemLookupDialog({super.key});

@override
ConsumerState<ConsumerStatefulWidget> createState() {
return _ItemLookupDialogState();
}
}

class _ItemLookupDialogState extends ConsumerState<ItemLookupDialog> {
final formKey = GlobalKey<FormState>();
final numberController = TextEditingController();

String? errorMessage;

void onSubmit() {
if (formKey.currentState!.validate()) {
search(int.parse(numberController.text));
}
}

void search(int number) async {
final item = await ref
.read(thingsRepositoryProvider.notifier)
.getItem(number: number);

if (item == null) {
setState(() {
errorMessage = 'Item #$number does not exist';
});
return;
}

final things = await ref.read(findThingsByItem(number: number));
final thing = things[0];

await Future.delayed(Duration.zero, () {
Navigator.of(context).pop(LookupResult(item: item, thing: thing));
});
}

@override
Widget build(BuildContext context) {
return AlertDialog(
icon: const Icon(Icons.search),
title: const Text('Item Lookup'),
actions: [
OutlinedButton(
onPressed: () {
Navigator.of(context).pop(null);
},
child: const Text('Cancel'),
),
ValueListenableBuilder(
valueListenable: numberController,
builder: (context, name, child) => FilledButton(
onPressed: name.text.isNotEmpty ? onSubmit : null,
child: const Text('Look up'),
),
),
],
contentPadding: const EdgeInsets.all(16),
content: SizedBox(
width: 500,
child: Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: TextFormField(
autofocus: true,
controller: numberController,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Number is required';
}

return null;
},
onChanged: (value) {
if (value.length < 3) {
return;
}
},
onFieldSubmitted: (value) => onSubmit(),
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
decoration: inputDecoration.copyWith(
labelText: 'Item Number',
constraints: const BoxConstraints(minWidth: 500),
prefixIcon: const Icon(Icons.numbers),
errorText: errorMessage,
),
),
),
),
);
}
}

class LookupResult {
final ItemModel item;
final ThingModel thing;

LookupResult({required this.item, required this.thing});
}
29 changes: 29 additions & 0 deletions apps/librarian/lib/modules/things/search/search_field.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:librarian_app/widgets/fields/search_field.dart';

import '../providers/selected_thing_provider.dart';
import '../providers/things_filter_provider.dart';
import 'item_lookup_button.dart';

class ThingsSearchField extends ConsumerWidget {
const ThingsSearchField({super.key});

@override
Widget build(BuildContext context, WidgetRef ref) {
return SearchField(
text: ref.watch(thingsFilterProvider),
onChanged: (value) {
ref.read(thingsFilterProvider.notifier).state = value;
},
onClearPressed: () {
ref.read(thingsFilterProvider.notifier).state = null;
ref.read(selectedThingProvider.notifier).state = null;
},
trailing: const Tooltip(
message: 'Item Lookup',
child: ItemLookupButton(),
),
);
}
}
Loading

0 comments on commit 3ba4bb4

Please sign in to comment.