Add glitchtip for centralized error logging
This commit is contained in:
@@ -6,6 +6,7 @@ import 'package:kooltab2/views/dialogs/close_tab_dialog.dart';
|
||||
import 'package:kooltab2/views/dialogs/new_tab_dialog.dart';
|
||||
import 'package:kooltab2/views/widgets/product_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
import '../models/bar_tab.dart';
|
||||
@@ -53,7 +54,7 @@ class _BarScreenViewState extends State<BarScreenView> {
|
||||
const SizedBox(width: 6),
|
||||
IconButton(
|
||||
tooltip: 'Refresh',
|
||||
onPressed: viewModel.load,
|
||||
onPressed: () => viewModel.load(),
|
||||
icon: const Icon(Icons.refresh_rounded),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
@@ -127,7 +128,8 @@ class _BarScreenViewState extends State<BarScreenView> {
|
||||
|
||||
try {
|
||||
await viewModel.addProductToSelectedTab(product);
|
||||
} catch (e) {
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('$e')),
|
||||
@@ -304,7 +306,7 @@ class _TabPanel extends StatefulWidget {
|
||||
final ValueChanged<String> onTabSelected;
|
||||
final Future<void> Function(TabItem item, int quantity) onItemQuantityChanged;
|
||||
final VoidCallback onCloseTabPressed;
|
||||
final ValueChanged<String> onTabClosed;
|
||||
final Future<void> Function(String) onTabClosed;
|
||||
|
||||
const _TabPanel({
|
||||
required this.tabs,
|
||||
@@ -460,7 +462,7 @@ class _OpenTabsList extends StatelessWidget {
|
||||
final List<BarTab> tabs;
|
||||
final String? selectedTabId;
|
||||
final ValueChanged<String> onTabSelected;
|
||||
final ValueChanged<String> onTabClosed;
|
||||
final Future<void> Function(String) onTabClosed;
|
||||
|
||||
const _OpenTabsList({
|
||||
required this.tabs,
|
||||
@@ -498,17 +500,25 @@ class _OpenTabsList extends StatelessWidget {
|
||||
extentRatio: 0.6,
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (_) {
|
||||
onTabSelected(tab.id);
|
||||
// TODO: rename/edit tab
|
||||
},
|
||||
onPressed: (_) => onTabSelected(tab.id),
|
||||
icon: Icons.edit_outlined,
|
||||
label: 'Edit',
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
SlidableAction(
|
||||
onPressed: (_) => onTabClosed(tab.id),
|
||||
onPressed: (_) async {
|
||||
try {
|
||||
await onTabClosed(tab.id);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not close tab: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: Icons.close_rounded,
|
||||
label: 'Close',
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
@@ -723,7 +733,20 @@ class _TabItemRow extends StatelessWidget {
|
||||
children: [
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () => onQuantityChanged(item, item.quantity - 1),
|
||||
onPressed: () async {
|
||||
try {
|
||||
await onQuantityChanged(item, item.quantity - 1);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Could not update quantity: $e'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.remove_rounded, size: 18),
|
||||
),
|
||||
SizedBox(
|
||||
@@ -736,7 +759,20 @@ class _TabItemRow extends StatelessWidget {
|
||||
),
|
||||
IconButton(
|
||||
visualDensity: VisualDensity.compact,
|
||||
onPressed: () => onQuantityChanged(item, item.quantity + 1),
|
||||
onPressed: () async {
|
||||
try {
|
||||
await onQuantityChanged(item, item.quantity + 1);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Could not update quantity: $e'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.add_rounded, size: 18),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../../models/payment_method.dart';
|
||||
import '../../viewmodels/bar_screen_view_model.dart';
|
||||
@@ -63,7 +64,15 @@ Future<void> confirmCloseTab(BuildContext context) async {
|
||||
|
||||
if (confirmed != true) return;
|
||||
|
||||
await viewModel.closeSelectedTab(paymentMethod: paymentMethod);
|
||||
try {
|
||||
await viewModel.closeSelectedTab(paymentMethod: paymentMethod);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not close tab: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaymentPicker extends StatelessWidget {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
Future<void> showNewTabDialog(BuildContext context) async {
|
||||
final controller = TextEditingController();
|
||||
@@ -39,5 +40,13 @@ Future<void> showNewTabDialog(BuildContext context) async {
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
await context.read<BarScreenViewModel>().createTab(name.trim());
|
||||
try {
|
||||
await context.read<BarScreenViewModel>().createTab(name.trim());
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!context.mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not create tab: $e')),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:image_picker/image_picker.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../models/product.dart';
|
||||
import '../viewmodels/product_list_view_model.dart';
|
||||
@@ -52,30 +53,39 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
return;
|
||||
}
|
||||
|
||||
final viewModel = context.read<ProductListViewModel>();
|
||||
final product = await viewModel.getProductById(widget.productId!);
|
||||
try {
|
||||
final viewModel = context.read<ProductListViewModel>();
|
||||
final product = await viewModel.getProductById(widget.productId!);
|
||||
|
||||
if (!mounted) return;
|
||||
if (!mounted) return;
|
||||
|
||||
if (product == null) {
|
||||
if (product == null) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('Product not found.')));
|
||||
|
||||
context.go('/products');
|
||||
return;
|
||||
}
|
||||
|
||||
_existingProduct = product;
|
||||
_nameController.text = product.name;
|
||||
_selectedCategory = product.category;
|
||||
_priceController.text = (product.priceInCents / 100).toStringAsFixed(2);
|
||||
_stockController.text = product.stockQuantity.toString();
|
||||
_lowStockController.text = product.lowStockThreshold.toString();
|
||||
_imagePath = product.imagePath;
|
||||
_hasImage = product.imagePath != null && product.imagePath!.isNotEmpty;
|
||||
|
||||
setState(() => _isLoading = false);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('Product not found.')));
|
||||
|
||||
).showSnackBar(SnackBar(content: Text('Could not load product: $e')));
|
||||
context.go('/products');
|
||||
return;
|
||||
}
|
||||
|
||||
_existingProduct = product;
|
||||
_nameController.text = product.name;
|
||||
_selectedCategory = product.category;
|
||||
_priceController.text = (product.priceInCents / 100).toStringAsFixed(2);
|
||||
_stockController.text = product.stockQuantity.toString();
|
||||
_lowStockController.text = product.lowStockThreshold.toString();
|
||||
_imagePath = product.imagePath;
|
||||
_hasImage = product.imagePath != null && product.imagePath!.isNotEmpty;
|
||||
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -159,29 +169,39 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
final name = _nameController.text.trim();
|
||||
final category = _selectedCategory!;
|
||||
final priceInCents = _parsePriceToCents(_priceController.text);
|
||||
final stockQuantity = int.parse(_stockController.text);
|
||||
final lowStockThreshold = int.parse(_lowStockController.text);
|
||||
final stockQuantity = int.tryParse(_stockController.text) ?? 0;
|
||||
final lowStockThreshold = int.tryParse(_lowStockController.text) ?? 0;
|
||||
|
||||
if (widget.isEditing) {
|
||||
final updatedProduct = _existingProduct!.copyWith(
|
||||
name: name,
|
||||
category: category,
|
||||
priceInCents: priceInCents,
|
||||
stockQuantity: stockQuantity,
|
||||
lowStockThreshold: lowStockThreshold,
|
||||
imagePath: _imagePath,
|
||||
);
|
||||
try {
|
||||
if (widget.isEditing) {
|
||||
final updatedProduct = _existingProduct!.copyWith(
|
||||
name: name,
|
||||
category: category,
|
||||
priceInCents: priceInCents,
|
||||
stockQuantity: stockQuantity,
|
||||
lowStockThreshold: lowStockThreshold,
|
||||
imagePath: _imagePath,
|
||||
);
|
||||
|
||||
await viewModel.updateProduct(updatedProduct);
|
||||
} else {
|
||||
await viewModel.addProduct(
|
||||
name: name,
|
||||
category: category,
|
||||
priceInCents: priceInCents,
|
||||
stockQuantity: stockQuantity,
|
||||
lowStockThreshold: lowStockThreshold,
|
||||
imagePath: _imagePath,
|
||||
await viewModel.updateProduct(updatedProduct);
|
||||
} else {
|
||||
await viewModel.addProduct(
|
||||
name: name,
|
||||
category: category,
|
||||
priceInCents: priceInCents,
|
||||
stockQuantity: stockQuantity,
|
||||
lowStockThreshold: lowStockThreshold,
|
||||
imagePath: _imagePath,
|
||||
);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!mounted) return;
|
||||
setState(() => _isSaving = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not save product: $e')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
@@ -218,7 +238,17 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
if (confirmed != true) return;
|
||||
|
||||
final viewModel = context.read<ProductListViewModel>();
|
||||
await viewModel.deleteProduct(widget.productId!);
|
||||
|
||||
try {
|
||||
await viewModel.deleteProduct(widget.productId!);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Could not delete product: $e')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
|
||||
import '../models/settings.dart';
|
||||
import '../utils/app_update_util.dart';
|
||||
@@ -117,7 +118,13 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
}
|
||||
|
||||
pinLockViewModel.setPinRequired(true);
|
||||
await context.read<SettingsViewModel>().updatePinRequired(true);
|
||||
|
||||
try {
|
||||
await context.read<SettingsViewModel>().updatePinRequired(true);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
_showError('Could not save PIN setting.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _changePin() async {
|
||||
@@ -156,7 +163,13 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
}
|
||||
|
||||
pinLockViewModel.setPinRequired(false);
|
||||
await context.read<SettingsViewModel>().updatePinRequired(false);
|
||||
|
||||
try {
|
||||
await context.read<SettingsViewModel>().updatePinRequired(false);
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
_showError('Could not save PIN setting.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -317,7 +330,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
}
|
||||
|
||||
_showUpdateDialog(update);
|
||||
} catch (e) {
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(
|
||||
@@ -360,7 +374,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
);
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
} catch (e, stack) {
|
||||
Sentry.captureException(e, stackTrace: stack);
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(
|
||||
|
||||
Reference in New Issue
Block a user