diff --git a/lib/l10n b/lib/l10n index 15a67a8a..7cf4974a 160000 --- a/lib/l10n +++ b/lib/l10n @@ -1 +1 @@ -Subproject commit 15a67a8aa7ec5af876b0467152de13baff8f7806 +Subproject commit 7cf4974a05bd78fa5efbea422e456ddb8a542441 diff --git a/lib/widget/category_display.dart b/lib/widget/category_display.dart index e6da65ae..04f03a74 100644 --- a/lib/widget/category_display.dart +++ b/lib/widget/category_display.dart @@ -481,7 +481,7 @@ class _PaginatedPartListState extends State { return _buildPart(context, item); }, noItemsFoundIndicatorBuilder: (context) { - return NoResultsWidget("No parts found"); + return NoResultsWidget(L10().partNoResults); }, ), separatorBuilder: (context, index) => const Divider(height: 1), diff --git a/lib/widget/company_list.dart b/lib/widget/company_list.dart index 29ec6a10..15a4af98 100644 --- a/lib/widget/company_list.dart +++ b/lib/widget/company_list.dart @@ -1,128 +1,195 @@ -import 'package:inventree/widget/company_detail.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart'; import 'package:inventree/api.dart'; import 'package:inventree/inventree/company.dart'; +import 'package:inventree/inventree/sentry.dart'; +import 'package:inventree/widget/paginator.dart'; import 'package:inventree/widget/refreshable_state.dart'; +import 'package:inventree/widget/company_detail.dart'; -abstract class CompanyListWidget extends StatefulWidget { +import '../l10.dart'; - final String title = ""; - Map filters = {}; + +class CompanyListWidget extends StatefulWidget { + + CompanyListWidget(this.title, this.filters, {Key? key}) : super(key: key); + + String title; + + Map filters; @override - _CompanyListState createState() => _CompanyListState(title, filters); - -} - -class SupplierListWidget extends CompanyListWidget { - @override - _CompanyListState createState() => _CompanyListState("Suppliers", {"is_supplier": "true"}); + _CompanyListWidgetState createState() => _CompanyListWidgetState(title, filters); } -class ManufacturerListWidget extends CompanyListWidget { - @override - _CompanyListState createState() => _CompanyListState("Manufacturers", {"is_manufacturer": "true"}); -} +class _CompanyListWidgetState extends RefreshableState { + _CompanyListWidgetState(this.title, this.filters); -class CustomerListWidget extends CompanyListWidget { - @override - _CompanyListState createState() => _CompanyListState("Customers", {"is_customer": "true"}); -} + final String title; - -class _CompanyListState extends RefreshableState { - - List _companies = []; - - List _filteredCompanies = []; - - String _title = "Companies"; + final Map filters; @override - String getAppBarTitle(BuildContext context) { return _title; } - - Map _filters = Map(); - - _CompanyListState(this._title, this._filters); - - @override - Future onBuild(BuildContext context) async { - refresh(); - } - - @override - Future request() async { - - await InvenTreeCompany().list(filters: _filters).then((var companies) { - - _companies.clear(); - - for (var c in companies) { - if (c is InvenTreeCompany) { - _companies.add(c); - } - } - - setState(() { - _filterResults(""); - }); - - }); - } - - void _filterResults(String text) { - - if (text.isEmpty) { - _filteredCompanies = _companies; - } else { - _filteredCompanies = _companies.where((c) => c.filter(text)).toList(); - } - } - - Widget _showCompany(BuildContext context, int index) { - - InvenTreeCompany company = _filteredCompanies[index]; - - return ListTile( - title: Text("${company.name}"), - subtitle: Text("${company.description}"), - leading: InvenTreeAPI().getImage(company.image), - onTap: () { - if (company.pk > 0) { - InvenTreeCompany().get(company.pk).then((var c) { - if (c != null && c is InvenTreeCompany) { - Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(c))); - } - }); - } - }, - ); - } + String getAppBarTitle(BuildContext context) => title; @override Widget getBody(BuildContext context) { - return ListView( - children: [ - TextField( - decoration: InputDecoration( - hintText: 'Filter results', - ), - onChanged: (String text) { - setState(() { - _filterResults(text); - }); - }, - ), - ListView.builder( - shrinkWrap: true, + + return PaginatedCompanyList(filters); + + } + +} + + +class PaginatedCompanyList extends StatefulWidget { + + PaginatedCompanyList(this.filters, {this.onTotalChanged}); + + final Map filters; + + Function(int)? onTotalChanged; + + @override + _CompanyListState createState() => _CompanyListState(filters, onTotalChanged); +} + +class _CompanyListState extends State { + + _CompanyListState(this.filters, this.onTotalChanged); + + static const _pageSize = 25; + + String _searchTerm = ""; + + Function(int)? onTotalChanged; + + final Map filters; + + final PagingController _pagingController = PagingController(firstPageKey: 0); + + final TextEditingController searchController = TextEditingController(); + + @override + void initState() { + _pagingController.addPageRequestListener((pageKey) { + _fetchPage(pageKey); + }); + + super.initState(); + } + + @override + void dispose() { + _pagingController.dispose(); + super.dispose(); + } + + int resultCount = 0; + + Future _fetchPage(int pageKey) async { + try { + Map params = filters; + + params["search"] = _searchTerm; + + final page = await InvenTreeCompany().listPaginated( + _pageSize, pageKey, filters: params); + + int pageLength = page?.length ?? 0; + int pageCount = page?.count ?? 0; + + final isLastPage = pageLength < _pageSize; + + List companies = []; + + if (page != null) { + for (var result in page.results) { + if (result is InvenTreeCompany) { + companies.add(result); + } else { + print(result.jsondata); + } + } + } + + if (isLastPage) { + _pagingController.appendLastPage(companies); + } else { + final int nextPageKey = pageKey + pageLength; + _pagingController.appendPage(companies, nextPageKey); + } + + if (onTotalChanged != null) { + onTotalChanged!(pageCount); + } + + setState(() { + resultCount = pageCount; + }); + } catch (error, stackTrace) { + print("Error! - ${error.toString()}"); + _pagingController.error = error; + + sentryReportError(error, stackTrace); + } + } + + void updateSearchTerm() { + _searchTerm = searchController.text; + _pagingController.refresh(); + } + + Widget _buildCompany(BuildContext context, InvenTreeCompany company) { + + return ListTile( + title: Text(company.name), + subtitle: Text(company.description), + leading: InvenTreeAPI().getImage( + company.image, + width: 40, + height: 40 + ), + onTap: () async { + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyDetailWidget(company))); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + PaginatedSearchWidget(searchController, updateSearchTerm, resultCount), + Expanded( + child: CustomScrollView( + shrinkWrap: true, physics: ClampingScrollPhysics(), - itemBuilder: _showCompany, itemCount: _filteredCompanies.length) + scrollDirection: Axis.vertical, + slivers: [ + PagedSliverList.separated( + pagingController: _pagingController, + builderDelegate: PagedChildBuilderDelegate( + itemBuilder: (context, item, index) { + return _buildCompany(context, item); + }, + noItemsFoundIndicatorBuilder: (context) { + return NoResultsWidget(L10().companyNoResults); + } + ), + separatorBuilder: (context, index) => const Divider(height: 1), + ) + ], + ) + ) ], ); } + } \ No newline at end of file diff --git a/lib/widget/drawer.dart b/lib/widget/drawer.dart index 6f6f6bbb..41f0f7fe 100644 --- a/lib/widget/drawer.dart +++ b/lib/widget/drawer.dart @@ -80,21 +80,21 @@ class InvenTreeDrawer extends StatelessWidget { if (!InvenTreeAPI().checkConnection(context)) return; _closeDrawer(); - Navigator.push(context, MaterialPageRoute(builder: (context) => SupplierListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().suppliers, {"is_supplier": "true"}))); } void _showManufacturers() { if (!InvenTreeAPI().checkConnection(context)) return; _closeDrawer(); - Navigator.push(context, MaterialPageRoute(builder: (context) => ManufacturerListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().manufacturers, {"is_manufacturer": "true"}))); } void _showCustomers() { if (!InvenTreeAPI().checkConnection(context)) return; _closeDrawer(); - Navigator.push(context, MaterialPageRoute(builder: (context) => CustomerListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().customers, {"is_customer": "true"}))); } /* @@ -144,7 +144,7 @@ class InvenTreeDrawer extends StatelessWidget { leading: FaIcon(FontAwesomeIcons.boxes), onTap: _showStock, ), - /* + ListTile( title: Text("Suppliers"), leading: FaIcon(FontAwesomeIcons.building), @@ -160,7 +160,6 @@ class InvenTreeDrawer extends StatelessWidget { leading: FaIcon(FontAwesomeIcons.users), onTap: _showCustomers, ), - */ ListTile( title: Text(L10().settings), leading: Icon(Icons.settings), diff --git a/lib/widget/home.dart b/lib/widget/home.dart index b62dca09..58ffad5e 100644 --- a/lib/widget/home.dart +++ b/lib/widget/home.dart @@ -79,19 +79,19 @@ class _InvenTreeHomePageState extends State { void _suppliers() { if (!InvenTreeAPI().checkConnection(context)) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => SupplierListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().suppliers, {"is_supplier": "true"}))); } void _manufacturers() { if (!InvenTreeAPI().checkConnection(context)) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => ManufacturerListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().manufacturers, {"is_manufacturer": "true"}))); } void _customers() { if (!InvenTreeAPI().checkConnection(context)) return; - Navigator.push(context, MaterialPageRoute(builder: (context) => CustomerListWidget())); + Navigator.push(context, MaterialPageRoute(builder: (context) => CompanyListWidget(L10().customers, {"is_customer": "true"}))); } void _selectProfile() { diff --git a/lib/widget/part_suppliers.dart b/lib/widget/part_suppliers.dart index 20f55c88..c7a06fc5 100644 --- a/lib/widget/part_suppliers.dart +++ b/lib/widget/part_suppliers.dart @@ -8,6 +8,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:inventree/inventree/part.dart'; import 'package:inventree/inventree/company.dart'; +import 'package:inventree/widget/company_detail.dart'; import 'package:inventree/widget/refreshable_state.dart'; class PartSupplierWidget extends StatefulWidget { @@ -57,7 +58,19 @@ class _PartSupplierState extends RefreshableState { height: 40, ), title: Text("${_part.SKU}"), - subtitle: Text("${_part.manufacturerName}: ${_part.MPN}") + subtitle: Text("${_part.manufacturerName}: ${_part.MPN}"), + onTap: () async { + var company = await InvenTreeCompany().get(_part.supplierId); + + if (company != null && company is InvenTreeCompany) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => CompanyDetailWidget(company) + ) + ); + } + }, ); }