refactor: streamline alias editing and improve category path handling in admin panel
This commit is contained in:
@@ -26,7 +26,6 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
|
|
||||||
final TextEditingController _aliasController = TextEditingController();
|
final TextEditingController _aliasController = TextEditingController();
|
||||||
int? _selectedProductId;
|
int? _selectedProductId;
|
||||||
int? _editingAliasId;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -81,31 +80,21 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
setState(() => _isSaving = true);
|
setState(() => _isSaving = true);
|
||||||
try {
|
try {
|
||||||
final repo = ref.read(adminRepositoryProvider);
|
final repo = ref.read(adminRepositoryProvider);
|
||||||
final isEditing = _editingAliasId != null;
|
|
||||||
if (isEditing) {
|
|
||||||
await repo.updateReceiptAlias(
|
|
||||||
_editingAliasId!,
|
|
||||||
receiptName: rawAlias,
|
|
||||||
productId: productId,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
await repo.upsertReceiptAlias(
|
await repo.upsertReceiptAlias(
|
||||||
receiptName: rawAlias,
|
receiptName: rawAlias,
|
||||||
productId: productId,
|
productId: productId,
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
_aliasController.clear();
|
_aliasController.clear();
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedProductId = null;
|
_selectedProductId = null;
|
||||||
_editingAliasId = null;
|
|
||||||
});
|
});
|
||||||
await _load();
|
await _load();
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text(isEditing ? 'Alias uppdaterat.' : 'Alias sparad.')),
|
const SnackBar(content: Text('Alias sparad.')),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
@@ -117,27 +106,95 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _startEditAlias(ReceiptAlias alias) {
|
Future<void> _editAlias(ReceiptAlias alias) async {
|
||||||
if (!alias.isGlobal) {
|
String aliasName = alias.receiptName;
|
||||||
|
int selectedProductId = alias.productId;
|
||||||
|
final nameController = TextEditingController(text: alias.receiptName);
|
||||||
|
|
||||||
|
final result = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => StatefulBuilder(
|
||||||
|
builder: (dialogContext, setDialogState) => AlertDialog(
|
||||||
|
title: const Text('Redigera alias'),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextField(
|
||||||
|
controller: nameController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Kvittonamn (alias)',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
onChanged: (value) => aliasName = value,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
DropdownButtonFormField<int>(
|
||||||
|
initialValue: selectedProductId,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Produkt',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
items: _products
|
||||||
|
.map(
|
||||||
|
(product) => DropdownMenuItem<int>(
|
||||||
|
value: product.id,
|
||||||
|
child: Text(product.displayName),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
setDialogState(() => selectedProductId = value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext, false),
|
||||||
|
child: const Text('Avbryt'),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () => Navigator.pop(dialogContext, true),
|
||||||
|
child: const Text('Spara'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
nameController.dispose();
|
||||||
|
if (result != true || !mounted) return;
|
||||||
|
|
||||||
|
final trimmedAlias = aliasName.trim();
|
||||||
|
if (trimmedAlias.isEmpty) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Privata alias redigeras av respektive användare.')),
|
const SnackBar(content: Text('Aliasnamn kan inte vara tomt.')),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() => _isSaving = true);
|
||||||
_editingAliasId = alias.id;
|
try {
|
||||||
_aliasController.text = alias.receiptName;
|
await ref.read(adminRepositoryProvider).updateReceiptAlias(
|
||||||
_selectedProductId = alias.productId;
|
alias.id,
|
||||||
});
|
receiptName: trimmedAlias,
|
||||||
|
productId: selectedProductId,
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
await _load();
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
const SnackBar(content: Text('Alias uppdaterat.')),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
buildCopyableErrorSnackBar(context, mapErrorToUserMessage(e, context)),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
if (mounted) setState(() => _isSaving = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cancelEditAlias() {
|
|
||||||
setState(() {
|
|
||||||
_editingAliasId = null;
|
|
||||||
_aliasController.clear();
|
|
||||||
_selectedProductId = null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _removeAlias(ReceiptAlias alias) async {
|
Future<void> _removeAlias(ReceiptAlias alias) async {
|
||||||
@@ -207,7 +264,7 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
|
|
||||||
Widget buildAliasCard(ReceiptAlias alias) {
|
Widget buildAliasCard(ReceiptAlias alias) {
|
||||||
final product = productById[alias.productId];
|
final product = productById[alias.productId];
|
||||||
final categoryPath = product?.categoryPath ?? 'okänd';
|
final categoryPath = product?.categoryPath?.trim();
|
||||||
|
|
||||||
return Card(
|
return Card(
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
@@ -215,6 +272,7 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text(alias.receiptName, style: const TextStyle(fontWeight: FontWeight.w500))),
|
Expanded(child: Text(alias.receiptName, style: const TextStyle(fontWeight: FontWeight.w500))),
|
||||||
|
if (categoryPath != null && categoryPath.isNotEmpty)
|
||||||
buildCategoryPathChip(categoryPath),
|
buildCategoryPathChip(categoryPath),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -244,11 +302,9 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => _startEditAlias(alias),
|
onPressed: _isSaving ? null : () => _editAlias(alias),
|
||||||
icon: const Icon(Icons.edit_outlined),
|
icon: const Icon(Icons.edit_outlined),
|
||||||
tooltip: alias.isGlobal
|
tooltip: 'Redigera alias',
|
||||||
? 'Redigera alias'
|
|
||||||
: 'Privata alias redigeras av användaren',
|
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => _removeAlias(alias),
|
onPressed: () => _removeAlias(alias),
|
||||||
@@ -326,13 +382,6 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
if (_editingAliasId != null) ...[
|
|
||||||
OutlinedButton(
|
|
||||||
onPressed: _isSaving ? null : _cancelEditAlias,
|
|
||||||
child: const Text('Avbryt'),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
],
|
|
||||||
FilledButton.icon(
|
FilledButton.icon(
|
||||||
onPressed: _isSaving ? null : _upsertAlias,
|
onPressed: _isSaving ? null : _upsertAlias,
|
||||||
icon: _isSaving
|
icon: _isSaving
|
||||||
@@ -341,8 +390,8 @@ class _AdminAliasesPanelState extends ConsumerState<AdminAliasesPanel> {
|
|||||||
height: 14,
|
height: 14,
|
||||||
child: CircularProgressIndicator(strokeWidth: 2),
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
)
|
)
|
||||||
: Icon(_editingAliasId != null ? Icons.edit_outlined : Icons.save_outlined),
|
: const Icon(Icons.save_outlined),
|
||||||
label: Text(_editingAliasId != null ? 'Uppdatera' : 'Spara'),
|
label: const Text('Spara'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -76,9 +76,10 @@ List<ProductOption> toProductOptions(List<AdminProduct> products) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget buildCategoryPathChip(String? categoryPath, {double maxWidth = 220}) {
|
Widget buildCategoryPathChip(String? categoryPath, {double maxWidth = 220}) {
|
||||||
final value = (categoryPath == null || categoryPath.trim().isEmpty)
|
if (categoryPath == null || categoryPath.trim().isEmpty) {
|
||||||
? 'okänd'
|
return const SizedBox.shrink();
|
||||||
: categoryPath.trim();
|
}
|
||||||
|
final value = categoryPath.trim();
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: value,
|
message: value,
|
||||||
child: Chip(
|
child: Chip(
|
||||||
|
|||||||
Reference in New Issue
Block a user