feat: implement admin product management panel with bulk categorization and premium user toggle

This commit is contained in:
Nils-Johan Gynther
2026-04-25 08:36:40 +02:00
parent e2b7b884aa
commit a02950c97a
9 changed files with 383 additions and 34 deletions
@@ -0,0 +1,24 @@
class AdminCategoryNode {
final int id;
final String name;
final int? parentId;
final List<AdminCategoryNode> children;
const AdminCategoryNode({
required this.id,
required this.name,
required this.parentId,
required this.children,
});
factory AdminCategoryNode.fromJson(Map<String, dynamic> json) =>
AdminCategoryNode(
id: (json['id'] as num).toInt(),
name: (json['name'] ?? '').toString(),
parentId: (json['parentId'] as num?)?.toInt(),
children: ((json['children'] as List<dynamic>?) ?? const [])
.map((child) =>
AdminCategoryNode.fromJson(child as Map<String, dynamic>))
.toList(),
);
}
@@ -0,0 +1,44 @@
class AdminProduct {
final int id;
final String name;
final String? canonicalName;
final String? normalizedName;
final int? categoryId;
final String? categoryPath;
const AdminProduct({
required this.id,
required this.name,
this.canonicalName,
this.normalizedName,
this.categoryId,
this.categoryPath,
});
String get displayName =>
canonicalName != null && canonicalName!.trim().isNotEmpty
? canonicalName!
: name;
factory AdminProduct.fromJson(Map<String, dynamic> json) {
final categoryRef = json['categoryRef'];
final names = <String>[];
dynamic current = categoryRef;
while (current is Map<String, dynamic>) {
final name = current['name']?.toString().trim();
if (name != null && name.isNotEmpty) {
names.insert(0, name);
}
current = current['parent'];
}
return AdminProduct(
id: (json['id'] as num).toInt(),
name: (json['name'] ?? '').toString(),
canonicalName: json['canonicalName']?.toString(),
normalizedName: json['normalizedName']?.toString(),
categoryId: (json['categoryId'] as num?)?.toInt(),
categoryPath: names.isEmpty ? null : names.join(' > '),
);
}
}