feat: implement alias strategy for receipt import with user-scoped and global fallback, enhance validation and normalization, and update UI components
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -173,6 +173,9 @@ class AdminRepository {
|
||||
Future<List<AdminProduct>> listProducts() =>
|
||||
_getList(ProductApiPaths.mine, AdminProduct.fromJson);
|
||||
|
||||
Future<List<AdminProduct>> listGlobalProducts() =>
|
||||
_getList(ProductApiPaths.list, AdminProduct.fromJson, requiresAuth: false);
|
||||
|
||||
Future<List<AdminProduct>> listDeletedProducts() =>
|
||||
_getList(ProductApiPaths.deleted, AdminProduct.fromJson);
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ class ReceiptAlias {
|
||||
final int id;
|
||||
final String receiptName;
|
||||
final int productId;
|
||||
final int? ownerId;
|
||||
final bool isGlobal;
|
||||
final String? productName;
|
||||
final String? productCanonicalName;
|
||||
|
||||
@@ -9,10 +11,14 @@ class ReceiptAlias {
|
||||
required this.id,
|
||||
required this.receiptName,
|
||||
required this.productId,
|
||||
required this.ownerId,
|
||||
required this.isGlobal,
|
||||
this.productName,
|
||||
this.productCanonicalName,
|
||||
});
|
||||
|
||||
bool get isPrivate => !isGlobal;
|
||||
|
||||
String get displayProductName {
|
||||
final canonical = productCanonicalName?.trim();
|
||||
if (canonical != null && canonical.isNotEmpty) return canonical;
|
||||
@@ -33,6 +39,8 @@ class ReceiptAlias {
|
||||
productId: (json['productId'] as num?)?.toInt() ??
|
||||
(productMap['id'] as num?)?.toInt() ??
|
||||
0,
|
||||
ownerId: (json['ownerId'] as num?)?.toInt(),
|
||||
isGlobal: json['isGlobal'] == true,
|
||||
productName: productMap['name']?.toString(),
|
||||
productCanonicalName: productMap['canonicalName']?.toString(),
|
||||
);
|
||||
|
||||
@@ -46,7 +46,7 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
||||
try {
|
||||
final results = await Future.wait<dynamic>([
|
||||
ref.read(adminRepositoryProvider).listReceiptAliases(),
|
||||
ref.read(adminRepositoryProvider).listProducts(),
|
||||
ref.read(adminRepositoryProvider).listGlobalProducts(),
|
||||
]);
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
@@ -67,7 +67,7 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
||||
}
|
||||
|
||||
Future<void> _upsertAlias() async {
|
||||
final rawAlias = _aliasController.text.trim().toLowerCase();
|
||||
final rawAlias = _aliasController.text.trim();
|
||||
final productId = _selectedProductId;
|
||||
if (rawAlias.isEmpty || productId == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@@ -186,6 +186,11 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Chip(
|
||||
visualDensity: VisualDensity.compact,
|
||||
label: Text(alias.isGlobal ? 'Global' : 'Privat'),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: IconButton(
|
||||
@@ -198,17 +203,6 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildAliasList({EdgeInsetsGeometry padding = EdgeInsets.zero}) {
|
||||
return ListView.builder(
|
||||
padding: padding,
|
||||
itemCount: filteredAliases.length,
|
||||
itemBuilder: (context, index) {
|
||||
final alias = filteredAliases[index];
|
||||
return buildAliasCard(alias);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final content = Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
Reference in New Issue
Block a user