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
This commit is contained in:
@@ -49,19 +49,19 @@ class AppShell extends ConsumerWidget {
|
||||
icon: Icons.storefront_outlined,
|
||||
label: 'Baslager',
|
||||
),
|
||||
_AppDestination(
|
||||
path: '/import',
|
||||
title: 'Importera',
|
||||
icon: Icons.upload_file_outlined,
|
||||
label: 'Importera',
|
||||
),
|
||||
_AppDestination(
|
||||
path: '/inkopslista',
|
||||
title: 'Inköpslista',
|
||||
icon: Icons.shopping_cart_outlined,
|
||||
label: 'Inköpslista',
|
||||
),
|
||||
];
|
||||
_AppDestination(
|
||||
path: '/import',
|
||||
title: 'Importera',
|
||||
icon: Icons.upload_file_outlined,
|
||||
label: 'Importera',
|
||||
),
|
||||
_AppDestination(
|
||||
path: '/inkopslista',
|
||||
title: 'Inköpslista',
|
||||
icon: Icons.shopping_cart_outlined,
|
||||
label: 'Inköpslista',
|
||||
),
|
||||
];
|
||||
|
||||
List<_AppDestination> _destinations() => _baseDestinations;
|
||||
|
||||
@@ -101,8 +101,8 @@ class AppShell extends ConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
final isRecipesRoute = location.startsWith('/recipes') &&
|
||||
!location.startsWith('/recipes/');
|
||||
final isRecipesRoute =
|
||||
location.startsWith('/recipes') && !location.startsWith('/recipes/');
|
||||
final isImportRoute = location == '/import';
|
||||
final isAdminRoute = location.startsWith('/admin');
|
||||
final adminTab = AdminViewTabX.fromQuery(
|
||||
@@ -133,6 +133,12 @@ class AppShell extends ConsumerWidget {
|
||||
selected: adminTab == AdminViewTab.database,
|
||||
onSelected: (_) => navigateToAdminTab(AdminViewTab.database),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
ChoiceChip(
|
||||
label: const Text('AI'),
|
||||
selected: adminTab == AdminViewTab.ai,
|
||||
onSelected: (_) => navigateToAdminTab(AdminViewTab.ai),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -140,7 +146,8 @@ class AppShell extends ConsumerWidget {
|
||||
|
||||
Widget shell = Scaffold(
|
||||
appBar: AppBar(
|
||||
title: isAdminRoute ? buildAdminTitle() : Text(selectedDestination.title),
|
||||
title:
|
||||
isAdminRoute ? buildAdminTitle() : Text(selectedDestination.title),
|
||||
bottom: isImportRoute
|
||||
? const TabBar(
|
||||
tabs: [
|
||||
@@ -148,17 +155,17 @@ class AppShell extends ConsumerWidget {
|
||||
icon: Icon(Icons.restaurant_menu_outlined),
|
||||
text: 'Recept',
|
||||
),
|
||||
Tab(
|
||||
icon: Icon(Icons.receipt_long_outlined),
|
||||
text: 'Kvitto',
|
||||
),
|
||||
Tab(
|
||||
icon: Icon(Icons.local_offer_outlined),
|
||||
text: 'Flyer',
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
Tab(
|
||||
icon: Icon(Icons.receipt_long_outlined),
|
||||
text: 'Kvitto',
|
||||
),
|
||||
Tab(
|
||||
icon: Icon(Icons.local_offer_outlined),
|
||||
text: 'Flyer',
|
||||
),
|
||||
],
|
||||
)
|
||||
: null,
|
||||
actions: [
|
||||
if (isRecipesRoute)
|
||||
Consumer(
|
||||
@@ -184,8 +191,9 @@ class AppShell extends ConsumerWidget {
|
||||
PopupMenuButton<int>(
|
||||
icon: const Icon(Icons.grid_view),
|
||||
tooltip: 'Välj antal kolumner',
|
||||
onSelected: (columns) =>
|
||||
ref.read(recipesViewProvider.notifier).setColumns(columns),
|
||||
onSelected: (columns) => ref
|
||||
.read(recipesViewProvider.notifier)
|
||||
.setColumns(columns),
|
||||
itemBuilder: (context) => const [
|
||||
PopupMenuItem(value: 2, child: Text('2 kolumner')),
|
||||
PopupMenuItem(value: 4, child: Text('4 kolumner')),
|
||||
@@ -288,9 +296,9 @@ class AppShell extends ConsumerWidget {
|
||||
),
|
||||
);
|
||||
|
||||
if (isImportRoute) {
|
||||
shell = DefaultTabController(length: 3, child: shell);
|
||||
}
|
||||
if (isImportRoute) {
|
||||
shell = DefaultTabController(length: 3, child: shell);
|
||||
}
|
||||
|
||||
return shell;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user