invoice/lib/ui/dashboard/dashboard_sidebar.dart

370 lines
13 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_sidebar_selectors.dart';
import 'package:invoiceninja_flutter/ui/app/help_text.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.dart';
import 'package:invoiceninja_flutter/ui/payment/payment_list_item.dart';
import 'package:invoiceninja_flutter/ui/quote/quote_list_item.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
class SidebarScaffold extends StatelessWidget {
const SidebarScaffold({
@required this.tabController,
});
final TabController tabController;
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final company = state.company;
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
title: Row(
children: [
Expanded(
child: TabBar(
isScrollable: true,
controller: tabController,
tabs: [
if (company.isModuleEnabled(EntityType.invoice))
Tab(
text: localization.invoices,
),
if (company.isModuleEnabled(EntityType.payment))
Tab(
text: localization.payments,
),
if (company.isModuleEnabled(EntityType.quote))
Tab(
text: localization.quotes,
),
],
),
),
IconButton(
icon: Icon(Icons.clear),
onPressed: () => store.dispatch(
UpdateDashboardSidebar(showSidebar: false),
),
),
],
),
),
body: TabBarView(
controller: tabController,
children: [
if (company.isModuleEnabled(EntityType.invoice)) InvoiceSidebar(),
if (company.isModuleEnabled(EntityType.payment)) PaymentSidebar(),
if (company.isModuleEnabled(EntityType.quote)) QuoteSidebar(),
],
),
);
}
}
class InvoiceSidebar extends StatelessWidget {
const InvoiceSidebar();
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final upcomingInvoices = memoizedUpcomingInvoices(state.invoiceState.map);
final pastDueInvoices = memoizedPastDueInvoices(state.invoiceState.map);
final selectedIds =
state.dashboardUIState.selectedEntities[EntityType.invoice];
return _DashboardSidebar(
entityType: EntityType.invoice,
label1: localization.upcomingInvoices +
(upcomingInvoices.isNotEmpty ? ' (${upcomingInvoices.length})' : ''),
list1: upcomingInvoices.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: upcomingInvoices.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceListItem(
invoice: upcomingInvoices[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label2: localization.pastDueInvoices +
(pastDueInvoices.isNotEmpty ? ' (${pastDueInvoices.length})' : ''),
list2: pastDueInvoices.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: pastDueInvoices.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceListItem(
invoice: pastDueInvoices[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label3: (selectedIds ?? <String>[]).isEmpty
? null
: localization.selectedInvoices + ' (${selectedIds.length})',
list3: (selectedIds ?? <String>[]).isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: selectedIds?.length,
itemBuilder: (BuildContext context, int index) {
final invoice = state.invoiceState.map[selectedIds[index]];
return invoice == null
? SizedBox()
: InvoiceListItem(
invoice: invoice,
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
);
}
}
class PaymentSidebar extends StatelessWidget {
const PaymentSidebar();
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final recentPayments = memoizedRecentPayments(state.paymentState.map);
final selectedIds =
state.dashboardUIState.selectedEntities[EntityType.payment];
return _DashboardSidebar(
entityType: EntityType.payment,
label1: localization.recentPayments +
(recentPayments.isNotEmpty ? ' (${recentPayments.length})' : ''),
list1: recentPayments.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: recentPayments.length,
itemBuilder: (BuildContext context, int index) {
return PaymentListItem(
payment: recentPayments[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label3: (selectedIds ?? <String>[]).isEmpty
? null
: localization.selectedPayments + ' (${selectedIds.length})',
list3: (selectedIds ?? <String>[]).isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: selectedIds?.length,
itemBuilder: (BuildContext context, int index) {
final payment = state.paymentState.map[selectedIds[index]];
return payment == null
? SizedBox()
: PaymentListItem(
payment: payment,
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
);
}
}
class QuoteSidebar extends StatelessWidget {
const QuoteSidebar();
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final upcomingQuotes = memoizedUpcomingQuotes(state.quoteState.map);
final expriedQuotes = memoizedExpiredQuotes(state.quoteState.map);
final selectedIds =
state.dashboardUIState.selectedEntities[EntityType.quote];
return _DashboardSidebar(
entityType: EntityType.quote,
label1: localization.upcomingQuotes +
(upcomingQuotes.isNotEmpty ? ' (${upcomingQuotes.length})' : ''),
list1: upcomingQuotes.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: upcomingQuotes.length,
itemBuilder: (BuildContext context, int index) {
return QuoteListItem(
quote: upcomingQuotes[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label2: localization.expiredQuotes +
(expriedQuotes.isNotEmpty ? ' (${expriedQuotes.length})' : ''),
list2: expriedQuotes.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: expriedQuotes.length,
itemBuilder: (BuildContext context, int index) {
return QuoteListItem(
quote: expriedQuotes[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label3: (selectedIds ?? <String>[]).isEmpty
? null
: localization.selectedQuotes + ' (${selectedIds.length})',
list3: (selectedIds ?? <String>[]).isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: selectedIds?.length,
itemBuilder: (BuildContext context, int index) {
final quote = state.quoteState.map[selectedIds[index]];
return quote == null
? SizedBox()
: QuoteListItem(
quote: quote,
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
);
}
}
class _DashboardSidebar extends StatelessWidget {
const _DashboardSidebar({
@required this.entityType,
@required this.label1,
@required this.list1,
this.label2,
this.list2,
this.label3,
this.list3,
});
final EntityType entityType;
final String label1;
final String label2;
final String label3;
final ListView list1;
final ListView list2;
final ListView list3;
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final textTheme = Theme.of(context).textTheme;
final store = StoreProvider.of<AppState>(context);
return Container(
color: Theme.of(context).cardColor,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: 4,
color: Theme.of(context).scaffoldBackgroundColor,
child: Container(
child: Text(label1, style: textTheme.bodyText2),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
width: double.infinity,
),
),
Expanded(
child: list1 == null
? HelpText(localization.noRecordsFound)
: ClipRRect(child: list1),
),
if (label2 != null) ...[
Material(
elevation: 4,
color: Theme.of(context).scaffoldBackgroundColor,
child: Container(
child: Text(label2, style: textTheme.bodyText2),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
width: double.infinity,
),
),
Expanded(
child: list2 == null
? HelpText(localization.noRecordsFound)
: ClipRRect(child: list2),
),
],
AnimatedContainer(
height: label3 == null
? 0
: (MediaQuery.of(context).size.height - 100) / 2,
duration: Duration(milliseconds: kDefaultAnimationDuration),
curve: Curves.easeInOutCubic,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Material(
elevation: 4,
color: Theme.of(context).scaffoldBackgroundColor,
child: Container(
child: Row(
children: [
Expanded(
child:
Text(label3 ?? '', style: textTheme.bodyText2)),
IconButton(
visualDensity: VisualDensity.compact,
icon: Icon(Icons.clear),
onPressed: () {
store.dispatch(UpdateDashboardSelection(
entityIds: null,
entityType: entityType,
));
},
)
],
),
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
width: double.infinity,
),
),
Expanded(
child: ClipRRect(child: list3 ?? SizedBox()),
),
],
),
),
],
),
);
}
}