feat: enhance profile screen with tab navigation and admin panels

- Added tab navigation for profile, database, users, suggestions, and AI sections.
- Implemented database management with inventory, pantry, and products tabs.
- Created Admin AI panel to display AI model information.
- Introduced Admin Pending Products panel for managing product approvals.
- Developed Admin Users panel for user management, including role changes and password resets.
- Added data models for AI models and pending products.
This commit is contained in:
Nils-Johan Gynther
2026-04-25 08:22:14 +02:00
parent 53afcc98a9
commit 8ea2b97c27
10 changed files with 1289 additions and 488 deletions
@@ -0,0 +1,29 @@
class AiModelInfo {
final String id;
final String name;
final String description;
final String model;
final String path;
final String trigger;
final String access;
const AiModelInfo({
required this.id,
required this.name,
required this.description,
required this.model,
required this.path,
required this.trigger,
required this.access,
});
factory AiModelInfo.fromJson(Map<String, dynamic> json) => AiModelInfo(
id: (json['id'] ?? '').toString(),
name: (json['name'] ?? '').toString(),
description: (json['description'] ?? '').toString(),
model: (json['model'] ?? '').toString(),
path: (json['path'] ?? '').toString(),
trigger: (json['trigger'] ?? '').toString(),
access: (json['access'] ?? '').toString(),
);
}
@@ -0,0 +1,54 @@
class PendingProduct {
final int id;
final String name;
final String? canonicalName;
final DateTime? createdAt;
final String? categoryPath;
final String? ownerUsername;
const PendingProduct({
required this.id,
required this.name,
this.canonicalName,
this.createdAt,
this.categoryPath,
this.ownerUsername,
});
String get displayName =>
canonicalName != null && canonicalName!.trim().isNotEmpty
? canonicalName!
: name;
factory PendingProduct.fromJson(Map<String, dynamic> json) {
final categoryRef = json['categoryRef'];
final owner = json['owner'];
final parts = <String>[];
if (categoryRef is Map<String, dynamic>) {
final parent = categoryRef['parent'];
if (parent is Map<String, dynamic>) {
final parentName = parent['name']?.toString();
if (parentName != null && parentName.trim().isNotEmpty) {
parts.add(parentName.trim());
}
}
final name = categoryRef['name']?.toString();
if (name != null && name.trim().isNotEmpty) {
parts.add(name.trim());
}
}
return PendingProduct(
id: (json['id'] as num).toInt(),
name: (json['name'] ?? '').toString(),
canonicalName: json['canonicalName']?.toString(),
createdAt: json['createdAt'] == null
? null
: DateTime.tryParse(json['createdAt'].toString()),
categoryPath: parts.isEmpty ? null : parts.join(' > '),
ownerUsername: owner is Map<String, dynamic>
? owner['username']?.toString()
: null,
);
}
}