mirror of
https://github.com/inventree/inventree-app.git
synced 2026-06-10 08:27:15 +00:00
User info (#826)
* Enhanced user information in "about" view * Add user info to nav drawer * Update release notes
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -277,6 +277,9 @@ class InvenTreeAPI {
|
||||
Map<String, dynamic> 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;
|
||||
|
||||
|
||||
@@ -1852,6 +1852,9 @@
|
||||
"usernameEmpty": "Username cannot be empty",
|
||||
"@usernameEmpty": {},
|
||||
|
||||
"userDetails": "User Details",
|
||||
"@userDetails": {},
|
||||
|
||||
"value": "Value",
|
||||
"@value": {
|
||||
"description": "value"
|
||||
|
||||
+46
-13
@@ -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(
|
||||
|
||||
+53
-38
@@ -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<InvenTreeDrawer> createState() => _InvenTreeDrawerState();
|
||||
}
|
||||
|
||||
class _InvenTreeDrawerState extends State<InvenTreeDrawer> {
|
||||
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<Widget> drawerTiles(BuildContext context) {
|
||||
List<Widget> 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user