feat: add isPrivate field to Product model and implement private product creation and retrieval
This commit is contained in:
@@ -44,12 +44,14 @@ class _EditDialog extends StatefulWidget {
|
||||
final _ItemEdit current;
|
||||
final List<ProductOption> products;
|
||||
final List<AdminCategoryNode> categoryTree;
|
||||
final Future<ProductOption?> Function(String name, int categoryId)? onCreate;
|
||||
|
||||
const _EditDialog({
|
||||
required this.item,
|
||||
required this.current,
|
||||
required this.products,
|
||||
required this.categoryTree,
|
||||
this.onCreate,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -62,6 +64,8 @@ class _EditDialogState extends State<_EditDialog> {
|
||||
int? _productId;
|
||||
String? _productName;
|
||||
_Destination _destination = _Destination.inventory;
|
||||
// Lokal lista — utökas om nya produkter skapas under dialogen
|
||||
late List<ProductOption> _localProducts;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -69,6 +73,7 @@ class _EditDialogState extends State<_EditDialog> {
|
||||
_productId = widget.current.productId;
|
||||
_productName = widget.current.productName;
|
||||
_destination = widget.current.destination;
|
||||
_localProducts = List.of(widget.products);
|
||||
_quantityCtrl = TextEditingController(
|
||||
text: (widget.current.quantity ?? widget.item.quantity)?.toString() ?? '',
|
||||
);
|
||||
@@ -95,17 +100,30 @@ class _EditDialogState extends State<_EditDialog> {
|
||||
|
||||
// Hjälpfunktion: välj produkt via tvåstegs-picker (kategori → produkt)
|
||||
Future<void> openCategoryPicker({int? preselectedCategoryId}) async {
|
||||
// onCreate-wrapper: lägg även till den nya produkten i _localProducts
|
||||
Future<ProductOption?> Function(String, int)? onCreateWrapped;
|
||||
if (widget.onCreate != null) {
|
||||
onCreateWrapped = (name, categoryId) async {
|
||||
final newProduct = await widget.onCreate!(name, categoryId);
|
||||
if (newProduct != null && mounted) {
|
||||
setState(() => _localProducts = [..._localProducts, newProduct]);
|
||||
}
|
||||
return newProduct;
|
||||
};
|
||||
}
|
||||
|
||||
final id = await CategoryThenProductPicker.show(
|
||||
context,
|
||||
categoryTree: widget.categoryTree,
|
||||
products: widget.products,
|
||||
products: _localProducts,
|
||||
currentProductId: _productId,
|
||||
preselectedCategoryId: preselectedCategoryId,
|
||||
onCreate: onCreateWrapped,
|
||||
);
|
||||
if (id != null && mounted) {
|
||||
setState(() {
|
||||
_productId = id;
|
||||
_productName = widget.products
|
||||
_productName = _localProducts
|
||||
.cast<ProductOption?>()
|
||||
.firstWhere((p) => p?.id == id, orElse: () => null)
|
||||
?.name;
|
||||
@@ -178,7 +196,7 @@ class _EditDialogState extends State<_EditDialog> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: ProductPickerField(
|
||||
products: widget.products,
|
||||
products: _localProducts,
|
||||
value: _productId,
|
||||
label: 'Produkt',
|
||||
onChanged: (id) {
|
||||
@@ -186,7 +204,7 @@ class _EditDialogState extends State<_EditDialog> {
|
||||
_productId = id;
|
||||
_productName = id == null
|
||||
? null
|
||||
: widget.products
|
||||
: _localProducts
|
||||
.cast<ProductOption?>()
|
||||
.firstWhere((p) => p?.id == id, orElse: () => null)
|
||||
?.name;
|
||||
@@ -303,17 +321,28 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
||||
final adminRepo = ref.read(adminRepositoryProvider);
|
||||
final results = await Future.wait([
|
||||
api.getJson(ProductApiPaths.list, token: token),
|
||||
api.getJson(ProductApiPaths.mine, token: token),
|
||||
adminRepo.listCategoryTree(),
|
||||
]);
|
||||
final data = results[0];
|
||||
final list = data is List ? data : ((data as Map<String, dynamic>?)?['items'] as List? ?? []);
|
||||
final globalData = results[0];
|
||||
final mineData = results[1];
|
||||
final globalList = globalData is List
|
||||
? globalData
|
||||
: ((globalData as Map<String, dynamic>?)?['items'] as List? ?? []);
|
||||
final mineList = mineData is List
|
||||
? mineData
|
||||
: ((mineData as Map<String, dynamic>?)?['items'] as List? ?? []);
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_products = list
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((e) => (id: e['id'] as int, name: e['name'] as String, categoryId: (e['categoryId'] as num?)?.toInt()))
|
||||
.toList();
|
||||
_categoryTree = results[1] as List<AdminCategoryNode>;
|
||||
_products = [
|
||||
...globalList
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((e) => (id: e['id'] as int, name: (e['canonicalName'] ?? e['name']) as String, categoryId: (e['categoryId'] as num?)?.toInt())),
|
||||
...mineList
|
||||
.cast<Map<String, dynamic>>()
|
||||
.map((e) => (id: e['id'] as int, name: (e['canonicalName'] ?? e['name']) as String, categoryId: (e['categoryId'] as num?)?.toInt())),
|
||||
];
|
||||
_categoryTree = results[2] as List<AdminCategoryNode>;
|
||||
_loadingProducts = false;
|
||||
});
|
||||
}
|
||||
@@ -413,7 +442,35 @@ class _ReceiptImportTabState extends ConsumerState<ReceiptImportTab> {
|
||||
|
||||
final result = await showDialog<_ItemEdit>(
|
||||
context: context,
|
||||
builder: (_) => _EditDialog(item: item, current: current, products: _products, categoryTree: _categoryTree),
|
||||
builder: (_) => _EditDialog(
|
||||
item: item,
|
||||
current: current,
|
||||
products: _products,
|
||||
categoryTree: _categoryTree,
|
||||
onCreate: (name, categoryId) async {
|
||||
try {
|
||||
final token = await ref.read(authStateProvider.future);
|
||||
final api = ref.read(apiClientProvider);
|
||||
final data = await api.postJson(
|
||||
ProductApiPaths.createPrivate,
|
||||
body: {
|
||||
'name': name.trim(),
|
||||
'categoryId': categoryId,
|
||||
},
|
||||
token: token,
|
||||
) as Map<String, dynamic>;
|
||||
final newProduct = (
|
||||
id: data['id'] as int,
|
||||
name: (data['canonicalName'] ?? data['name']) as String,
|
||||
categoryId: categoryId,
|
||||
);
|
||||
if (mounted) setState(() => _products = [..._products, newProduct]);
|
||||
return newProduct;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
if (result != null && mounted) {
|
||||
setState(() {
|
||||
|
||||
Reference in New Issue
Block a user