From ea075f35e827b11e60561e855291a51e947c2ecf Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 30 May 2026 14:19:32 +1000 Subject: [PATCH] User info (#826) * Enhanced user information in "about" view * Add user info to nav drawer * Update release notes --- assets/release_notes.md | 1 + lib/api.dart | 3 ++ lib/l10n/app_en.arb | 3 ++ lib/settings/about.dart | 59 ++++++++++++++++++++------ lib/widget/drawer.dart | 91 ++++++++++++++++++++++++----------------- 5 files changed, 106 insertions(+), 51 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index 72f45c18..44aecd6c 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -1,6 +1,7 @@ ## 0.24.2 - May 2026 --- +- Display user information in the app drawer - Support "creation_date" field for stock items - Moves notifications to the top of the screen - Updated translations diff --git a/lib/api.dart b/lib/api.dart index f8fd4218..03bcd152 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -277,6 +277,9 @@ class InvenTreeAPI { Map userInfo = {}; String get username => (userInfo["username"] ?? "") as String; + String get userEmail => (userInfo["email"] ?? "") as String; + String get userFirstName => (userInfo["first_name"] ?? "") as String; + String get userLastName => (userInfo["last_name"] ?? "") as String; int get userId => (userInfo["pk"] ?? -1) as int; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f3bc6152..1ed200e9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1852,6 +1852,9 @@ "usernameEmpty": "Username cannot be empty", "@usernameEmpty": {}, + "userDetails": "User Details", + "@userDetails": {}, + "value": "Value", "@value": { "description": "value" diff --git a/lib/settings/about.dart b/lib/settings/about.dart index fa11eb3b..f451ba29 100644 --- a/lib/settings/about.dart +++ b/lib/settings/about.dart @@ -93,19 +93,7 @@ class InvenTreeAboutWidget extends StatelessWidget { : L10().notConnected, ), leading: Icon(TablerIcons.globe), - trailing: InvenTreeAPI().isConnected() - ? Icon(TablerIcons.circle_check, color: COLOR_SUCCESS) - : Icon(TablerIcons.circle_x, color: COLOR_DANGER), - ), - ); - - tiles.add( - ListTile( - title: Text(L10().username), - subtitle: Text(InvenTreeAPI().username), - leading: InvenTreeAPI().username.isNotEmpty - ? Icon(TablerIcons.user) - : Icon(TablerIcons.user_cancel, color: COLOR_DANGER), + trailing: Icon(TablerIcons.circle_check, color: COLOR_SUCCESS), ), ); @@ -141,6 +129,51 @@ class InvenTreeAboutWidget extends StatelessWidget { leading: Icon(TablerIcons.plug), ), ); + + tiles.add( + ListTile( + title: Text( + L10().userDetails, + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + ); + + tiles.add( + ListTile( + title: Text(L10().username), + subtitle: Text(InvenTreeAPI().username), + leading: InvenTreeAPI().username.isNotEmpty + ? Icon(TablerIcons.user) + : Icon(TablerIcons.user_cancel, color: COLOR_DANGER), + ), + ); + + final String email = InvenTreeAPI().userEmail; + + if (email.isNotEmpty) { + tiles.add( + ListTile( + title: Text(L10().email), + subtitle: Text(email), + leading: Icon(TablerIcons.at), + ), + ); + } + + final String firstName = InvenTreeAPI().userFirstName; + final String lastName = InvenTreeAPI().userLastName; + final String fullName = "$firstName $lastName".trim(); + + if (fullName.isNotEmpty) { + tiles.add( + ListTile( + title: Text(L10().name), + subtitle: Text(fullName), + leading: Icon(TablerIcons.id_badge), + ), + ); + } } else { tiles.add( ListTile( diff --git a/lib/widget/drawer.dart b/lib/widget/drawer.dart index 738d9bf4..e25c7920 100644 --- a/lib/widget/drawer.dart +++ b/lib/widget/drawer.dart @@ -1,6 +1,7 @@ import "package:adaptive_theme/adaptive_theme.dart"; import "package:flutter/material.dart"; import "package:flutter_tabler_icons/flutter_tabler_icons.dart"; +import "package:package_info_plus/package_info_plus.dart"; import "package:inventree/api.dart"; import "package:inventree/app_colors.dart"; @@ -10,6 +11,7 @@ import "package:inventree/inventree/purchase_order.dart"; import "package:inventree/inventree/sales_order.dart"; import "package:inventree/inventree/stock.dart"; import "package:inventree/l10.dart"; +import "package:inventree/settings/about.dart"; import "package:inventree/settings/settings.dart"; import "package:inventree/widget/build/build_list.dart"; import "package:inventree/widget/order/sales_order_list.dart"; @@ -79,63 +81,56 @@ class ThemeSelectionDialog extends StatelessWidget { } } -class InvenTreeDrawer extends StatelessWidget { - const InvenTreeDrawer(this.context); +class InvenTreeDrawer extends StatefulWidget { + const InvenTreeDrawer(this.parentContext); - final BuildContext context; + final BuildContext parentContext; + @override + State createState() => _InvenTreeDrawerState(); +} + +class _InvenTreeDrawerState extends State { void _closeDrawer() { - // Close the drawer - Navigator.of(context).pop(); + Navigator.of(widget.parentContext).pop(); } bool _checkConnection() { return InvenTreeAPI().checkConnection(); } - /* - * Return to the 'home' screen. - * This will empty the navigation stack. - */ void _home() { _closeDrawer(); - - while (Navigator.of(context).canPop()) { - Navigator.of(context).pop(); + while (Navigator.of(widget.parentContext).canPop()) { + Navigator.of(widget.parentContext).pop(); } } - // Load "parts" page void _parts() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute(builder: (context) => CategoryDisplayWidget(null)), ); } } - // Load "stock" page void _stock() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute(builder: (context) => LocationDisplayWidget(null)), ); } } - // Load "sales orders" page void _salesOrders() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute( builder: (context) => SalesOrderListWidget(filters: {}), ), @@ -143,13 +138,11 @@ class InvenTreeDrawer extends StatelessWidget { } } - // Load "purchase orders" page void _purchaseOrders() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute( builder: (context) => PurchaseOrderListWidget(filters: {}), ), @@ -157,13 +150,11 @@ class InvenTreeDrawer extends StatelessWidget { } } - // Load "build orders" page void _buildOrders() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute( builder: (context) => BuildOrderListWidget(filters: {}), ), @@ -171,28 +162,34 @@ class InvenTreeDrawer extends StatelessWidget { } } - // Load notifications screen void _notifications() { _closeDrawer(); - if (_checkConnection()) { Navigator.push( - context, + widget.parentContext, MaterialPageRoute(builder: (context) => NotificationWidget()), ); } } - // Load settings widget void _settings() { _closeDrawer(); Navigator.push( - context, + widget.parentContext, MaterialPageRoute(builder: (context) => InvenTreeSettingsWidget()), ); } - // Return an icon representing the current theme mode + void _about() { + _closeDrawer(); + PackageInfo.fromPlatform().then((PackageInfo info) { + Navigator.push( + widget.parentContext, + MaterialPageRoute(builder: (context) => InvenTreeAboutWidget(info)), + ); + }); + } + Widget _getThemeModeIcon(AdaptiveThemeMode mode) { switch (mode) { case AdaptiveThemeMode.dark: @@ -204,11 +201,23 @@ class InvenTreeDrawer extends StatelessWidget { } } - // Construct list of tiles to display in the "drawer" menu + Widget? _buildUserTile() { + if (!InvenTreeAPI().isConnected()) return null; + + final String username = InvenTreeAPI().username; + final String email = InvenTreeAPI().userEmail; + + return ListTile( + leading: Icon(TablerIcons.user_circle, color: COLOR_ACTION), + title: Text(username), + subtitle: email.isNotEmpty ? Text(email) : null, + onTap: _about, + ); + } + List drawerTiles(BuildContext context) { List tiles = []; - // "Home" access tiles.add( ListTile( leading: Image.asset("assets/image/logo_transparent.png", height: 24), @@ -276,13 +285,13 @@ class InvenTreeDrawer extends StatelessWidget { tiles.add(Divider()); } - int notification_count = InvenTreeAPI().notification_counter; + final int notificationCount = InvenTreeAPI().notification_counter; tiles.add( ListTile( leading: Icon(TablerIcons.bell, color: COLOR_ACTION), - trailing: notification_count > 0 - ? Text(notification_count.toString()) + trailing: notificationCount > 0 + ? Text(notificationCount.toString()) : null, title: Text(L10().notifications), onTap: _notifications, @@ -321,6 +330,12 @@ class InvenTreeDrawer extends StatelessWidget { ), ); + final Widget? userTile = _buildUserTile(); + if (userTile != null) { + tiles.add(Divider()); + tiles.add(userTile); + } + return tiles; }