Skip to content

Commit

Permalink
General Improvements (#105)
Browse files Browse the repository at this point in the history
* Combine filters

* Fix selected option overlay

* Order items in value selection dialog

* Add search to value selection dialog

* Fix search field

* Move and refactor dialogs

* Refactor filters

* Fix borrowing rules tests

* Polish

* Refactor user selection dialog to use value selection dialog

* Switch User Filter to users collection

* Refactor multiselect button

* Refactor filters into a provider
  • Loading branch information
ANDREYDEN authored Apr 8, 2024
1 parent b276e7e commit 9763f72
Show file tree
Hide file tree
Showing 26 changed files with 592 additions and 703 deletions.
4 changes: 2 additions & 2 deletions lib/business_logic/item_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import 'package:spare_parts/utilities/constants.dart';
import 'package:spare_parts/utilities/helpers.dart';
import 'package:spare_parts/widgets/dialogs/print_dialog/print_dialog_mobile.dart'
if (dart.library.html) 'package:spare_parts/widgets/dialogs/print_dialog/print_dialog_web.dart';
import 'package:spare_parts/widgets/inputs/user_selection_dialog.dart';
import 'package:spare_parts/widgets/dialogs/user_selection_dialog.dart';
import 'package:spare_parts/widgets/inventory_list_item/inventory_item_form.dart';

import '../services/repositories/repositories.dart';
Expand Down Expand Up @@ -97,7 +97,7 @@ class AssignItemAction extends ItemAction {
final users = await showDialog<List<CustomUser>?>(
context: context,
builder: (context) => UserSelectionDialog(
title: 'Select user',
title: 'Select User',
isSingleSelection: true,
selectedUsers: item.borrower == null ? [] : [item.borrower!],
),
Expand Down
10 changes: 10 additions & 0 deletions lib/entities/custom_user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,14 @@ class CustomUser {
'photoURL': photoURL,
};
}

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;

return other is CustomUser && other.uid == uid;
}

@override
int get hashCode => uid.hashCode;
}
4 changes: 3 additions & 1 deletion lib/factories/inventory_item_factory.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'package:spare_parts/entities/custom_user.dart';
import 'package:spare_parts/entities/inventory_item.dart';
import 'package:spare_parts/factories/factory.dart';

