diff --git a/lib/redux/project/project_selectors.dart b/lib/redux/project/project_selectors.dart index 6ed863540..26ccd49b5 100644 --- a/lib/redux/project/project_selectors.dart +++ b/lib/redux/project/project_selectors.dart @@ -70,3 +70,40 @@ List filteredProjectsSelector( return list; } + +var memoizedProjectStatsForClient = memo4((int clientId, + BuiltMap projectMap, + String activeLabel, + String archivedLabel) => + projectStatsForClient(clientId, projectMap, activeLabel, archivedLabel)); + +String projectStatsForClient( + int clientId, + BuiltMap projectMap, + String activeLabel, + String archivedLabel) { + int countActive = 0; + int countArchived = 0; + projectMap.forEach((projectId, project) { + if (project.clientId == clientId) { + if (project.isActive) { + countActive++; + } else if (project.isArchived) { + countArchived++; + } + } + }); + + String str = ''; + if (countActive > 0) { + str = '$countActive $activeLabel'; + if (countArchived > 0) { + str += ' • '; + } + } + if (countArchived > 0) { + str += '$countArchived $archivedLabel'; + } + + return str; +} diff --git a/lib/ui/app/app_drawer.dart b/lib/ui/app/app_drawer.dart index 61870dbca..c4b79f775 100644 --- a/lib/ui/app/app_drawer.dart +++ b/lib/ui/app/app_drawer.dart @@ -10,6 +10,7 @@ import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; import 'package:invoiceninja_flutter/redux/product/product_actions.dart'; import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart'; import 'package:invoiceninja_flutter/ui/settings/settings_screen.dart'; +import 'package:invoiceninja_flutter/utils/icons.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; @@ -152,7 +153,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.client, - icon: FontAwesomeIcons.users, + icon: getEntityIcon(EntityType.client), title: localization.clients, onTap: () => store.dispatch(ViewClientList(context)), onCreateTap: () { @@ -164,7 +165,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.product, - icon: FontAwesomeIcons.cube, + icon: getEntityIcon(EntityType.product), title: localization.products, onTap: () { store.dispatch(ViewProductList(context)); @@ -178,7 +179,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.invoice, - icon: FontAwesomeIcons.filePdf, + icon: getEntityIcon(EntityType.invoice), title: localization.invoices, onTap: () => store.dispatch(ViewInvoiceList(context)), onCreateTap: () { @@ -190,7 +191,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.payment, - icon: FontAwesomeIcons.creditCard, + icon: getEntityIcon(EntityType.payment), title: localization.payments, onTap: () => store.dispatch(ViewPaymentList(context)), onCreateTap: () { @@ -202,7 +203,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.quote, - icon: FontAwesomeIcons.fileAlt, + icon: getEntityIcon(EntityType.quote), title: localization.quotes, onTap: () => store.dispatch(ViewQuoteList(context)), onCreateTap: () { @@ -215,7 +216,7 @@ class AppDrawer extends StatelessWidget { DrawerTile( company: company, entityType: EntityType.project, - icon: FontAwesomeIcons.briefcase, + icon: getEntityIcon(EntityType.project), title: localization.projects, onTap: () => store.dispatch(ViewProjectList(context)), onCreateTap: () { diff --git a/lib/ui/client/view/client_view_overview.dart b/lib/ui/client/view/client_view_overview.dart index c4bea3e89..28564fc90 100644 --- a/lib/ui/client/view/client_view_overview.dart +++ b/lib/ui/client/view/client_view_overview.dart @@ -3,15 +3,16 @@ import 'package:invoiceninja_flutter/data/models/client_model.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.dart'; +import 'package:invoiceninja_flutter/redux/project/project_selectors.dart'; import 'package:invoiceninja_flutter/redux/payment/payment_selectors.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart'; import 'package:invoiceninja_flutter/ui/app/FieldGrid.dart'; import 'package:invoiceninja_flutter/ui/client/view/client_view_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:invoiceninja_flutter/ui/app/icon_message.dart'; import 'package:invoiceninja_flutter/ui/app/two_value_header.dart'; +import 'package:invoiceninja_flutter/utils/icons.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; class ClientOverview extends StatelessWidget { @@ -67,7 +68,7 @@ class ClientOverview extends StatelessWidget { height: 1.0, ), EntityListTile( - icon: FontAwesomeIcons.filePdf, + icon: getEntityIcon(EntityType.invoice), title: localization.invoices, onTap: () => viewModel.onEntityPressed(context, EntityType.invoice), subtitle: memoizedInvoiceStatsForClient( @@ -77,7 +78,7 @@ class ClientOverview extends StatelessWidget { localization.archived), ), EntityListTile( - icon: FontAwesomeIcons.creditCard, + icon: getEntityIcon(EntityType.payment), title: localization.payments, onTap: () => viewModel.onEntityPressed(context, EntityType.payment), subtitle: memoizedPaymentStatsForClient( @@ -87,16 +88,32 @@ class ClientOverview extends StatelessWidget { localization.active, localization.archived), ), - company.isModuleEnabled(EntityType.quote) ? EntityListTile( - icon: FontAwesomeIcons.fileAlt, - title: localization.quotes, - onTap: () => viewModel.onEntityPressed(context, EntityType.quote), - subtitle: memoizedQuoteStatsForClient( - client.id, - state.quoteState.map, - localization.active, - localization.archived), - ) : Container(), + company.isModuleEnabled(EntityType.quote) + ? EntityListTile( + icon: getEntityIcon(EntityType.quote), + title: localization.quotes, + onTap: () => + viewModel.onEntityPressed(context, EntityType.quote), + subtitle: memoizedQuoteStatsForClient( + client.id, + state.quoteState.map, + localization.active, + localization.archived), + ) + : Container(), + company.isModuleEnabled(EntityType.project) + ? EntityListTile( + icon: getEntityIcon(EntityType.project), + title: localization.projects, + onTap: () => + viewModel.onEntityPressed(context, EntityType.project), + subtitle: memoizedProjectStatsForClient( + client.id, + state.projectState.map, + localization.active, + localization.archived), + ) + : Container(), ], ); } diff --git a/lib/utils/icons.dart b/lib/utils/icons.dart index 7d10a1f6d..ac8de93f4 100644 --- a/lib/utils/icons.dart +++ b/lib/utils/icons.dart @@ -38,6 +38,8 @@ IconData getEntityIcon(EntityType entityType) { switch (entityType) { case EntityType.product: return FontAwesomeIcons.cube; + case EntityType.project: + return FontAwesomeIcons.cube; case EntityType.client: return FontAwesomeIcons.users; case EntityType.invoice: