feat: enhance error dialogs with delete functionality and improve documentation
This commit is contained in:
+38
-11
@@ -24,18 +24,29 @@ Ge en detaljerad rapport enligt följande struktur:
|
||||
|
||||
---
|
||||
### **1. Allmän kodkvalitet**
|
||||
- **Optimeringar**:
|
||||
- Finns det ineffektiva algoritmer (t.ex. O(n²) istället för O(n))?
|
||||
- Kan loopar, databaserfrågor (Prisma) eller API-anrop optimeras (t.ex. med caching, batch-behandling)?
|
||||
- Finns det onödig kod (död kod, duplicerad logik)?
|
||||
- Kan minne eller CPU-användning reduceras (t.ex. undvika djupa kopior, använda streams)?
|
||||
|
||||
- **Läsbarhet/underhållbarhet**:
|
||||
- **Läsbarhet/underhållbarhet** (kan blockera om allvarligt):
|
||||
- Finns det bristande namngivning (variabler, funktioner, klasser)?
|
||||
- Saknas kommentarer för komplex logik?
|
||||
- Kan modulariseringen förbättras (t.ex. splitta stora funktioner/klasser)?
|
||||
- Följs TypeScript-bäst-praxis (t.ex. starka typer, interfaces, SOLID-principer)?
|
||||
|
||||
---
|
||||
### **1b. Performance-optimeringar** (INFORMATIONAL)
|
||||
|
||||
Dessa rapporteras men blockerar inte commit. Kan adresseras i senare iteration:
|
||||
|
||||
- **Algoritm-effektivitet**:
|
||||
- Finns det O(n²) eller värre algoritmer som kan vara O(n)?
|
||||
- Finns onödig kod (död kod, duplicerad logik)?
|
||||
|
||||
- **Resurser**:
|
||||
- Kan minne eller CPU-användning reduceras (t.ex. undvika djupa kopior, använda streams)?
|
||||
- Kan loopar eller databaserfrågor (Prisma) optimeras (t.ex. med caching, batch-behandling)?
|
||||
- Finns N+1-frågor eller ineffektiva `include/select`-mönster?
|
||||
|
||||
**Severity**: `Low` eller `Medium` beroende på påverkan. Blockerar aldrig commit.
|
||||
|
||||
---
|
||||
### **2. Säkerhetsanalys**
|
||||
- **Sårbarheter**:
|
||||
@@ -82,13 +93,27 @@ Ge en detaljerad rapport enligt följande struktur:
|
||||
- **Uppskattad tid** för att implementera förslagen.
|
||||
- **Rekommenderade verktyg** för automatiserade kontroller (t.ex. `ESLint`, `Prisma Lint`, `OWASP Dependency-Check`).
|
||||
|
||||
---
|
||||
### **Klassificering av fynd (Severity)**
|
||||
|
||||
**BLOCKING** (hindrar commit):
|
||||
- `Critical`: Säkerhetshål, scope-brister (IDOR), SQL-injection, XSS, eller data-loss risk.
|
||||
- `High`: Allvarlig korrektness-fel, felaktig autentisering/auktorisation, eller felaktig felhantering som påverkar produktion.
|
||||
|
||||
**INFORMATIONAL** (rapporteras, men blockerar inte):
|
||||
- `Medium`: Code-quality, läsbarhet, testluckor, eller mindre performance-optimeringar.
|
||||
- `Low`: Stilfrågor, dokumentation, eller nice-to-have refactor.
|
||||
|
||||
**Regel**: Gate-beslut = `PASS` om inga `Critical` eller `High` finns. `BLOCK` annars.
|
||||
|
||||
---
|
||||
### **Regler för analysen**
|
||||
- Var **specifik**: Ge **kod-exempel** för varje förslag.
|
||||
- Var **praktisk**: Fokusera på **realistiska förbättringar** som kan implementeras nu.
|
||||
- Var **kritisk**: Peka ut **allvarliga risker** (t.ex. säkerhetshål) först.
|
||||
- Använd **severity** per fynd: `Critical`, `High`, `Medium`, `Low`.
|
||||
- För varje fynd: ange fil, kort riskbeskrivning, varför det är ett problem, och konkret åtgärd.
|
||||
- Använd **severity** per fynd enligt klassificering ovan: `Critical`, `High`, `Medium`, `Low`.
|
||||
- För varje fynd: ange fil, kort riskbeskrivning, varför det är ett problem, severity, och konkret åtgärd.
|
||||
- **Separa fynd efter severity**: Listet först alla `Critical`/`High` (blocking), sedan `Medium`/`Low` (informational).
|
||||
- Om inga allvarliga risker hittas: skriv det explicit och lyft kvarvarande risker/testluckor.
|
||||
- Ignorera filer som inte är relevanta (t.ex. node_modules, .git, binärfiler).
|
||||
- Prioritera körbarhet: föreslagna åtgärder ska kunna göras i denna kodbas utan större arkitekturprojekt.
|
||||
@@ -100,9 +125,11 @@ Ge en detaljerad rapport enligt följande struktur:
|
||||
### **Outputformat (obligatoriskt)**
|
||||
1. `Scope`
|
||||
2. `Gate-beslut` (`PASS` eller `BLOCK`)
|
||||
3. `1. Allmän kodkvalitet`
|
||||
4. `2. Säkerhetsanalys`
|
||||
5. `3. Sammanfattning`
|
||||
3. `1. Allmän kodkvalitet` (blocking issues)
|
||||
4. `1b. Performance-optimeringar` (informational)
|
||||
5. `2. Säkerhetsanalys` (blocking issues)
|
||||
6. `2b. Backend-specifik kontroll` (blocking + informational)
|
||||
7. `3. Sammanfattning` (topprioriteringar, tidskattning)
|
||||
|
||||
Om inga relevanta filer hittas:
|
||||
- Skriv `Inget att analysera` och varför (t.ex. tom staged + tom working tree).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
/// Visar en global dialogruta med ett felmeddelande och en kopieringsknapp.
|
||||
/// Visar en global dialogruta med ett felmeddelande och knappar för kopiera och ta bort.
|
||||
void showGlobalErrorDialog(BuildContext context, String errorMessage) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -17,7 +17,7 @@ void showGlobalErrorDialog(BuildContext context, String errorMessage) {
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Stäng'),
|
||||
child: const Text('Ta bort'),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
@@ -26,6 +26,7 @@ void showGlobalErrorDialog(BuildContext context, String errorMessage) {
|
||||
child: const Text('Kopiera'),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: errorMessage));
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(content: Text('Felmeddelande kopierat!')),
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter/services.dart';
|
||||
|
||||
import '../../../core/l10n/l10n.dart';
|
||||
|
||||
/// Visar en dialogruta med ett felmeddelande och en kopieringsknapp.
|
||||
/// Visar en dialogruta med ett felmeddelande och knappar för kopiera och ta bort.
|
||||
void showErrorDialog(BuildContext context, String errorMessage) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@@ -28,6 +28,7 @@ void showErrorDialog(BuildContext context, String errorMessage) {
|
||||
child: Text(context.l10n.errorDialogCopy),
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: errorMessage));
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(context.l10n.errorDialogCopied)),
|
||||
);
|
||||
|
||||
@@ -460,6 +460,44 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
||||
);
|
||||
}
|
||||
|
||||
void _deleteItem(int index) {
|
||||
final items = _items;
|
||||
if (items == null || index < 0 || index >= items.length) return;
|
||||
|
||||
// Ta bort raden från items
|
||||
final remainingItems = <ParsedReceiptItem>[...items];
|
||||
remainingItems.removeAt(index);
|
||||
|
||||
// Re-index edits och selected i en enda pass
|
||||
final remainingEdits = <int, ItemEdit>{};
|
||||
final remainingSelected = <int, bool>{};
|
||||
var newIndex = 0;
|
||||
|
||||
for (var oldIndex = 0; oldIndex < items.length; oldIndex++) {
|
||||
if (oldIndex != index) {
|
||||
if (_edits.containsKey(oldIndex)) {
|
||||
remainingEdits[newIndex] = _edits[oldIndex]!;
|
||||
}
|
||||
if (_selected[oldIndex] == true) {
|
||||
remainingSelected[newIndex] = true;
|
||||
}
|
||||
newIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
final notifier = ref.read(receiptImportSessionProvider.notifier);
|
||||
if (remainingItems.isEmpty) {
|
||||
notifier.clear();
|
||||
} else {
|
||||
notifier.setImportedResult(
|
||||
items: remainingItems,
|
||||
edits: remainingEdits,
|
||||
selected: remainingSelected,
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _addSelected() async {
|
||||
final items = _items;
|
||||
if (items == null) return;
|
||||
@@ -572,9 +610,36 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
||||
SnackBar(content: Text(parts.join(', ') + '.')),
|
||||
);
|
||||
|
||||
// Avmarkera sparade rader och uppdatera inventariet
|
||||
// Ta bort de tillagda raderna från listan
|
||||
final addedIndexSet = toAdd.toSet();
|
||||
final remainingItems = <ParsedReceiptItem>[];
|
||||
final remainingEdits = <int, ItemEdit>{};
|
||||
final remainingSelected = <int, bool>{};
|
||||
|
||||
var newIndex = 0;
|
||||
for (var oldIndex = 0; oldIndex < items.length; oldIndex++) {
|
||||
if (!addedIndexSet.contains(oldIndex)) {
|
||||
remainingItems.add(items[oldIndex]);
|
||||
if (_edits.containsKey(oldIndex)) {
|
||||
remainingEdits[newIndex] = _edits[oldIndex]!;
|
||||
}
|
||||
if (_selected[oldIndex] == true) {
|
||||
remainingSelected[newIndex] = true;
|
||||
}
|
||||
newIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
final notifier = ref.read(receiptImportSessionProvider.notifier);
|
||||
notifier.setSelectedForIndexes(toAdd, false);
|
||||
if (remainingItems.isEmpty) {
|
||||
notifier.clear();
|
||||
} else {
|
||||
notifier.setImportedResult(
|
||||
items: remainingItems,
|
||||
edits: remainingEdits,
|
||||
selected: remainingSelected,
|
||||
);
|
||||
}
|
||||
setState(() {});
|
||||
await _loadInventory();
|
||||
} catch (e) {
|
||||
@@ -779,6 +844,7 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
||||
i,
|
||||
initialEntryMode: ImportProductEntryMode.create,
|
||||
),
|
||||
onDeleteRequested: () => _deleteItem(i),
|
||||
matchedViaBadgeBuilder: _buildMatchedViaBadge,
|
||||
);
|
||||
},
|
||||
@@ -811,6 +877,7 @@ class _ReceiptImportResultRow extends ConsumerWidget {
|
||||
final VoidCallback onEditRequested;
|
||||
final VoidCallback onSelectExistingRequested;
|
||||
final VoidCallback onCreateRequested;
|
||||
final VoidCallback onDeleteRequested;
|
||||
final Widget Function(ParsedReceiptItem item, ThemeData theme)
|
||||
matchedViaBadgeBuilder;
|
||||
|
||||
@@ -824,6 +891,7 @@ class _ReceiptImportResultRow extends ConsumerWidget {
|
||||
required this.onEditRequested,
|
||||
required this.onSelectExistingRequested,
|
||||
required this.onCreateRequested,
|
||||
required this.onDeleteRequested,
|
||||
required this.matchedViaBadgeBuilder,
|
||||
});
|
||||
|
||||
@@ -1019,7 +1087,12 @@ class _ReceiptImportResultRow extends ConsumerWidget {
|
||||
],
|
||||
],
|
||||
),
|
||||
trailing: Icon(
|
||||
trailing: SizedBox(
|
||||
width: 80,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
hasProduct
|
||||
? Icons.check_circle
|
||||
: (isSuggested ? Icons.help_outline : Icons.error_outline),
|
||||
@@ -1028,6 +1101,16 @@ class _ReceiptImportResultRow extends ConsumerWidget {
|
||||
: (isSuggested ? Colors.orange : theme.colorScheme.tertiary),
|
||||
size: 20,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete_outline, size: 18, color: theme.colorScheme.error),
|
||||
onPressed: onDeleteRequested,
|
||||
tooltip: 'Ta bort rad',
|
||||
constraints: const BoxConstraints(minWidth: 40, minHeight: 40),
|
||||
padding: const EdgeInsets.all(4),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
onTap: onEditRequested,
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user