class InventoryItemFactory extends Factory<InventoryItem> {
@override
InventoryItem create({String? name}) {
InventoryItem create({String? name, CustomUser? borrower}) {
return InventoryItem(
id: faker.guid.guid(),
type: faker.company.suffix(),
name: name ?? faker.company.name(),
borrower: borrower
);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/inventory_view_filter_selection.dart';

class AvailableItemsFilter extends StatelessWidget {
final bool value;
final void Function() onPressed;
const AvailableItemsFilter({super.key});

const AvailableItemsFilter({
super.key,
required this.value,
required this.onPressed,
});
void onPressed(BuildContext context) {
final selection = context.read<InventoryViewFilterSelection>();
selection.toggleShowOnlyAvailableItems();
}

@override
Widget build(BuildContext context) {
final value = context.select<InventoryViewFilterSelection, bool>(
(selection) => selection.showOnlyAvailableItems);

return TextButton.icon(
label: Text('Only available items'),
icon: Icon(
Expand All @@ -20,7 +23,7 @@ class AvailableItemsFilter extends StatelessWidget {
style: TextButton.styleFrom(
foregroundColor: Theme.of(context).textTheme.bodyLarge!.color,
),
onPressed: onPressed,
onPressed: () => onPressed(context),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import 'package:flutter/foundation.dart';
import 'package:spare_parts/entities/custom_user.dart';

class InventoryViewFilterSelection extends ChangeNotifier {
List<String> selectedItemTypes = [];
List<CustomUser> selectedBorrowers = [];
bool showOnlyAvailableItems = false;

InventoryViewFilterSelection({
this.selectedItemTypes = const [],
this.selectedBorrowers = const [],
this.showOnlyAvailableItems = false,
});

void updateSelectedItemTypes(List<String> selectedItemTypes) {
this.selectedItemTypes = selectedItemTypes;
notifyListeners();
}

void updateSelectedBorrowers(List<CustomUser> selectedBorrowers) {
this.selectedBorrowers = selectedBorrowers;
notifyListeners();
}

void toggleShowOnlyAvailableItems() {
showOnlyAvailableItems = !showOnlyAvailableItems;
notifyListeners();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/available_items_filter.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/item_type_filter.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/user_filter.dart';
import 'package:spare_parts/utilities/constants.dart';

class InventoryViewFilters extends StatelessWidget {
const InventoryViewFilters({
super.key,
});

@override
Widget build(BuildContext context) {
bool isAdmin = context.watch<UserRole>() == UserRole.admin;

return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
SizedBox(width: 5),
ItemTypeFilter(),
if (isAdmin) ...[
SizedBox(width: 10),
UserFilter(),
SizedBox(width: 10),
AvailableItemsFilter(),
],
],
),
);
}
}
37 changes: 37 additions & 0 deletions lib/pages/home_page/inventory_view/filters/item_type_filter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/inventory_view_filter_selection.dart';
import 'package:spare_parts/utilities/constants.dart';
import 'package:spare_parts/widgets/dialogs/value_selection_dialog.dart';
import 'package:spare_parts/widgets/inputs/multiselect_button.dart';

class ItemTypeFilter extends StatelessWidget {
const ItemTypeFilter({
super.key,
});

void onChanged(BuildContext context, List<String> selectedItemTypes) {
final selection = context.read<InventoryViewFilterSelection>();
selection.updateSelectedItemTypes(selectedItemTypes);
}

@override
Widget build(BuildContext context) {
final selectedItemTypes =
context.select<InventoryViewFilterSelection, List<String>>(
(selection) => selection.selectedItemTypes);

return MultiselectButton<String>(
buttonLabel: 'Item Types',
hasSelection: selectedItemTypes.isNotEmpty,
onConfirm: (values) => onChanged(context, values),
dialog: ValueSelectionDialog<String>(
values: itemTypes.keys.toList(),
selectedValues: selectedItemTypes,
title: 'Pick Item Types',
leadingBuilder: (itemType) =>
Icon(itemTypes[itemType] ?? itemTypes['Other']!),
),
);
}
}
36 changes: 0 additions & 36 deletions lib/pages/home_page/inventory_view/filters/search_field.dart

This file was deleted.

71 changes: 20 additions & 51 deletions lib/pages/home_page/inventory_view/filters/user_filter.dart
Original file line number Diff line number Diff line change
@@ -1,65 +1,34 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spare_parts/dtos/user_dto.dart';
import 'package:spare_parts/services/callable_service.dart';
import 'package:spare_parts/entities/custom_user.dart';
import 'package:spare_parts/pages/home_page/inventory_view/filters/inventory_view_filter_selection.dart';
import 'package:spare_parts/widgets/dialogs/user_selection_dialog.dart';
import 'package:spare_parts/widgets/inputs/multiselect_button.dart';
import 'package:spare_parts/widgets/user_avatar.dart';

class UserFilter extends StatefulWidget {
final List<String> selectedUsers;
final void Function(List<String>) onChanged;
final IconData? icon;

class UserFilter extends StatelessWidget {
const UserFilter({
super.key,
required this.selectedUsers,
required this.onChanged,
this.icon,
super.key
});

@override
State<UserFilter> createState() => _UserFilterState();
}

class _UserFilterState extends State<UserFilter> {
late Future<List<UserDto>> _userQuery;

@override
void initState() {
final callableService = context.read<CallableService>();
_userQuery = callableService.getUsers();

super.initState();
void onChanged(BuildContext context, List<CustomUser> selectedUsers) {
final selection = context.read<InventoryViewFilterSelection>();
selection.updateSelectedBorrowers(selectedUsers);
}

@override
Widget build(BuildContext context) {
return FutureBuilder<List<UserDto>>(
future: _userQuery,
builder: (context, snap) {
if (!snap.hasData || snap.hasError) {
return SizedBox.square(
dimension: 20,
child: CircularProgressIndicator(strokeWidth: 3),
);
}

final users = snap.data!;

return MultiselectButton(
buttonLabel: 'Borrowers',
values: users.map((u) => u.id).toList(),
selectedValues: widget.selectedUsers,
onConfirm: widget.onChanged,
icon: widget.icon,
labelBuilder: (uid) =>
users.singleWhere((user) => user.id == uid).name,
leadingBuilder: (uid) {
final user = users.singleWhere((user) => user.id == uid);
return UserAvatar(photoUrl: user.photoUrl);
},
);
},
final selectedUsers =
context.select<InventoryViewFilterSelection, List<CustomUser>>(
(selection) => selection.selectedBorrowers);

return MultiselectButton<CustomUser>(
buttonLabel: 'Borrowers',
hasSelection: selectedUsers.isNotEmpty,
onConfirm: (values) => onChanged(context, values),
dialog: UserSelectionDialog(
selectedUsers: selectedUsers,
title: 'Pick Borrowers',
),
);
}
}
Loading

0 comments on commit 9763f72

Please sign in to comment.