fix: formatting
This commit is contained in:
@@ -13,7 +13,6 @@ class KoolTabApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _KoolTabAppState extends State<KoolTabApp> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp.router(
|
||||
|
||||
@@ -7,10 +7,7 @@ import '../viewmodels/product_list_view_model.dart';
|
||||
class AppBootstrap extends StatefulWidget {
|
||||
final Widget child;
|
||||
|
||||
const AppBootstrap({
|
||||
super.key,
|
||||
required this.child,
|
||||
});
|
||||
const AppBootstrap({super.key, required this.child});
|
||||
|
||||
@override
|
||||
State<AppBootstrap> createState() => _AppBootstrapState();
|
||||
|
||||
+1
-4
@@ -32,10 +32,7 @@ GoRouter createAppRouter(PinLockViewModel pinLockViewModel) {
|
||||
return null;
|
||||
},
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
redirect: (context, state) => '/bar'
|
||||
),
|
||||
GoRoute(path: '/', redirect: (context, state) => '/bar'),
|
||||
GoRoute(
|
||||
path: '/lock',
|
||||
builder: (context, state) => PinEntryView(
|
||||
|
||||
@@ -46,16 +46,10 @@ class BarTabs extends Table {
|
||||
class TabItems extends Table {
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get tabId => text().references(
|
||||
BarTabs,
|
||||
#id,
|
||||
onDelete: KeyAction.cascade,
|
||||
)();
|
||||
TextColumn get tabId =>
|
||||
text().references(BarTabs, #id, onDelete: KeyAction.cascade)();
|
||||
|
||||
TextColumn get productId => text().references(
|
||||
Products,
|
||||
#id,
|
||||
)();
|
||||
TextColumn get productId => text().references(Products, #id)();
|
||||
|
||||
TextColumn get productName => text()();
|
||||
|
||||
@@ -72,8 +66,11 @@ class TabItems extends Table {
|
||||
@DataClassName('ClosedTabsRow')
|
||||
class ClosedTabs extends Table {
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get originalTabId => text()();
|
||||
|
||||
TextColumn get customerName => text()();
|
||||
|
||||
DateTimeColumn get closedAt => dateTime()();
|
||||
|
||||
@override
|
||||
@@ -83,10 +80,15 @@ class ClosedTabs extends Table {
|
||||
@DataClassName('ClosedTabItemRow')
|
||||
class ClosedTabItems extends Table {
|
||||
TextColumn get id => text()();
|
||||
|
||||
TextColumn get closedTabId => text()();
|
||||
|
||||
TextColumn get productId => text()();
|
||||
|
||||
TextColumn get productName => text()();
|
||||
|
||||
IntColumn get quantity => integer()();
|
||||
|
||||
IntColumn get unitPriceInCents => integer()();
|
||||
|
||||
@override
|
||||
@@ -99,7 +101,9 @@ class AppSettingsTable extends Table {
|
||||
String get tableName => 'app_settings';
|
||||
|
||||
TextColumn get id => text()();
|
||||
|
||||
BoolColumn get pinRequired => boolean().withDefault(const Constant(false))();
|
||||
|
||||
TextColumn get themeMode => text().withDefault(const Constant('system'))();
|
||||
|
||||
@override
|
||||
@@ -115,7 +119,7 @@ class AppSettingsTable extends Table {
|
||||
ClosedTabs,
|
||||
ClosedTabItems,
|
||||
|
||||
AppSettingsTable
|
||||
AppSettingsTable,
|
||||
],
|
||||
)
|
||||
class AppDatabase extends _$AppDatabase {
|
||||
|
||||
+4
-5
@@ -55,9 +55,9 @@ Future<void> main() async {
|
||||
DriftSettingsService(database: context.read<AppDatabase>()),
|
||||
),
|
||||
ChangeNotifierProvider<InventoryViewModel>(
|
||||
create: (context) => InventoryViewModel(
|
||||
productService: context.read<ProductService>(),
|
||||
)..load(),
|
||||
create: (context) =>
|
||||
InventoryViewModel(productService: context.read<ProductService>())
|
||||
..load(),
|
||||
),
|
||||
ChangeNotifierProvider<ProductListViewModel>(
|
||||
create: (context) => ProductListViewModel(
|
||||
@@ -66,8 +66,7 @@ Future<void> main() async {
|
||||
),
|
||||
),
|
||||
ChangeNotifierProvider<BarScreenViewModel>(
|
||||
create: (context) =>
|
||||
BarScreenViewModel(
|
||||
create: (context) => BarScreenViewModel(
|
||||
barTabService: context.read<BarTabService>(),
|
||||
inventory: context.read<InventoryViewModel>(),
|
||||
),
|
||||
|
||||
@@ -18,17 +18,11 @@ class BarTab {
|
||||
});
|
||||
|
||||
int get totalInCents {
|
||||
return items.fold<int>(
|
||||
0,
|
||||
(total, item) => total + item.lineTotalInCents,
|
||||
);
|
||||
return items.fold<int>(0, (total, item) => total + item.lineTotalInCents);
|
||||
}
|
||||
|
||||
int get itemCount {
|
||||
return items.fold<int>(
|
||||
0,
|
||||
(total, item) => total + item.quantity,
|
||||
);
|
||||
return items.fold<int>(0, (total, item) => total + item.quantity);
|
||||
}
|
||||
|
||||
String get formattedTotal {
|
||||
|
||||
@@ -4,15 +4,9 @@ class AppSettings {
|
||||
final bool pinRequired;
|
||||
final AppThemeMode themeMode;
|
||||
|
||||
const AppSettings({
|
||||
required this.pinRequired,
|
||||
required this.themeMode,
|
||||
});
|
||||
const AppSettings({required this.pinRequired, required this.themeMode});
|
||||
|
||||
AppSettings copyWith({
|
||||
bool? pinRequired,
|
||||
AppThemeMode? themeMode,
|
||||
}) {
|
||||
AppSettings copyWith({bool? pinRequired, AppThemeMode? themeMode}) {
|
||||
return AppSettings(
|
||||
pinRequired: pinRequired ?? this.pinRequired,
|
||||
themeMode: themeMode ?? this.themeMode,
|
||||
|
||||
@@ -13,9 +13,7 @@ abstract class BarTabService {
|
||||
|
||||
Future<BarTab?> getTabById(String id);
|
||||
|
||||
Future<BarTab> createTab({
|
||||
required String customerName,
|
||||
});
|
||||
Future<BarTab> createTab({required String customerName});
|
||||
|
||||
Future<void> addProductToTab({
|
||||
required String tabId,
|
||||
@@ -38,9 +36,7 @@ class DriftBarTabService implements BarTabService {
|
||||
final AppDatabase database;
|
||||
final _uuid = const Uuid();
|
||||
|
||||
DriftBarTabService({
|
||||
required this.database,
|
||||
});
|
||||
DriftBarTabService({required this.database});
|
||||
|
||||
TabItem _mapItemRow(TabItemRow row) {
|
||||
return TabItem(
|
||||
@@ -53,10 +49,7 @@ class DriftBarTabService implements BarTabService {
|
||||
);
|
||||
}
|
||||
|
||||
BarTab _mapTabRow(
|
||||
BarTabRow row,
|
||||
List<TabItem> items,
|
||||
) {
|
||||
BarTab _mapTabRow(BarTabRow row, List<TabItem> items) {
|
||||
return BarTab(
|
||||
id: row.id,
|
||||
customerName: row.customerName,
|
||||
@@ -81,9 +74,7 @@ class DriftBarTabService implements BarTabService {
|
||||
Future<List<TabItem>> _getItemsForTab(String tabId) async {
|
||||
final query = database.select(database.tabItems)
|
||||
..where((item) => item.tabId.equals(tabId))
|
||||
..orderBy([
|
||||
(item) => OrderingTerm.asc(item.createdAt),
|
||||
]);
|
||||
..orderBy([(item) => OrderingTerm.asc(item.createdAt)]);
|
||||
|
||||
final rows = await query.get();
|
||||
|
||||
@@ -94,9 +85,7 @@ class DriftBarTabService implements BarTabService {
|
||||
Future<List<BarTab>> getOpenTabs() async {
|
||||
final query = database.select(database.barTabs)
|
||||
..where((tab) => tab.status.equals('open'))
|
||||
..orderBy([
|
||||
(tab) => OrderingTerm.desc(tab.openedAt),
|
||||
]);
|
||||
..orderBy([(tab) => OrderingTerm.desc(tab.openedAt)]);
|
||||
|
||||
final tabRows = await query.get();
|
||||
|
||||
@@ -128,12 +117,12 @@ class DriftBarTabService implements BarTabService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<BarTab> createTab({
|
||||
required String customerName,
|
||||
}) async {
|
||||
Future<BarTab> createTab({required String customerName}) async {
|
||||
final id = _uuid.v4();
|
||||
|
||||
await database.into(database.barTabs).insert(
|
||||
await database
|
||||
.into(database.barTabs)
|
||||
.insert(
|
||||
BarTabsCompanion.insert(
|
||||
id: id,
|
||||
customerName: customerName,
|
||||
@@ -160,8 +149,7 @@ class DriftBarTabService implements BarTabService {
|
||||
final existingItemQuery = database.select(database.tabItems)
|
||||
..where(
|
||||
(item) =>
|
||||
item.tabId.equals(tabId) &
|
||||
item.productId.equals(product.id),
|
||||
item.tabId.equals(tabId) & item.productId.equals(product.id),
|
||||
);
|
||||
|
||||
final existingItem = await existingItemQuery.getSingleOrNull();
|
||||
@@ -171,15 +159,15 @@ class DriftBarTabService implements BarTabService {
|
||||
..where((item) => item.id.equals(existingItem.id));
|
||||
|
||||
await updateQuery.write(
|
||||
TabItemsCompanion(
|
||||
quantity: Value(existingItem.quantity + 1),
|
||||
),
|
||||
TabItemsCompanion(quantity: Value(existingItem.quantity + 1)),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await database.into(database.tabItems).insert(
|
||||
await database
|
||||
.into(database.tabItems)
|
||||
.insert(
|
||||
TabItemsCompanion.insert(
|
||||
id: _uuid.v4(),
|
||||
tabId: tabId,
|
||||
@@ -209,11 +197,7 @@ class DriftBarTabService implements BarTabService {
|
||||
final updateQuery = database.update(database.tabItems)
|
||||
..where((item) => item.id.equals(tabItemId));
|
||||
|
||||
await updateQuery.write(
|
||||
TabItemsCompanion(
|
||||
quantity: Value(quantity),
|
||||
),
|
||||
);
|
||||
await updateQuery.write(TabItemsCompanion(quantity: Value(quantity)));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -236,7 +220,9 @@ class DriftBarTabService implements BarTabService {
|
||||
|
||||
final closedTabId = _uuid.v4();
|
||||
|
||||
await database.into(database.closedTabs).insert(
|
||||
await database
|
||||
.into(database.closedTabs)
|
||||
.insert(
|
||||
ClosedTabsCompanion.insert(
|
||||
id: closedTabId,
|
||||
originalTabId: tabId,
|
||||
@@ -246,7 +232,9 @@ class DriftBarTabService implements BarTabService {
|
||||
);
|
||||
|
||||
for (final item in items) {
|
||||
await database.into(database.closedTabItems).insert(
|
||||
await database
|
||||
.into(database.closedTabItems)
|
||||
.insert(
|
||||
ClosedTabItemsCompanion.insert(
|
||||
id: _uuid.v4(),
|
||||
closedTabId: closedTabId,
|
||||
@@ -268,9 +256,7 @@ class DriftBarTabService implements BarTabService {
|
||||
@override
|
||||
Future<List<ClosedTab>> getClosedTabs() async {
|
||||
final query = database.select(database.closedTabs)
|
||||
..orderBy([
|
||||
(tab) => OrderingTerm.desc(tab.closedAt),
|
||||
]);
|
||||
..orderBy([(tab) => OrderingTerm.desc(tab.closedAt)]);
|
||||
|
||||
final closedTabRows = await query.get();
|
||||
|
||||
@@ -282,13 +268,15 @@ class DriftBarTabService implements BarTabService {
|
||||
|
||||
final itemRows = await itemsQuery.get();
|
||||
|
||||
closedTabs.add(ClosedTab(
|
||||
closedTabs.add(
|
||||
ClosedTab(
|
||||
id: row.id,
|
||||
originalTabId: row.originalTabId,
|
||||
customerName: row.customerName,
|
||||
closedAt: row.closedAt,
|
||||
items: itemRows.map<ClosedTabItem>(_mapClosedItemRow).toList(),
|
||||
));
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return closedTabs;
|
||||
|
||||
@@ -23,6 +23,7 @@ abstract class ProductService {
|
||||
Future<void> deleteProduct(String id);
|
||||
|
||||
Future<void> decreaseStock(String productId, int amount);
|
||||
|
||||
Future<void> increaseStock(String productId, int amount);
|
||||
}
|
||||
|
||||
@@ -30,9 +31,7 @@ class DriftProductService implements ProductService {
|
||||
final AppDatabase database;
|
||||
final _uuid = const Uuid();
|
||||
|
||||
DriftProductService({
|
||||
required this.database,
|
||||
});
|
||||
DriftProductService({required this.database});
|
||||
|
||||
Product _mapRowToProduct(ProductRow row) {
|
||||
return Product(
|
||||
@@ -51,9 +50,7 @@ class DriftProductService implements ProductService {
|
||||
Future<List<Product>> getProducts() async {
|
||||
final query = database.select(database.products)
|
||||
..where((product) => product.active.equals(true))
|
||||
..orderBy([
|
||||
(product) => OrderingTerm.asc(product.name),
|
||||
]);
|
||||
..orderBy([(product) => OrderingTerm.asc(product.name)]);
|
||||
|
||||
final rows = await query.get();
|
||||
|
||||
@@ -83,7 +80,9 @@ class DriftProductService implements ProductService {
|
||||
required int priceInCents,
|
||||
required String? imagePath,
|
||||
}) async {
|
||||
await database.into(database.products).insert(
|
||||
await database
|
||||
.into(database.products)
|
||||
.insert(
|
||||
ProductsCompanion.insert(
|
||||
id: _uuid.v4(),
|
||||
name: name,
|
||||
@@ -135,9 +134,7 @@ class DriftProductService implements ProductService {
|
||||
}
|
||||
|
||||
await updateProduct(
|
||||
product.copyWith(
|
||||
stockQuantity: product.stockQuantity - amount,
|
||||
),
|
||||
product.copyWith(stockQuantity: product.stockQuantity - amount),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,10 +147,7 @@ class DriftProductService implements ProductService {
|
||||
}
|
||||
|
||||
await updateProduct(
|
||||
product.copyWith(
|
||||
stockQuantity: product.stockQuantity + amount,
|
||||
),
|
||||
product.copyWith(stockQuantity: product.stockQuantity + amount),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,9 @@ class DriftSettingsService implements SettingsService {
|
||||
|
||||
@override
|
||||
Future<void> saveSettings(AppSettings settings) async {
|
||||
await database.into(database.appSettingsTable).insertOnConflictUpdate(
|
||||
await database
|
||||
.into(database.appSettingsTable)
|
||||
.insertOnConflictUpdate(
|
||||
AppSettingsTableCompanion.insert(
|
||||
id: _settingsId,
|
||||
pinRequired: Value(settings.pinRequired),
|
||||
|
||||
+33
-15
@@ -253,10 +253,7 @@ final ThemeData darkTheme = ThemeData(
|
||||
fontWeight: FontWeight.w800,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
titleMedium: TextStyle(color: Colors.white, fontWeight: FontWeight.w700),
|
||||
bodyLarge: TextStyle(color: Colors.white70, fontSize: 16, height: 1.5),
|
||||
bodyMedium: TextStyle(color: Colors.white60, height: 1.4),
|
||||
bodySmall: TextStyle(color: Colors.white38, height: 1.3),
|
||||
@@ -311,9 +308,17 @@ final ThemeData neoBrutalDarkTheme = ThemeData(
|
||||
fontWeight: FontWeight.w800,
|
||||
color: Colors.white,
|
||||
),
|
||||
bodyLarge: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white),
|
||||
bodyLarge: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
bodyMedium: TextStyle(fontSize: 14, color: Colors.white70),
|
||||
bodySmall: TextStyle(fontSize: 12, color: Colors.white54, fontWeight: FontWeight.w600),
|
||||
bodySmall: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white54,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
@@ -331,7 +336,10 @@ final ThemeData neoBrutalDarkTheme = ThemeData(
|
||||
cardTheme: CardThemeData(
|
||||
color: _nbSurface,
|
||||
elevation: 0,
|
||||
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md, vertical: AppSpacing.sm),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.md,
|
||||
vertical: AppSpacing.sm,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
side: const BorderSide(color: Colors.white, width: 3),
|
||||
@@ -380,7 +388,10 @@ final ThemeData neoBrutalDarkTheme = ThemeData(
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: _nbSurface,
|
||||
hintStyle: const TextStyle(color: Colors.white38, fontWeight: FontWeight.w600),
|
||||
hintStyle: const TextStyle(
|
||||
color: Colors.white38,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(18),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
@@ -408,7 +419,10 @@ final ThemeData neoBrutalDarkTheme = ThemeData(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
contentTextStyle: const TextStyle(color: Colors.white70, fontWeight: FontWeight.w600),
|
||||
contentTextStyle: const TextStyle(
|
||||
color: Colors.white70,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
@@ -428,7 +442,10 @@ final ThemeData neoBrutalDarkTheme = ThemeData(
|
||||
side: const BorderSide(color: Colors.white, width: 3),
|
||||
),
|
||||
behavior: SnackBarBehavior.floating,
|
||||
contentTextStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
|
||||
contentTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
|
||||
dividerTheme: const DividerThemeData(color: Colors.white, thickness: 3),
|
||||
@@ -645,7 +662,11 @@ final ThemeData lightTheme = ThemeData(
|
||||
contentTextStyle: const TextStyle(color: Colors.white),
|
||||
),
|
||||
|
||||
dividerTheme: const DividerThemeData(color: _outlineL, thickness: 1, space: 1),
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: _outlineL,
|
||||
thickness: 1,
|
||||
space: 1,
|
||||
),
|
||||
|
||||
chipTheme: ChipThemeData(
|
||||
backgroundColor: _surfaceRaisedL,
|
||||
@@ -673,10 +694,7 @@ final ThemeData lightTheme = ThemeData(
|
||||
fontWeight: FontWeight.w800,
|
||||
letterSpacing: -0.2,
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
titleMedium: TextStyle(color: Colors.black87, fontWeight: FontWeight.w700),
|
||||
bodyLarge: TextStyle(color: Colors.black54, fontSize: 16, height: 1.5),
|
||||
bodyMedium: TextStyle(color: Colors.black45, height: 1.4),
|
||||
bodySmall: TextStyle(color: Colors.black38, height: 1.3),
|
||||
|
||||
+12
-41
@@ -13,9 +13,7 @@ class UpdateChecker {
|
||||
|
||||
_hasChecked = true;
|
||||
|
||||
final updater = AppUpdateUtil(
|
||||
serverUrl: "https://updater.brammie15.dev",
|
||||
);
|
||||
final updater = AppUpdateUtil(serverUrl: "https://updater.brammie15.dev");
|
||||
|
||||
try {
|
||||
final update = await updater.checkForUpdate();
|
||||
@@ -35,10 +33,7 @@ class UpdateChecker {
|
||||
}
|
||||
}
|
||||
|
||||
static void _showUpdateDialog(
|
||||
BuildContext context,
|
||||
UpdateInfo update,
|
||||
) {
|
||||
static void _showUpdateDialog(BuildContext context, UpdateInfo update) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: !update.mandatory,
|
||||
@@ -84,60 +79,36 @@ Future<void> _downloadAndInstall(UpdateInfo update) async {
|
||||
destinationFilename: "update.apk",
|
||||
sha256checksum: update.sha256,
|
||||
)
|
||||
.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) {
|
||||
|
||||
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;
|
||||
|
||||
|
||||
case OtaStatus.INSTALLING:
|
||||
debugPrint(
|
||||
"Installing update",
|
||||
);
|
||||
debugPrint("Installing update");
|
||||
break;
|
||||
|
||||
|
||||
case OtaStatus.INSTALLATION_ERROR:
|
||||
debugPrint(
|
||||
"Installation error: ${event.value}",
|
||||
);
|
||||
debugPrint("Installation error: ${event.value}");
|
||||
break;
|
||||
|
||||
|
||||
case OtaStatus.DOWNLOAD_ERROR:
|
||||
debugPrint(
|
||||
"Download error: ${event.value}",
|
||||
);
|
||||
debugPrint("Download error: ${event.value}");
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
"OTA update failed: $e",
|
||||
);
|
||||
debugPrint("OTA update failed: $e");
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,7 @@ class BarScreenViewModel extends ChangeNotifier {
|
||||
final BarTabService barTabService;
|
||||
final InventoryViewModel inventory;
|
||||
|
||||
BarScreenViewModel({
|
||||
required this.barTabService,
|
||||
required this.inventory,
|
||||
});
|
||||
BarScreenViewModel({required this.barTabService, required this.inventory});
|
||||
|
||||
List<BarTab> _tabs = [];
|
||||
String? _selectedTabId;
|
||||
@@ -91,10 +88,7 @@ class BarScreenViewModel extends ChangeNotifier {
|
||||
throw Exception('Product is out of stock.');
|
||||
}
|
||||
|
||||
await barTabService.addProductToTab(
|
||||
tabId: tab.id,
|
||||
product: product,
|
||||
);
|
||||
await barTabService.addProductToTab(tabId: tab.id, product: product);
|
||||
|
||||
await inventory.decreaseStock(product.id, 1);
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import '../services/bar_tab_service.dart';
|
||||
class HistoryViewModel extends ChangeNotifier {
|
||||
final BarTabService barTabService;
|
||||
|
||||
HistoryViewModel({
|
||||
required this.barTabService,
|
||||
});
|
||||
HistoryViewModel({required this.barTabService});
|
||||
|
||||
List<ClosedTab> _closedTabs = [];
|
||||
bool _isLoading = false;
|
||||
@@ -20,8 +18,11 @@ class HistoryViewModel extends ChangeNotifier {
|
||||
if (_searchQuery.isEmpty) return _closedTabs;
|
||||
|
||||
return _closedTabs
|
||||
.where((tab) =>
|
||||
tab.customerName.toLowerCase().contains(_searchQuery.toLowerCase()))
|
||||
.where(
|
||||
(tab) => tab.customerName.toLowerCase().contains(
|
||||
_searchQuery.toLowerCase(),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import '../services/product_service.dart';
|
||||
class InventoryViewModel extends ChangeNotifier {
|
||||
final ProductService productService;
|
||||
|
||||
InventoryViewModel({
|
||||
required this.productService,
|
||||
});
|
||||
InventoryViewModel({required this.productService});
|
||||
|
||||
List<Product> _products = [];
|
||||
|
||||
@@ -19,40 +17,28 @@ class InventoryViewModel extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> decreaseStock(
|
||||
String productId,
|
||||
int amount,
|
||||
) async {
|
||||
Future<void> decreaseStock(String productId, int amount) async {
|
||||
await productService.decreaseStock(productId, amount);
|
||||
|
||||
final index = _products.indexWhere(
|
||||
(product) => product.id == productId,
|
||||
);
|
||||
final index = _products.indexWhere((product) => product.id == productId);
|
||||
|
||||
if (index != -1) {
|
||||
_products[index] = _products[index].copyWith(
|
||||
stockQuantity:
|
||||
_products[index].stockQuantity - amount,
|
||||
stockQuantity: _products[index].stockQuantity - amount,
|
||||
);
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> increaseStock(
|
||||
String productId,
|
||||
int amount,
|
||||
) async {
|
||||
Future<void> increaseStock(String productId, int amount) async {
|
||||
await productService.increaseStock(productId, amount);
|
||||
|
||||
final index = _products.indexWhere(
|
||||
(product) => product.id == productId,
|
||||
);
|
||||
final index = _products.indexWhere((product) => product.id == productId);
|
||||
|
||||
if (index != -1) {
|
||||
_products[index] = _products[index].copyWith(
|
||||
stockQuantity:
|
||||
_products[index].stockQuantity + amount,
|
||||
stockQuantity: _products[index].stockQuantity + amount,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,7 @@ class ProductListViewModel extends ChangeNotifier {
|
||||
final ProductService productService;
|
||||
final InventoryViewModel inventory;
|
||||
|
||||
ProductListViewModel({
|
||||
required this.productService,
|
||||
required this.inventory,
|
||||
});
|
||||
ProductListViewModel({required this.productService, required this.inventory});
|
||||
|
||||
static const List<String> _defaultCategories = [
|
||||
'Bier',
|
||||
|
||||
@@ -24,8 +24,11 @@ class SettingsViewModel extends ChangeNotifier {
|
||||
bool get checkingForUpdates => _checkingForUpdates;
|
||||
|
||||
AppSettings get settings => _settings;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
bool get hasLoaded => _hasLoaded;
|
||||
|
||||
String? get errorMessage => _errorMessage;
|
||||
|
||||
Future<void> ensureLoaded() async {
|
||||
@@ -69,7 +72,6 @@ class SettingsViewModel extends ChangeNotifier {
|
||||
UpdateInfo update, {
|
||||
void Function(double progress)? onProgress,
|
||||
}) async {
|
||||
|
||||
final url = update.download.startsWith("http")
|
||||
? update.download
|
||||
: "https://updater.brammie15.dev${update.download}";
|
||||
@@ -83,28 +85,20 @@ class SettingsViewModel extends ChangeNotifier {
|
||||
);
|
||||
|
||||
await for (final event in stream) {
|
||||
|
||||
debugPrint(
|
||||
"OTA: ${event.status} ${event.value}",
|
||||
);
|
||||
debugPrint("OTA: ${event.status} ${event.value}");
|
||||
|
||||
if (event.status == OtaStatus.DOWNLOADING) {
|
||||
final progress =
|
||||
double.tryParse(event.value ?? "0") ?? 0;
|
||||
final progress = double.tryParse(event.value ?? "0") ?? 0;
|
||||
|
||||
onProgress?.call(progress / 100);
|
||||
}
|
||||
|
||||
if (event.status == OtaStatus.DOWNLOAD_ERROR) {
|
||||
throw Exception(
|
||||
"Download failed: ${event.value}",
|
||||
);
|
||||
throw Exception("Download failed: ${event.value}");
|
||||
}
|
||||
|
||||
if (event.status == OtaStatus.INSTALLATION_ERROR) {
|
||||
throw Exception(
|
||||
"Installation failed: ${event.value}",
|
||||
);
|
||||
throw Exception("Installation failed: ${event.value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ class BarScreenView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _BarScreenViewState extends State<BarScreenView> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
|
||||
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
||||
Future<void> showNewTabDialog(BuildContext context) async {
|
||||
final controller = TextEditingController();
|
||||
|
||||
@@ -14,9 +13,7 @@ Future<void> showNewTabDialog(BuildContext context) async {
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Customer / group name',
|
||||
),
|
||||
decoration: const InputDecoration(labelText: 'Customer / group name'),
|
||||
onSubmitted: (value) {
|
||||
Navigator.of(dialogContext).pop(value);
|
||||
},
|
||||
|
||||
@@ -52,9 +52,12 @@ class _HistoryScreenViewState extends State<HistoryScreenView> with RouteAware {
|
||||
appBar: AppBar(
|
||||
title: Row(
|
||||
children: [
|
||||
IconButton(onPressed: (){
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
context.go('/bar');
|
||||
}, icon: const Icon(Icons.arrow_back)),
|
||||
},
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
const Text('Tab History'),
|
||||
],
|
||||
|
||||
@@ -14,11 +14,7 @@ class PinEntryView extends StatefulWidget {
|
||||
final PinEntryMode mode;
|
||||
final VoidCallback? onSuccess;
|
||||
|
||||
const PinEntryView({
|
||||
super.key,
|
||||
required this.mode,
|
||||
this.onSuccess,
|
||||
});
|
||||
const PinEntryView({super.key, required this.mode, this.onSuccess});
|
||||
|
||||
@override
|
||||
State<PinEntryView> createState() => _PinEntryViewState();
|
||||
@@ -210,7 +206,9 @@ class _PinDots extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
color: isFilled ? scheme.primary : Colors.transparent,
|
||||
border: Border.all(
|
||||
color: isFilled ? scheme.primary : scheme.onSurface.withValues(alpha: 0.3),
|
||||
color: isFilled
|
||||
? scheme.primary
|
||||
: scheme.onSurface.withValues(alpha: 0.3),
|
||||
width: 1.4,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -13,10 +13,7 @@ import '../viewmodels/product_list_view_model.dart';
|
||||
class ProductFormView extends StatefulWidget {
|
||||
final String? productId;
|
||||
|
||||
const ProductFormView({
|
||||
super.key,
|
||||
this.productId,
|
||||
});
|
||||
const ProductFormView({super.key, this.productId});
|
||||
|
||||
bool get isEditing => productId != null;
|
||||
|
||||
@@ -60,11 +57,9 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
if (!mounted) return;
|
||||
|
||||
if (product == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Product not found.'),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('Product not found.')));
|
||||
|
||||
context.go('/products');
|
||||
return;
|
||||
@@ -102,7 +97,8 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
}
|
||||
|
||||
final extension = path.extension(pickedFile.path);
|
||||
final fileName = 'product_${DateTime.now().millisecondsSinceEpoch}$extension';
|
||||
final fileName =
|
||||
'product_${DateTime.now().millisecondsSinceEpoch}$extension';
|
||||
final newPath = path.join(imagesDirectory.path, fileName);
|
||||
|
||||
final copiedFile = await File(pickedFile.path).copy(newPath);
|
||||
@@ -139,19 +135,15 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
if (!_formKey.currentState!.validate()) return;
|
||||
|
||||
if (_imagePath == null || _imagePath!.isEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Choose a product image.'),
|
||||
),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(const SnackBar(content: Text('Choose a product image.')));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectedCategory == null) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Please select a category.'),
|
||||
),
|
||||
const SnackBar(content: Text('Please select a category.')),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -239,19 +231,14 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
height: 220,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
border: Border.all(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: hasImage
|
||||
? Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.file(
|
||||
File(_imagePath!),
|
||||
fit: BoxFit.fitHeight,
|
||||
),
|
||||
Image.file(File(_imagePath!), fit: BoxFit.fitHeight),
|
||||
Positioned(
|
||||
right: 12,
|
||||
bottom: 12,
|
||||
@@ -301,9 +288,7 @@ class _ProductFormViewState extends State<ProductFormView> {
|
||||
),
|
||||
resizeToAvoidBottomInset: false,
|
||||
body: _isLoading
|
||||
? const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
)
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: Center(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 720),
|
||||
|
||||
@@ -87,7 +87,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
|
||||
void _showError(String message) {
|
||||
if (!mounted) return;
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
||||
|
||||
Future<void> _enablePin() async {
|
||||
@@ -168,8 +170,10 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
),
|
||||
body: Builder(
|
||||
builder: (context) {
|
||||
final isLoading = settingsViewModel.isLoading || pinLockViewModel.isLoading;
|
||||
final hasLoaded = settingsViewModel.hasLoaded && pinLockViewModel.hasLoaded;
|
||||
final isLoading =
|
||||
settingsViewModel.isLoading || pinLockViewModel.isLoading;
|
||||
final hasLoaded =
|
||||
settingsViewModel.hasLoaded && pinLockViewModel.hasLoaded;
|
||||
|
||||
if (isLoading && !hasLoaded) {
|
||||
return const Center(
|
||||
@@ -231,7 +235,8 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
AppThemeMode.light => 'Light',
|
||||
AppThemeMode.dark => 'Dark',
|
||||
}),
|
||||
onChanged: (value) => Navigator.pop(context, value),
|
||||
onChanged: (value) =>
|
||||
Navigator.pop(context, value),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
@@ -265,14 +270,10 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Version $_appVersion',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
.withValues(alpha: 0.5),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurface.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -306,9 +307,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text('Update check failed: $e')),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text('Update check failed: $e')));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,9 +350,9 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
} catch (e) {
|
||||
if (!mounted) return;
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Update failed: $e")),
|
||||
);
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text("Update failed: $e")));
|
||||
}
|
||||
},
|
||||
child: const Text("Update"),
|
||||
@@ -391,7 +392,9 @@ class _SettingsSection extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: scheme.onSurface.withValues(alpha: 0.04),
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
border: Border.all(color: scheme.onSurface.withValues(alpha: 0.06)),
|
||||
border: Border.all(
|
||||
color: scheme.onSurface.withValues(alpha: 0.06),
|
||||
),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Column(children: children),
|
||||
@@ -427,12 +430,19 @@ class _SettingsTile extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(icon, size: 22, color: scheme.onSurface.withValues(alpha: 0.7)),
|
||||
Icon(
|
||||
icon,
|
||||
size: 22,
|
||||
color: scheme.onSurface.withValues(alpha: 0.7),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
@@ -440,7 +450,10 @@ class _SettingsTile extends StatelessWidget {
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (onTap != null)
|
||||
Icon(Icons.chevron_right_rounded, color: scheme.onSurface.withValues(alpha: 0.3)),
|
||||
Icon(
|
||||
Icons.chevron_right_rounded,
|
||||
color: scheme.onSurface.withValues(alpha: 0.3),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -475,7 +488,10 @@ class _SettingsSwitchTile extends StatelessWidget {
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Switch(value: value, onChanged: onChanged),
|
||||
|
||||
@@ -120,10 +120,12 @@ class _ClosedTabItemRow extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final unitPrice =
|
||||
NumberFormat.simpleCurrency().format(item.unitPriceInCents / 100);
|
||||
final lineTotal =
|
||||
NumberFormat.simpleCurrency().format(item.lineTotalInCents / 100);
|
||||
final unitPrice = NumberFormat.simpleCurrency().format(
|
||||
item.unitPriceInCents / 100,
|
||||
);
|
||||
final lineTotal = NumberFormat.simpleCurrency().format(
|
||||
item.lineTotalInCents / 100,
|
||||
);
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
|
||||
return Padding(
|
||||
@@ -135,7 +137,10 @@ class _ClosedTabItemRow extends StatelessWidget {
|
||||
item.productName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -148,7 +153,10 @@ class _ClosedTabItemRow extends StatelessWidget {
|
||||
child: Text(
|
||||
lineTotal,
|
||||
textAlign: TextAlign.end,
|
||||
style: TextStyle(fontWeight: FontWeight.w700, color: scheme.onSurface),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:kooltab2/models/product.dart';
|
||||
|
||||
|
||||
class ProductTile extends StatelessWidget {
|
||||
final Product product;
|
||||
final bool enabled;
|
||||
@@ -95,9 +94,7 @@ class ProductTile extends StatelessWidget {
|
||||
Positioned(
|
||||
top: 10,
|
||||
right: 10,
|
||||
child: _StockBadge(
|
||||
product: product,
|
||||
),
|
||||
child: _StockBadge(product: product),
|
||||
),
|
||||
|
||||
// Out of stock overlay
|
||||
@@ -134,9 +131,7 @@ class ProductTile extends StatelessWidget {
|
||||
class _StockBadge extends StatelessWidget {
|
||||
final Product product;
|
||||
|
||||
const _StockBadge({
|
||||
required this.product,
|
||||
});
|
||||
const _StockBadge({required this.product});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -150,10 +145,7 @@ class _StockBadge extends StatelessWidget {
|
||||
product.stockQuantity <= product.lowStockThreshold; // adjust name
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: low
|
||||
? Colors.amber.withValues(alpha: 0.9)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class SlideConfirm extends StatefulWidget {
|
||||
final VoidCallback onConfirmed;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user