Files
recipe-app/flutter/lib/core/api/api_paths.dart
T
Nils-Johan Gynther 67a7590525
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 12m45s
Test Suite / flutter-quality (push) Failing after 7m24s
feat(ai): add AI trace tracking and admin panel
- Add AiTrace model to Prisma schema with relations to User
- Implement AiTraceService with CRUD operations for AI traces
- Add new admin panel for AI traces with filtering and detail views
- Integrate trace persistence in receipt import flow
- Add API endpoints for listing and retrieving AI traces
- Update Flutter admin UI with new AI tab and navigation
- Add new domain models for AI traces and details
- Add migration for AiTrace table creation

BREAKING CHANGE: None
2026-05-21 17:33:21 +02:00

190 lines
7.6 KiB
Dart

class AuthApiPaths {
static const login = '/auth/login';
}
class ProductApiPaths {
static const list = '/products';
static const mine = '/products/mine';
static const createPrivate = '/products/private';
static const privateList = '/products/private';
static String promotePrivate(int id) => '/products/private/$id/promote';
static const pending = '/products/pending';
static const aiCategorizeBulk = '/products/ai-categorize-bulk';
static const deleted = '/products/deleted';
static const merge = '/products/merge';
static const mergePrivate = '/products/private/merge';
static String updateMineCategory(int id) => '/products/mine/$id/category';
static const backfillMineCategories = '/products/mine/backfill-categories';
static String mergePreview(int sourceProductId, int targetProductId) =>
'/products/merge-preview?sourceProductId=$sourceProductId&targetProductId=$targetProductId';
static String setStatus(int id) => '/products/$id/status';
static String update(int id) => '/products/$id';
static String canonicalName(int id) => '/products/$id/canonical-name';
static String canonicalNamePrivate(int id) =>
'/products/private/$id/canonical-name';
static String remove(int id) => '/products/$id';
static String restore(int id) => '/products/$id/restore';
static const bulkUpdate = '/products/bulk-update';
}
class AiApiPaths {
static const models = '/ai/models';
static String traces({
required String source,
int? limit,
String? cursor,
String? period,
bool onlyErrors = false,
}) {
final params = <String, String>{'source': source};
if (limit != null) params['limit'] = '$limit';
if (cursor != null && cursor.isNotEmpty) params['cursor'] = cursor;
if (period != null && period.isNotEmpty) params['period'] = period;
if (onlyErrors) params['onlyErrors'] = 'true';
final query = params.entries
.map((e) =>
'${Uri.encodeQueryComponent(e.key)}=${Uri.encodeQueryComponent(e.value)}')
.join('&');
return '/ai/traces?$query';
}
static String traceById(String traceId) =>
'/ai/traces/${Uri.encodeComponent(traceId)}';
}
class CategoryApiPaths {
static const tree = '/categories/tree';
}
class ReceiptImportApiPaths {
static const refreshCategories = '/receipt-import/refresh-categories';
static const unitMappings = '/receipt-import/unit-mappings';
}
class FlyerImportApiPaths {
static const parse = '/flyer-import/parse';
static const latestSession = '/flyer-import/sessions/latest';
static String bySession(int sessionId) => '/flyer-import/sessions/$sessionId';
static String sourceBySession(int sessionId) =>
'/flyer-import/sessions/$sessionId/source';
static String patchItem(int sessionId, int itemId) =>
'/flyer-import/sessions/$sessionId/items/$itemId';
}
class FlyerSelectionApiPaths {
static String bySession(int sessionId) =>
'/flyer-sessions/$sessionId/selections';
static String bulkBySession(int sessionId) =>
'/flyer-sessions/$sessionId/selections/bulk';
static String planToShoppingListBySession(int sessionId) =>
'/flyer-sessions/$sessionId/selections/plan-to-shopping-list';
static const open = '/flyer-selections/open';
}
class ShoppingListApiPaths {
static const items = '/shopping-list/items';
static String updateStatus(int itemId) =>
'/shopping-list/items/$itemId/status';
}
class HelpTextApiPaths {
static String byKey(String key) => '/help-texts/${Uri.encodeComponent(key)}';
}
class ReceiptAliasApiPaths {
static const list = '/receipt-aliases';
static String update(int id) => '/receipt-aliases/$id';
static String remove(int id) => '/receipt-aliases/$id';
}
class RecipeApiPaths {
static const list = '/recipes';
static String detail(int id) => '/recipes/$id';
static String update(int id) => '/recipes/$id';
static String remove(int id) => '/recipes/$id';
static String setVisibility(int id) => '/recipes/$id/visibility';
static String share(int id) => '/recipes/$id/share';
static String unshare(int id, String username) =>
'/recipes/$id/share/${Uri.encodeComponent(username)}';
static String inventoryPreview(int id) => '/recipes/$id/inventory-preview';
static String analysis(int id) => '/recipes/$id/analysis';
static String rematch(int id) => '/recipes/$id/rematch';
static const parseMarkdown = '/recipes/parse-markdown';
static const aiSuggestions = '/recipes/ai-suggestions';
}
class InventoryApiPaths {
static const list = '/inventory';
static const mergeMany = '/inventory/merge-many';
static const bulkDelete = '/inventory/bulk-delete';
static String update(int id) => '/inventory/$id';
static String remove(int id) => '/inventory/$id';
static String moveToPantry(int id) => '/inventory/$id/move-to-pantry';
static String moveToPantryAdmin(int id) =>
'/inventory/admin/$id/move-to-pantry';
static String consume(int id) => '/inventory/$id/consume';
static String consumptionHistory(int id) =>
'/inventory/$id/consumption-history';
}
class AdminInventoryApiPaths {
static const list = '/inventory/admin';
static String withFilters({int? userId, String? sort}) {
final params = <String, String>{};
if (userId != null) params['userId'] = '$userId';
if (sort != null && sort.isNotEmpty) params['sort'] = sort;
if (params.isEmpty) return list;
final query = params.entries
.map((e) =>
'${Uri.encodeQueryComponent(e.key)}=${Uri.encodeQueryComponent(e.value)}')
.join('&');
return '$list?$query';
}
static String update(int id) => '/inventory/admin/$id';
static String remove(int id) => '/inventory/admin/$id';
static String moveToPantry(int id) => '/inventory/admin/$id/move-to-pantry';
static const merge = '/inventory/admin/merge';
static String mergePreview(int sourceInventoryId, int targetInventoryId) =>
'/inventory/admin/merge-preview?sourceInventoryId=$sourceInventoryId&targetInventoryId=$targetInventoryId';
}
class PantryApiPaths {
static const list = '/pantry';
static String remove(int id) => '/pantry/$id';
static String moveToInventory(int id) => '/pantry/$id/move-to-inventory';
static String moveToInventoryAdmin(int id) =>
'/pantry/admin/$id/move-to-inventory';
static const adminList = '/pantry/admin';
static const adminCreate = '/pantry/admin';
static String adminUpdate(int id) => '/pantry/admin/$id';
static String adminRemove(int id) => '/pantry/admin/$id';
}
class UserApiPaths {
static const me = '/users/me';
static const list = '/users';
static String setRole(int id) => '/users/$id/role';
static String setPremium(int id) => '/users/$id/premium';
static String setRecipeSharing(int id) => '/users/$id/recipe-sharing';
static String updateEmail(int id) => '/users/$id/email';
static String delete(int id) => '/users/$id';
static String resetPassword(int id) => '/users/$id/reset-password';
}
class MealPlanApiPaths {
static const list = '/meal-plan';
static String listByRange(String from, String to) =>
'$list?from=${Uri.encodeQueryComponent(from)}&to=${Uri.encodeQueryComponent(to)}';
static String shoppingList(String from, String to) =>
'$list/shopping-list?from=${Uri.encodeQueryComponent(from)}&to=${Uri.encodeQueryComponent(to)}';
static String inventoryCompare(String from, String to) =>
'$list/inventory-compare?from=${Uri.encodeQueryComponent(from)}&to=${Uri.encodeQueryComponent(to)}';
static String removeByDate(String date) =>
'$list/${Uri.encodeComponent(date)}';
}