Add glitchtip for centralized error logging
This commit is contained in:
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:kooltab2/views/settings_view.dart';
|
import 'package:kooltab2/views/settings_view.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../viewmodels/pin_lock_view_model.dart';
|
import '../viewmodels/pin_lock_view_model.dart';
|
||||||
import '../views/bar_screen_view.dart';
|
import '../views/bar_screen_view.dart';
|
||||||
@@ -16,7 +17,7 @@ final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
|
|||||||
GoRouter createAppRouter(PinLockViewModel pinLockViewModel) {
|
GoRouter createAppRouter(PinLockViewModel pinLockViewModel) {
|
||||||
return GoRouter(
|
return GoRouter(
|
||||||
initialLocation: '/bar',
|
initialLocation: '/bar',
|
||||||
observers: [routeObserver],
|
observers: [routeObserver, SentryNavigatorObserver()],
|
||||||
refreshListenable: pinLockViewModel,
|
refreshListenable: pinLockViewModel,
|
||||||
redirect: (context, state) {
|
redirect: (context, state) {
|
||||||
if (!pinLockViewModel.hasLoaded) return null;
|
if (!pinLockViewModel.hasLoaded) return null;
|
||||||
|
|||||||
+24
-5
@@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
@@ -10,6 +11,7 @@ import 'package:kooltab2/viewmodels/inventory_view_model.dart';
|
|||||||
import 'package:kooltab2/viewmodels/pin_lock_view_model.dart';
|
import 'package:kooltab2/viewmodels/pin_lock_view_model.dart';
|
||||||
import 'package:kooltab2/viewmodels/settings_view_model.dart';
|
import 'package:kooltab2/viewmodels/settings_view_model.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import 'app/app.dart';
|
import 'app/app.dart';
|
||||||
import 'app/app_bootstrap.dart';
|
import 'app/app_bootstrap.dart';
|
||||||
@@ -41,7 +43,21 @@ Future<void> main() async {
|
|||||||
|
|
||||||
final appRouter = createAppRouter(pinLockViewModel);
|
final appRouter = createAppRouter(pinLockViewModel);
|
||||||
|
|
||||||
runApp(
|
await SentryFlutter.init(
|
||||||
|
(options) {
|
||||||
|
options.dsn =
|
||||||
|
'https://2c31c1baf3ee495288ad08a56d7baf0e@glitchtip.brammie15.dev/1';
|
||||||
|
options.tracesSampleRate = kReleaseMode ? 0.1 : 1.0;
|
||||||
|
options.enableAutoSessionTracking = false;
|
||||||
|
options.attachScreenshot = true;
|
||||||
|
options.attachViewHierarchy = true;
|
||||||
|
options.enableLogs = true;
|
||||||
|
options.attachThreads = true;
|
||||||
|
|
||||||
|
options.replay.onErrorSampleRate = 1.0;
|
||||||
|
options.replay.sessionSampleRate = 0.05;
|
||||||
|
},
|
||||||
|
appRunner: () => runApp(
|
||||||
MultiProvider(
|
MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<AppDatabase>.value(value: database),
|
Provider<AppDatabase>.value(value: database),
|
||||||
@@ -58,9 +74,9 @@ Future<void> main() async {
|
|||||||
DriftSettingsService(database: context.read<AppDatabase>()),
|
DriftSettingsService(database: context.read<AppDatabase>()),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<InventoryViewModel>(
|
ChangeNotifierProvider<InventoryViewModel>(
|
||||||
create: (context) =>
|
create: (context) => InventoryViewModel(
|
||||||
InventoryViewModel(productService: context.read<ProductService>())
|
productService: context.read<ProductService>(),
|
||||||
..load(),
|
)..load(),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<ProductListViewModel>(
|
ChangeNotifierProvider<ProductListViewModel>(
|
||||||
create: (context) => ProductListViewModel(
|
create: (context) => ProductListViewModel(
|
||||||
@@ -78,7 +94,9 @@ Future<void> main() async {
|
|||||||
create: (context) =>
|
create: (context) =>
|
||||||
HistoryViewModel(barTabService: context.read<BarTabService>()),
|
HistoryViewModel(barTabService: context.read<BarTabService>()),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider<PinLockViewModel>.value(value: pinLockViewModel),
|
ChangeNotifierProvider<PinLockViewModel>.value(
|
||||||
|
value: pinLockViewModel,
|
||||||
|
),
|
||||||
ChangeNotifierProvider<SettingsViewModel>(
|
ChangeNotifierProvider<SettingsViewModel>(
|
||||||
create: (context) => SettingsViewModel(
|
create: (context) => SettingsViewModel(
|
||||||
settingsService: context.read<SettingsService>(),
|
settingsService: context.read<SettingsService>(),
|
||||||
@@ -96,5 +114,6 @@ Future<void> main() async {
|
|||||||
],
|
],
|
||||||
child: AppBootstrap(child: KoolTabApp(router: appRouter)),
|
child: AppBootstrap(child: KoolTabApp(router: appRouter)),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kooltab2/utils/app_update_util.dart';
|
import 'package:kooltab2/utils/app_update_util.dart';
|
||||||
import 'package:ota_update/ota_update.dart';
|
import 'package:ota_update/ota_update.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import 'app_config.dart';
|
import 'app_config.dart';
|
||||||
|
|
||||||
@@ -86,34 +87,31 @@ Future<void> _downloadAndInstall(UpdateInfo update) async {
|
|||||||
)
|
)
|
||||||
.listen((OtaEvent event) {
|
.listen((OtaEvent event) {
|
||||||
debugPrint("OTA status: ${event.status}");
|
debugPrint("OTA status: ${event.status}");
|
||||||
|
|
||||||
debugPrint("OTA value: ${event.value}");
|
debugPrint("OTA value: ${event.value}");
|
||||||
|
|
||||||
switch (event.status) {
|
switch (event.status) {
|
||||||
case OtaStatus.DOWNLOADING:
|
case OtaStatus.DOWNLOADING:
|
||||||
final progress = double.tryParse(event.value ?? "0") ?? 0;
|
final progress = double.tryParse(event.value ?? "0") ?? 0;
|
||||||
|
|
||||||
debugPrint("Downloading ${progress.toStringAsFixed(0)}%");
|
debugPrint("Downloading ${progress.toStringAsFixed(0)}%");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OtaStatus.INSTALLING:
|
case OtaStatus.INSTALLING:
|
||||||
debugPrint("Installing update");
|
debugPrint("Installing update");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OtaStatus.INSTALLATION_ERROR:
|
case OtaStatus.INSTALLATION_ERROR:
|
||||||
debugPrint("Installation error: ${event.value}");
|
debugPrint("Installation error: ${event.value}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OtaStatus.DOWNLOAD_ERROR:
|
case OtaStatus.DOWNLOAD_ERROR:
|
||||||
debugPrint("Download error: ${event.value}");
|
debugPrint("Download error: ${event.value}");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}, onError: (e, stack) {
|
||||||
|
debugPrint("OTA stream error: $e");
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
debugPrint("OTA update failed: $e");
|
debugPrint("OTA update failed: $e");
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../models/payment_method.dart';
|
import '../models/payment_method.dart';
|
||||||
import '../models/bar_tab.dart';
|
import '../models/bar_tab.dart';
|
||||||
@@ -76,10 +77,16 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> createTab(String customerName) async {
|
Future<void> createTab(String customerName) async {
|
||||||
|
try {
|
||||||
final tab = await barTabService.createTab(customerName: customerName);
|
final tab = await barTabService.createTab(customerName: customerName);
|
||||||
|
|
||||||
_selectedTabId = tab.id;
|
_selectedTabId = tab.id;
|
||||||
await _reloadTabs();
|
await _reloadTabs();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: createTab error: $e');
|
||||||
|
_errorMessage = 'Could not create tab.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> addProductToSelectedTab(Product product) async {
|
Future<void> addProductToSelectedTab(Product product) async {
|
||||||
@@ -88,9 +95,12 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
if (tab == null) return;
|
if (tab == null) return;
|
||||||
|
|
||||||
if (product.stockQuantity <= 0) {
|
if (product.stockQuantity <= 0) {
|
||||||
throw Exception('Product is out of stock.');
|
_errorMessage = 'Product is out of stock.';
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
await barTabService.addProductToTab(
|
await barTabService.addProductToTab(
|
||||||
tabId: tab.id,
|
tabId: tab.id,
|
||||||
product: product,
|
product: product,
|
||||||
@@ -98,11 +108,19 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await _reloadTabs();
|
await _reloadTabs();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: addProductToSelectedTab error: $e');
|
||||||
|
_errorMessage = 'Could not add product to tab.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changeItemQuantity(TabItem item, int quantity) async {
|
Future<void> changeItemQuantity(TabItem item, int quantity) async {
|
||||||
final difference = quantity - item.quantity;
|
final difference = quantity - item.quantity;
|
||||||
|
|
||||||
|
try {
|
||||||
await barTabService.updateTabItemQuantity(
|
await barTabService.updateTabItemQuantity(
|
||||||
tabItemId: item.id,
|
tabItemId: item.id,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
@@ -116,6 +134,13 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await _reloadTabs();
|
await _reloadTabs();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: changeItemQuantity error: $e');
|
||||||
|
_errorMessage = 'Could not update item quantity.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> closeSelectedTab({PaymentMethod paymentMethod = PaymentMethod.cash}) async {
|
Future<void> closeSelectedTab({PaymentMethod paymentMethod = PaymentMethod.cash}) async {
|
||||||
@@ -123,18 +148,33 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
|
|
||||||
if (tab == null) return;
|
if (tab == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
await barTabService.closeTab(tab.id, paymentMethod: paymentMethod);
|
await barTabService.closeTab(tab.id, paymentMethod: paymentMethod);
|
||||||
|
|
||||||
await _reloadTabs();
|
await _reloadTabs();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: closeSelectedTab error: $e');
|
||||||
|
_errorMessage = 'Could not close tab.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> closeTab(String tabId, {PaymentMethod paymentMethod = PaymentMethod.cash}) async {
|
Future<void> closeTab(String tabId, {PaymentMethod paymentMethod = PaymentMethod.cash}) async {
|
||||||
|
try {
|
||||||
await barTabService.closeTab(tabId, paymentMethod: paymentMethod);
|
await barTabService.closeTab(tabId, paymentMethod: paymentMethod);
|
||||||
|
|
||||||
await _reloadTabs();
|
await _reloadTabs();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: closeTab error: $e');
|
||||||
|
_errorMessage = 'Could not close tab.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _reloadTabs() async {
|
Future<void> _reloadTabs() async {
|
||||||
|
try {
|
||||||
_tabs = await barTabService.getOpenTabs();
|
_tabs = await barTabService.getOpenTabs();
|
||||||
|
|
||||||
if (_selectedTabId == null ||
|
if (_selectedTabId == null ||
|
||||||
@@ -143,5 +183,11 @@ class BarScreenViewModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('BarScreenViewModel: reloadTabs error: $e');
|
||||||
|
_errorMessage = 'Could not reload tabs.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:kooltab2/models/payment_method.dart';
|
|||||||
import 'package:kooltab2/services/pin_lock_service.dart';
|
import 'package:kooltab2/services/pin_lock_service.dart';
|
||||||
import 'package:kooltab2/services/product_service.dart';
|
import 'package:kooltab2/services/product_service.dart';
|
||||||
import 'package:kooltab2/services/settings_service.dart';
|
import 'package:kooltab2/services/settings_service.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class DevMenuViewModel extends ChangeNotifier {
|
class DevMenuViewModel extends ChangeNotifier {
|
||||||
@@ -255,6 +256,12 @@ class DevMenuViewModel extends ChangeNotifier {
|
|||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
throw Exception('Test GlitchTip error!');
|
||||||
|
} catch (exception, stackTrace) {
|
||||||
|
Sentry.captureException(exception, stackTrace: stackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> generateMockOrders({int count = 100}) async {
|
Future<void> generateMockOrders({int count = 100}) async {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../models/product.dart';
|
import '../models/product.dart';
|
||||||
import '../services/product_service.dart';
|
import '../services/product_service.dart';
|
||||||
@@ -13,11 +14,18 @@ class InventoryViewModel extends ChangeNotifier {
|
|||||||
List<Product> get products => _products;
|
List<Product> get products => _products;
|
||||||
|
|
||||||
Future<void> load() async {
|
Future<void> load() async {
|
||||||
|
try {
|
||||||
_products = await productService.getProducts();
|
_products = await productService.getProducts();
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('InventoryViewModel: load error: $e');
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> decreaseStock(String productId, int amount) async {
|
Future<void> decreaseStock(String productId, int amount) async {
|
||||||
|
try {
|
||||||
await productService.decreaseStock(productId, amount);
|
await productService.decreaseStock(productId, amount);
|
||||||
|
|
||||||
final index = _products.indexWhere((product) => product.id == productId);
|
final index = _products.indexWhere((product) => product.id == productId);
|
||||||
@@ -29,9 +37,15 @@ class InventoryViewModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('InventoryViewModel: decreaseStock error: $e');
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> increaseStock(String productId, int amount) async {
|
Future<void> increaseStock(String productId, int amount) async {
|
||||||
|
try {
|
||||||
await productService.increaseStock(productId, amount);
|
await productService.increaseStock(productId, amount);
|
||||||
|
|
||||||
final index = _products.indexWhere((product) => product.id == productId);
|
final index = _products.indexWhere((product) => product.id == productId);
|
||||||
@@ -43,5 +57,10 @@ class InventoryViewModel extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('InventoryViewModel: increaseStock error: $e');
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../models/product.dart';
|
import '../models/product.dart';
|
||||||
import '../services/product_service.dart';
|
import '../services/product_service.dart';
|
||||||
@@ -66,6 +67,7 @@ class ProductListViewModel extends ChangeNotifier {
|
|||||||
required int priceInCents,
|
required int priceInCents,
|
||||||
required String? imagePath,
|
required String? imagePath,
|
||||||
}) async {
|
}) async {
|
||||||
|
try {
|
||||||
await productService.createProduct(
|
await productService.createProduct(
|
||||||
name: name,
|
name: name,
|
||||||
category: category,
|
category: category,
|
||||||
@@ -76,16 +78,39 @@ class ProductListViewModel extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
await loadProducts();
|
await loadProducts();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('ProductListViewModel: addProduct error: $e');
|
||||||
|
_errorMessage = 'Could not add product.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateProduct(Product product) async {
|
Future<void> updateProduct(Product product) async {
|
||||||
|
try {
|
||||||
await productService.updateProduct(product);
|
await productService.updateProduct(product);
|
||||||
await loadProducts();
|
await loadProducts();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('ProductListViewModel: updateProduct error: $e');
|
||||||
|
_errorMessage = 'Could not update product.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteProduct(String id) async {
|
Future<void> deleteProduct(String id) async {
|
||||||
|
try {
|
||||||
await productService.deleteProduct(id);
|
await productService.deleteProduct(id);
|
||||||
await loadProducts();
|
await loadProducts();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('ProductListViewModel: deleteProduct error: $e');
|
||||||
|
_errorMessage = 'Could not delete product.';
|
||||||
|
notifyListeners();
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Product?> getProductById(String id) {
|
Future<Product?> getProductById(String id) {
|
||||||
|
|||||||
@@ -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/dialogs/new_tab_dialog.dart';
|
||||||
import 'package:kooltab2/views/widgets/product_tile.dart';
|
import 'package:kooltab2/views/widgets/product_tile.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
import '../models/bar_tab.dart';
|
import '../models/bar_tab.dart';
|
||||||
@@ -53,7 +54,7 @@ class _BarScreenViewState extends State<BarScreenView> {
|
|||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: 'Refresh',
|
tooltip: 'Refresh',
|
||||||
onPressed: viewModel.load,
|
onPressed: () => viewModel.load(),
|
||||||
icon: const Icon(Icons.refresh_rounded),
|
icon: const Icon(Icons.refresh_rounded),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
@@ -127,7 +128,8 @@ class _BarScreenViewState extends State<BarScreenView> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await viewModel.addProductToSelectedTab(product);
|
await viewModel.addProductToSelectedTab(product);
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(content: Text('$e')),
|
SnackBar(content: Text('$e')),
|
||||||
@@ -304,7 +306,7 @@ class _TabPanel extends StatefulWidget {
|
|||||||
final ValueChanged<String> onTabSelected;
|
final ValueChanged<String> onTabSelected;
|
||||||
final Future<void> Function(TabItem item, int quantity) onItemQuantityChanged;
|
final Future<void> Function(TabItem item, int quantity) onItemQuantityChanged;
|
||||||
final VoidCallback onCloseTabPressed;
|
final VoidCallback onCloseTabPressed;
|
||||||
final ValueChanged<String> onTabClosed;
|
final Future<void> Function(String) onTabClosed;
|
||||||
|
|
||||||
const _TabPanel({
|
const _TabPanel({
|
||||||
required this.tabs,
|
required this.tabs,
|
||||||
@@ -460,7 +462,7 @@ class _OpenTabsList extends StatelessWidget {
|
|||||||
final List<BarTab> tabs;
|
final List<BarTab> tabs;
|
||||||
final String? selectedTabId;
|
final String? selectedTabId;
|
||||||
final ValueChanged<String> onTabSelected;
|
final ValueChanged<String> onTabSelected;
|
||||||
final ValueChanged<String> onTabClosed;
|
final Future<void> Function(String) onTabClosed;
|
||||||
|
|
||||||
const _OpenTabsList({
|
const _OpenTabsList({
|
||||||
required this.tabs,
|
required this.tabs,
|
||||||
@@ -498,17 +500,25 @@ class _OpenTabsList extends StatelessWidget {
|
|||||||
extentRatio: 0.6,
|
extentRatio: 0.6,
|
||||||
children: [
|
children: [
|
||||||
SlidableAction(
|
SlidableAction(
|
||||||
onPressed: (_) {
|
onPressed: (_) => onTabSelected(tab.id),
|
||||||
onTabSelected(tab.id);
|
|
||||||
// TODO: rename/edit tab
|
|
||||||
},
|
|
||||||
icon: Icons.edit_outlined,
|
icon: Icons.edit_outlined,
|
||||||
label: 'Edit',
|
label: 'Edit',
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
foregroundColor: Theme.of(context).colorScheme.onSecondary,
|
||||||
),
|
),
|
||||||
SlidableAction(
|
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,
|
icon: Icons.close_rounded,
|
||||||
label: 'Close',
|
label: 'Close',
|
||||||
backgroundColor: Theme.of(context).colorScheme.error,
|
backgroundColor: Theme.of(context).colorScheme.error,
|
||||||
@@ -723,7 +733,20 @@ class _TabItemRow extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
visualDensity: VisualDensity.compact,
|
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),
|
icon: const Icon(Icons.remove_rounded, size: 18),
|
||||||
),
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
@@ -736,7 +759,20 @@ class _TabItemRow extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
visualDensity: VisualDensity.compact,
|
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),
|
icon: const Icon(Icons.add_rounded, size: 18),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/svg.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../../models/payment_method.dart';
|
import '../../models/payment_method.dart';
|
||||||
import '../../viewmodels/bar_screen_view_model.dart';
|
import '../../viewmodels/bar_screen_view_model.dart';
|
||||||
@@ -63,7 +64,15 @@ Future<void> confirmCloseTab(BuildContext context) async {
|
|||||||
|
|
||||||
if (confirmed != true) return;
|
if (confirmed != true) return;
|
||||||
|
|
||||||
|
try {
|
||||||
await viewModel.closeSelectedTab(paymentMethod: paymentMethod);
|
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 {
|
class _PaymentPicker extends StatelessWidget {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
Future<void> showNewTabDialog(BuildContext context) async {
|
Future<void> showNewTabDialog(BuildContext context) async {
|
||||||
final controller = TextEditingController();
|
final controller = TextEditingController();
|
||||||
@@ -39,5 +40,13 @@ Future<void> showNewTabDialog(BuildContext context) async {
|
|||||||
|
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
|
try {
|
||||||
await context.read<BarScreenViewModel>().createTab(name.trim());
|
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/path.dart' as path;
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../models/product.dart';
|
import '../models/product.dart';
|
||||||
import '../viewmodels/product_list_view_model.dart';
|
import '../viewmodels/product_list_view_model.dart';
|
||||||
@@ -52,6 +53,7 @@ class _ProductFormViewState extends State<ProductFormView> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
final viewModel = context.read<ProductListViewModel>();
|
final viewModel = context.read<ProductListViewModel>();
|
||||||
final product = await viewModel.getProductById(widget.productId!);
|
final product = await viewModel.getProductById(widget.productId!);
|
||||||
|
|
||||||
@@ -76,6 +78,14 @@ class _ProductFormViewState extends State<ProductFormView> {
|
|||||||
_hasImage = product.imagePath != null && product.imagePath!.isNotEmpty;
|
_hasImage = product.imagePath != null && product.imagePath!.isNotEmpty;
|
||||||
|
|
||||||
setState(() => _isLoading = false);
|
setState(() => _isLoading = false);
|
||||||
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
if (!mounted) return;
|
||||||
|
ScaffoldMessenger.of(
|
||||||
|
context,
|
||||||
|
).showSnackBar(SnackBar(content: Text('Could not load product: $e')));
|
||||||
|
context.go('/products');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -159,9 +169,10 @@ class _ProductFormViewState extends State<ProductFormView> {
|
|||||||
final name = _nameController.text.trim();
|
final name = _nameController.text.trim();
|
||||||
final category = _selectedCategory!;
|
final category = _selectedCategory!;
|
||||||
final priceInCents = _parsePriceToCents(_priceController.text);
|
final priceInCents = _parsePriceToCents(_priceController.text);
|
||||||
final stockQuantity = int.parse(_stockController.text);
|
final stockQuantity = int.tryParse(_stockController.text) ?? 0;
|
||||||
final lowStockThreshold = int.parse(_lowStockController.text);
|
final lowStockThreshold = int.tryParse(_lowStockController.text) ?? 0;
|
||||||
|
|
||||||
|
try {
|
||||||
if (widget.isEditing) {
|
if (widget.isEditing) {
|
||||||
final updatedProduct = _existingProduct!.copyWith(
|
final updatedProduct = _existingProduct!.copyWith(
|
||||||
name: name,
|
name: name,
|
||||||
@@ -183,6 +194,15 @@ class _ProductFormViewState extends State<ProductFormView> {
|
|||||||
imagePath: _imagePath,
|
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;
|
if (!mounted) return;
|
||||||
|
|
||||||
@@ -218,7 +238,17 @@ class _ProductFormViewState extends State<ProductFormView> {
|
|||||||
if (confirmed != true) return;
|
if (confirmed != true) return;
|
||||||
|
|
||||||
final viewModel = context.read<ProductListViewModel>();
|
final viewModel = context.read<ProductListViewModel>();
|
||||||
|
|
||||||
|
try {
|
||||||
await viewModel.deleteProduct(widget.productId!);
|
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;
|
if (!mounted) return;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||||
|
|
||||||
import '../models/settings.dart';
|
import '../models/settings.dart';
|
||||||
import '../utils/app_update_util.dart';
|
import '../utils/app_update_util.dart';
|
||||||
@@ -117,7 +118,13 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pinLockViewModel.setPinRequired(true);
|
pinLockViewModel.setPinRequired(true);
|
||||||
|
|
||||||
|
try {
|
||||||
await context.read<SettingsViewModel>().updatePinRequired(true);
|
await context.read<SettingsViewModel>().updatePinRequired(true);
|
||||||
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
_showError('Could not save PIN setting.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _changePin() async {
|
Future<void> _changePin() async {
|
||||||
@@ -156,7 +163,13 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pinLockViewModel.setPinRequired(false);
|
pinLockViewModel.setPinRequired(false);
|
||||||
|
|
||||||
|
try {
|
||||||
await context.read<SettingsViewModel>().updatePinRequired(false);
|
await context.read<SettingsViewModel>().updatePinRequired(false);
|
||||||
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
|
_showError('Could not save PIN setting.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -317,7 +330,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_showUpdateDialog(update);
|
_showUpdateDialog(update);
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
@@ -360,7 +374,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e, stack) {
|
||||||
|
Sentry.captureException(e, stackTrace: stack);
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
ScaffoldMessenger.of(
|
ScaffoldMessenger.of(
|
||||||
|
|||||||
+20
-12
@@ -516,18 +516,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: jni
|
name: jni
|
||||||
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
|
sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "0.14.2"
|
||||||
jni_flutter:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: jni_flutter
|
|
||||||
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.1"
|
|
||||||
json_annotation:
|
json_annotation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -708,10 +700,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
sha256: "149441ca6e4f38193b2e004c0ca6376a3d11f51fa5a77552d8bd4d2b0c0912ba"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.1"
|
version: "2.2.23"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -816,6 +808,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.0"
|
version: "0.6.0"
|
||||||
|
sentry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sentry
|
||||||
|
sha256: a84bf3a83b3ce1c89fce28b9f97659e3826df14a73a256952465aac2c5efdf5a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.25.0"
|
||||||
|
sentry_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sentry_flutter
|
||||||
|
sha256: c85575266d91f57364e9b4cb522835156ec3c5581c78339dc7fae491cd0c6f76
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.25.0"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ dependencies:
|
|||||||
open_filex: ^4.7.0
|
open_filex: ^4.7.0
|
||||||
ota_update: ^7.1.0
|
ota_update: ^7.1.0
|
||||||
flutter_svg: ^2.3.0
|
flutter_svg: ^2.3.0
|
||||||
|
sentry_flutter: ^9.25.0
|
||||||
# permission_handler: ^12.0.3
|
# permission_handler: ^12.0.3
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user