feat: Add _getMap and _postMap methods for improved API handling and refactor resetPassword to use _postMap
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-11 10:40:54 +02:00
parent c63afad672
commit 573c12cdc3
@@ -42,7 +42,7 @@ class AdminRepository {
return _parseList(data, fromJson);
}
/// POST-anrop som returnerar ett typad objekt med [fromJson].
/// POST-anrop som returnerar ett typat objekt med [fromJson].
Future<T> _post<T>(
String path, {
required Map<String, dynamic>? body,
@@ -57,7 +57,7 @@ class AdminRepository {
return parse(data);
}
/// PATCH-anrop som returnerar ett typad objekt med [fromJson].
/// PATCH-anrop som returnerar ett typat objekt med [fromJson].
Future<T> _patch<T>(
String path, {
required Map<String, dynamic> body,
@@ -98,6 +98,31 @@ class AdminRepository {
);
}
Future<Map<String, dynamic>> _getMap(
String path, {
bool requiresAuth = true,
}) async {
final token = requiresAuth ? await _token() : null;
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(path, token: token),
);
return Map<String, dynamic>.from(data as Map);
}
Future<Map<String, dynamic>> _postMap(
String path, {
Map<String, dynamic>? body,
bool requiresAuth = true,
}) async {
final token = requiresAuth ? await _token() : null;
final data = await guardedApiCall(
_ref,
() => _apiClient.postJson(path, body: body, token: token),
);
return Map<String, dynamic>.from(data as Map);
}
/// Tolerant listparsning — accepterar ren lista eller wrapper ({items, data}).
static List<T> _parseList<T>(
dynamic data,
@@ -164,17 +189,16 @@ class AdminRepository {
/// Returns `{ temporaryPassword, to, subject, body }`.
Future<Map<String, dynamic>> resetPassword(int userId) =>
_post<Map<String, dynamic>>(
UserApiPaths.resetPassword(userId),
body: null,
parse: (d) => d as Map<String, dynamic>,
);
_postMap(UserApiPaths.resetPassword(userId), body: null);
// ── Produkter ──────────────────────────────────────────────────────────────
Future<List<AdminProduct>> listProducts() =>
_getList(ProductApiPaths.list, AdminProduct.fromJson, requiresAuth: false);
@Deprecated('Use listProducts(). Kept for temporary compatibility.')
Future<List<AdminProduct>> listGlobalProducts() => listProducts();
Future<List<PendingProduct>> listPrivateProducts() =>
_getList(ProductApiPaths.privateList, PendingProduct.fromJson);
@@ -204,8 +228,6 @@ class AdminRepository {
_postVoid(ProductApiPaths.restore(productId));
// ── Product canonical name updates ────────────────────────────────────────
// Admin can update any product; users can only update their own private products
Future<void> updateCanonicalName(int productId, String canonicalName) =>
_patchVoid(
ProductApiPaths.canonicalName(productId),
@@ -219,8 +241,6 @@ class AdminRepository {
);
// ── Product merging ────────────────────────────────────────────────────────
// Admin can merge any products; users can only merge their own private products
Future<void> mergeProductsPrivate({
required int sourceProductId,
required int targetProductId,
@@ -232,23 +252,28 @@ class AdminRepository {
/// Skapar en ny aktiv produkt (kräver admin). Returnerar `{id, name, categoryId?}`.
Future<Map<String, dynamic>> createProduct(String name, {int? categoryId}) =>
_post<Map<String, dynamic>>(
_postMap(
ProductApiPaths.list,
body: {
'name': name.trim(),
if (categoryId != null) 'categoryId': categoryId,
},
parse: (d) => d as Map<String, dynamic>,
);
int _parseUpdatedCount(dynamic data) {
if (data is! Map) {
debugPrint('[AdminRepository] bulkSetCategory unexpected response type: ${data.runtimeType}');
return 0;
}
final map = Map<String, dynamic>.from(data);
return (map['updated'] as num?)?.toInt() ?? 0;
}
Future<int> bulkSetCategory(List<int> ids, {required int? categoryId}) =>
_post<int>(
ProductApiPaths.bulkUpdate,
body: {'ids': ids, 'categoryId': categoryId},
parse: (d) {
final map = Map<String, dynamic>.from(d as Map);
return (map['updated'] as num?)?.toInt() ?? 0;
},
parse: _parseUpdatedCount,
);
Future<void> mergeProducts({
@@ -263,17 +288,8 @@ class AdminRepository {
Future<Map<String, dynamic>> previewMerge({
required int sourceProductId,
required int targetProductId,
}) async {
final token = await _token();
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(
ProductApiPaths.mergePreview(sourceProductId, targetProductId),
token: token,
),
);
return Map<String, dynamic>.from(data as Map);
}
}) =>
_getMap(ProductApiPaths.mergePreview(sourceProductId, targetProductId));
Future<List<AdminAiCategorizeResult>> aiCategorizeBulk({
List<int>? productIds,
@@ -403,28 +419,28 @@ class AdminRepository {
Future<void> removeAdminInventory(int inventoryId) =>
_deleteVoid(AdminInventoryApiPaths.remove(inventoryId));
Future<void> moveAdminInventoryToPantry(int inventoryId) =>
Future<void> moveAdminInventoryToPantry(int inventoryId) =>
_postVoid(AdminInventoryApiPaths.moveToPantry(inventoryId));
// ── Admin pantry ──────────────────────────────────────────────────────────
// ── Admin pantry ──────────────────────────────────────────────────────────
Future<List<AdminPantryItem>> listAdminPantry({int? userId}) {
final params = <String, String>{};
if (userId != null) params['userId'] = '$userId';
final path = params.isEmpty
? PantryApiPaths.adminList
: '${PantryApiPaths.adminList}?${params.entries.map((e) => '${Uri.encodeQueryComponent(e.key)}=${Uri.encodeQueryComponent(e.value)}').join('&')}';
return _getList(path, AdminPantryItem.fromJson);
}
Future<List<AdminPantryItem>> listAdminPantry({int? userId}) {
final params = <String, String>{};
if (userId != null) params['userId'] = '$userId';
final path = params.isEmpty
? PantryApiPaths.adminList
: '${PantryApiPaths.adminList}?${params.entries.map((e) => '${Uri.encodeQueryComponent(e.key)}=${Uri.encodeQueryComponent(e.value)}').join('&')}';
return _getList(path, AdminPantryItem.fromJson);
}
Future<void> removeAdminPantryItem(int pantryItemId) =>
_deleteVoid(PantryApiPaths.adminRemove(pantryItemId));
Future<void> removeAdminPantryItem(int pantryItemId) =>
_deleteVoid(PantryApiPaths.adminRemove(pantryItemId));
Future<void> moveAdminPantryToInventory(
int pantryItemId,
Map<String, dynamic> body,
) =>
_postVoid(PantryApiPaths.moveToInventoryAdmin(pantryItemId), body);
Future<void> moveAdminPantryToInventory(
int pantryItemId,
Map<String, dynamic> body,
) =>
_postVoid(PantryApiPaths.moveToInventoryAdmin(pantryItemId), body);
Future<void> mergeAdminInventory({
required int sourceInventoryId,
@@ -438,15 +454,8 @@ class AdminRepository {
Future<Map<String, dynamic>> previewAdminInventoryMerge({
required int sourceInventoryId,
required int targetInventoryId,
}) async {
final token = await _token();
final data = await guardedApiCall(
_ref,
() => _apiClient.getJson(
}) =>
_getMap(
AdminInventoryApiPaths.mergePreview(sourceInventoryId, targetInventoryId),
token: token,
),
);
return Map<String, dynamic>.from(data as Map);
}
);
}