feat: add payment method
This commit is contained in:
+10
-1
@@ -34,7 +34,7 @@ class _KoolTabAppState extends State<KoolTabApp> {
|
||||
debugShowCheckedModeBanner: false,
|
||||
routerConfig: widget.router,
|
||||
theme: lightTheme,
|
||||
darkTheme: darkTheme,
|
||||
darkTheme: _themeFor(settings.themeMode),
|
||||
themeMode: _mapThemeMode(settings.themeMode, context),
|
||||
);
|
||||
}
|
||||
@@ -45,8 +45,17 @@ class _KoolTabAppState extends State<KoolTabApp> {
|
||||
return switch (mode) {
|
||||
AppThemeMode.dark => ThemeMode.dark,
|
||||
AppThemeMode.light => ThemeMode.light,
|
||||
AppThemeMode.ugly => ThemeMode.dark,
|
||||
AppThemeMode.system =>
|
||||
hasLoaded ? ThemeMode.system : ThemeMode.dark,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
ThemeData _themeFor(AppThemeMode mode) {
|
||||
return switch (mode) {
|
||||
AppThemeMode.ugly => uglyTheme,
|
||||
_ => darkTheme,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,8 @@ class ClosedTabs extends Table {
|
||||
|
||||
DateTimeColumn get closedAt => dateTime()();
|
||||
|
||||
TextColumn get paymentMethod => text().withDefault(const Constant('cash'))();
|
||||
|
||||
@override
|
||||
Set<Column> get primaryKey => {id};
|
||||
}
|
||||
@@ -126,7 +128,7 @@ class AppDatabase extends _$AppDatabase {
|
||||
AppDatabase([QueryExecutor? executor]) : super(executor ?? _openConnection());
|
||||
|
||||
@override
|
||||
int get schemaVersion => 5;
|
||||
int get schemaVersion => 6;
|
||||
|
||||
@override
|
||||
MigrationStrategy get migration {
|
||||
@@ -152,6 +154,10 @@ class AppDatabase extends _$AppDatabase {
|
||||
if (from < 5) {
|
||||
await migrator.createTable(appSettingsTable);
|
||||
}
|
||||
|
||||
if (from < 6) {
|
||||
await migrator.addColumn(closedTabs, closedTabs.paymentMethod);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1417,12 +1417,25 @@ class $ClosedTabsTable extends ClosedTabs
|
||||
type: DriftSqlType.dateTime,
|
||||
requiredDuringInsert: true,
|
||||
);
|
||||
static const VerificationMeta _paymentMethodMeta = const VerificationMeta(
|
||||
'paymentMethod',
|
||||
);
|
||||
@override
|
||||
late final GeneratedColumn<String> paymentMethod = GeneratedColumn<String>(
|
||||
'payment_method',
|
||||
aliasedName,
|
||||
false,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
defaultValue: const Constant('cash'),
|
||||
);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [
|
||||
id,
|
||||
originalTabId,
|
||||
customerName,
|
||||
closedAt,
|
||||
paymentMethod,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@@ -1471,6 +1484,15 @@ class $ClosedTabsTable extends ClosedTabs
|
||||
} else if (isInserting) {
|
||||
context.missing(_closedAtMeta);
|
||||
}
|
||||
if (data.containsKey('payment_method')) {
|
||||
context.handle(
|
||||
_paymentMethodMeta,
|
||||
paymentMethod.isAcceptableOrUnknown(
|
||||
data['payment_method']!,
|
||||
_paymentMethodMeta,
|
||||
),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -1496,6 +1518,10 @@ class $ClosedTabsTable extends ClosedTabs
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}closed_at'],
|
||||
)!,
|
||||
paymentMethod: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}payment_method'],
|
||||
)!,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1510,11 +1536,13 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
final String originalTabId;
|
||||
final String customerName;
|
||||
final DateTime closedAt;
|
||||
final String paymentMethod;
|
||||
const ClosedTabsRow({
|
||||
required this.id,
|
||||
required this.originalTabId,
|
||||
required this.customerName,
|
||||
required this.closedAt,
|
||||
required this.paymentMethod,
|
||||
});
|
||||
@override
|
||||
Map<String, Expression> toColumns(bool nullToAbsent) {
|
||||
@@ -1523,6 +1551,7 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
map['original_tab_id'] = Variable<String>(originalTabId);
|
||||
map['customer_name'] = Variable<String>(customerName);
|
||||
map['closed_at'] = Variable<DateTime>(closedAt);
|
||||
map['payment_method'] = Variable<String>(paymentMethod);
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -1532,6 +1561,7 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
originalTabId: Value(originalTabId),
|
||||
customerName: Value(customerName),
|
||||
closedAt: Value(closedAt),
|
||||
paymentMethod: Value(paymentMethod),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1545,6 +1575,7 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
originalTabId: serializer.fromJson<String>(json['originalTabId']),
|
||||
customerName: serializer.fromJson<String>(json['customerName']),
|
||||
closedAt: serializer.fromJson<DateTime>(json['closedAt']),
|
||||
paymentMethod: serializer.fromJson<String>(json['paymentMethod']),
|
||||
);
|
||||
}
|
||||
@override
|
||||
@@ -1555,6 +1586,7 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
'originalTabId': serializer.toJson<String>(originalTabId),
|
||||
'customerName': serializer.toJson<String>(customerName),
|
||||
'closedAt': serializer.toJson<DateTime>(closedAt),
|
||||
'paymentMethod': serializer.toJson<String>(paymentMethod),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1563,11 +1595,13 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
String? originalTabId,
|
||||
String? customerName,
|
||||
DateTime? closedAt,
|
||||
String? paymentMethod,
|
||||
}) => ClosedTabsRow(
|
||||
id: id ?? this.id,
|
||||
originalTabId: originalTabId ?? this.originalTabId,
|
||||
customerName: customerName ?? this.customerName,
|
||||
closedAt: closedAt ?? this.closedAt,
|
||||
paymentMethod: paymentMethod ?? this.paymentMethod,
|
||||
);
|
||||
ClosedTabsRow copyWithCompanion(ClosedTabsCompanion data) {
|
||||
return ClosedTabsRow(
|
||||
@@ -1579,6 +1613,9 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
? data.customerName.value
|
||||
: this.customerName,
|
||||
closedAt: data.closedAt.present ? data.closedAt.value : this.closedAt,
|
||||
paymentMethod: data.paymentMethod.present
|
||||
? data.paymentMethod.value
|
||||
: this.paymentMethod,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1588,13 +1625,15 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
..write('id: $id, ')
|
||||
..write('originalTabId: $originalTabId, ')
|
||||
..write('customerName: $customerName, ')
|
||||
..write('closedAt: $closedAt')
|
||||
..write('closedAt: $closedAt, ')
|
||||
..write('paymentMethod: $paymentMethod')
|
||||
..write(')'))
|
||||
.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(id, originalTabId, customerName, closedAt);
|
||||
int get hashCode =>
|
||||
Object.hash(id, originalTabId, customerName, closedAt, paymentMethod);
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@@ -1602,7 +1641,8 @@ class ClosedTabsRow extends DataClass implements Insertable<ClosedTabsRow> {
|
||||
other.id == this.id &&
|
||||
other.originalTabId == this.originalTabId &&
|
||||
other.customerName == this.customerName &&
|
||||
other.closedAt == this.closedAt);
|
||||
other.closedAt == this.closedAt &&
|
||||
other.paymentMethod == this.paymentMethod);
|
||||
}
|
||||
|
||||
class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
@@ -1610,12 +1650,14 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
final Value<String> originalTabId;
|
||||
final Value<String> customerName;
|
||||
final Value<DateTime> closedAt;
|
||||
final Value<String> paymentMethod;
|
||||
final Value<int> rowid;
|
||||
const ClosedTabsCompanion({
|
||||
this.id = const Value.absent(),
|
||||
this.originalTabId = const Value.absent(),
|
||||
this.customerName = const Value.absent(),
|
||||
this.closedAt = const Value.absent(),
|
||||
this.paymentMethod = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
ClosedTabsCompanion.insert({
|
||||
@@ -1623,6 +1665,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
required String originalTabId,
|
||||
required String customerName,
|
||||
required DateTime closedAt,
|
||||
this.paymentMethod = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
}) : id = Value(id),
|
||||
originalTabId = Value(originalTabId),
|
||||
@@ -1633,6 +1676,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
Expression<String>? originalTabId,
|
||||
Expression<String>? customerName,
|
||||
Expression<DateTime>? closedAt,
|
||||
Expression<String>? paymentMethod,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
@@ -1640,6 +1684,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
if (originalTabId != null) 'original_tab_id': originalTabId,
|
||||
if (customerName != null) 'customer_name': customerName,
|
||||
if (closedAt != null) 'closed_at': closedAt,
|
||||
if (paymentMethod != null) 'payment_method': paymentMethod,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
@@ -1649,6 +1694,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
Value<String>? originalTabId,
|
||||
Value<String>? customerName,
|
||||
Value<DateTime>? closedAt,
|
||||
Value<String>? paymentMethod,
|
||||
Value<int>? rowid,
|
||||
}) {
|
||||
return ClosedTabsCompanion(
|
||||
@@ -1656,6 +1702,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
originalTabId: originalTabId ?? this.originalTabId,
|
||||
customerName: customerName ?? this.customerName,
|
||||
closedAt: closedAt ?? this.closedAt,
|
||||
paymentMethod: paymentMethod ?? this.paymentMethod,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
@@ -1675,6 +1722,9 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
if (closedAt.present) {
|
||||
map['closed_at'] = Variable<DateTime>(closedAt.value);
|
||||
}
|
||||
if (paymentMethod.present) {
|
||||
map['payment_method'] = Variable<String>(paymentMethod.value);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = Variable<int>(rowid.value);
|
||||
}
|
||||
@@ -1688,6 +1738,7 @@ class ClosedTabsCompanion extends UpdateCompanion<ClosedTabsRow> {
|
||||
..write('originalTabId: $originalTabId, ')
|
||||
..write('customerName: $customerName, ')
|
||||
..write('closedAt: $closedAt, ')
|
||||
..write('paymentMethod: $paymentMethod, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
@@ -3536,6 +3587,7 @@ typedef $$ClosedTabsTableCreateCompanionBuilder =
|
||||
required String originalTabId,
|
||||
required String customerName,
|
||||
required DateTime closedAt,
|
||||
Value<String> paymentMethod,
|
||||
Value<int> rowid,
|
||||
});
|
||||
typedef $$ClosedTabsTableUpdateCompanionBuilder =
|
||||
@@ -3544,6 +3596,7 @@ typedef $$ClosedTabsTableUpdateCompanionBuilder =
|
||||
Value<String> originalTabId,
|
||||
Value<String> customerName,
|
||||
Value<DateTime> closedAt,
|
||||
Value<String> paymentMethod,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
||||
@@ -3575,6 +3628,11 @@ class $$ClosedTabsTableFilterComposer
|
||||
column: $table.closedAt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get paymentMethod => $composableBuilder(
|
||||
column: $table.paymentMethod,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$ClosedTabsTableOrderingComposer
|
||||
@@ -3605,6 +3663,11 @@ class $$ClosedTabsTableOrderingComposer
|
||||
column: $table.closedAt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get paymentMethod => $composableBuilder(
|
||||
column: $table.paymentMethod,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $$ClosedTabsTableAnnotationComposer
|
||||
@@ -3631,6 +3694,11 @@ class $$ClosedTabsTableAnnotationComposer
|
||||
|
||||
GeneratedColumn<DateTime> get closedAt =>
|
||||
$composableBuilder(column: $table.closedAt, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get paymentMethod => $composableBuilder(
|
||||
column: $table.paymentMethod,
|
||||
builder: (column) => column,
|
||||
);
|
||||
}
|
||||
|
||||
class $$ClosedTabsTableTableManager
|
||||
@@ -3668,12 +3736,14 @@ class $$ClosedTabsTableTableManager
|
||||
Value<String> originalTabId = const Value.absent(),
|
||||
Value<String> customerName = const Value.absent(),
|
||||
Value<DateTime> closedAt = const Value.absent(),
|
||||
Value<String> paymentMethod = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => ClosedTabsCompanion(
|
||||
id: id,
|
||||
originalTabId: originalTabId,
|
||||
customerName: customerName,
|
||||
closedAt: closedAt,
|
||||
paymentMethod: paymentMethod,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
@@ -3682,12 +3752,14 @@ class $$ClosedTabsTableTableManager
|
||||
required String originalTabId,
|
||||
required String customerName,
|
||||
required DateTime closedAt,
|
||||
Value<String> paymentMethod = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => ClosedTabsCompanion.insert(
|
||||
id: id,
|
||||
originalTabId: originalTabId,
|
||||
customerName: customerName,
|
||||
closedAt: closedAt,
|
||||
paymentMethod: paymentMethod,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
|
||||
@@ -7,6 +7,7 @@ class ClosedTab {
|
||||
final String originalTabId;
|
||||
final String customerName;
|
||||
final DateTime closedAt;
|
||||
final String paymentMethod;
|
||||
final List<ClosedTabItem> items;
|
||||
|
||||
ClosedTab({
|
||||
@@ -14,6 +15,7 @@ class ClosedTab {
|
||||
required this.originalTabId,
|
||||
required this.customerName,
|
||||
required this.closedAt,
|
||||
required this.paymentMethod,
|
||||
required this.items,
|
||||
});
|
||||
|
||||
@@ -25,6 +27,14 @@ class ClosedTab {
|
||||
String get formattedTotal =>
|
||||
NumberFormat.simpleCurrency().format(totalInCents / 100);
|
||||
|
||||
String get formattedPaymentMethod {
|
||||
return switch (paymentMethod) {
|
||||
'cash' => 'Cash',
|
||||
'payconiq' => 'Payconiq',
|
||||
_ => 'Cash',
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
@@ -33,6 +43,7 @@ class ClosedTab {
|
||||
originalTabId == other.originalTabId &&
|
||||
customerName == other.customerName &&
|
||||
closedAt == other.closedAt &&
|
||||
paymentMethod == other.paymentMethod &&
|
||||
_listEquals(items, other.items);
|
||||
|
||||
@override
|
||||
@@ -41,6 +52,7 @@ class ClosedTab {
|
||||
originalTabId,
|
||||
customerName,
|
||||
closedAt,
|
||||
paymentMethod,
|
||||
Object.hashAll(items),
|
||||
);
|
||||
}
|
||||
@@ -53,4 +65,4 @@ bool _listEquals<T>(List<T>? a, List<T>? b) {
|
||||
if (a[i] != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
enum AppThemeMode { system, light, dark }
|
||||
enum AppThemeMode { system, light, dark, ugly }
|
||||
|
||||
class AppSettings {
|
||||
final bool pinRequired;
|
||||
|
||||
@@ -29,7 +29,7 @@ abstract class BarTabService {
|
||||
|
||||
/// Archives the tab's current items into history and clears them.
|
||||
/// The tab itself stays open under the same customer name.
|
||||
Future<void> closeTab(String tabId);
|
||||
Future<void> closeTab(String tabId, {String paymentMethod = 'cash'});
|
||||
|
||||
Future<List<ClosedTab>> getClosedTabs();
|
||||
|
||||
@@ -234,7 +234,7 @@ class DriftBarTabService implements BarTabService {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> closeTab(String tabId) async {
|
||||
Future<void> closeTab(String tabId, {String paymentMethod = 'cash'}) async {
|
||||
await database.transaction(() async {
|
||||
final tabQuery = database.select(database.barTabs)
|
||||
..where((tab) => tab.id.equals(tabId));
|
||||
@@ -261,6 +261,7 @@ class DriftBarTabService implements BarTabService {
|
||||
originalTabId: tabId,
|
||||
customerName: tabRow.customerName,
|
||||
closedAt: DateTime.now(),
|
||||
paymentMethod: Value(paymentMethod),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -325,6 +326,7 @@ class DriftBarTabService implements BarTabService {
|
||||
originalTabId: row.originalTabId,
|
||||
customerName: row.customerName,
|
||||
closedAt: row.closedAt,
|
||||
paymentMethod: row.paymentMethod,
|
||||
items: itemRows.map<ClosedTabItem>(_mapClosedItemRow).toList(),
|
||||
),
|
||||
);
|
||||
|
||||
+319
@@ -727,3 +727,322 @@ final ThemeData lightTheme = ThemeData(
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ugly theme — intentionally repulsive
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const _uglyFg = Color(0xFFFFF700);
|
||||
const _uglyBg = Color(0xFF00FFFF);
|
||||
const _uglySurface = Color(0xFFFF00FF);
|
||||
const _uglyPrimary = Color(0xFFCCFF00);
|
||||
const _uglySecondary = Color(0xFFFF1493);
|
||||
const _uglyError = Color(0xFFFF6600);
|
||||
|
||||
final ThemeData uglyTheme = ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.light,
|
||||
|
||||
colorScheme: const ColorScheme.light(
|
||||
primary: _uglyPrimary,
|
||||
secondary: _uglySecondary,
|
||||
surface: _uglySurface,
|
||||
error: _uglyError,
|
||||
onPrimary: Color(0xFF0000FF),
|
||||
onSecondary: _uglyFg,
|
||||
onSurface: _uglyFg,
|
||||
onError: Color(0xFF000000),
|
||||
onSurfaceVariant: Color(0xFFFF0000),
|
||||
),
|
||||
|
||||
scaffoldBackgroundColor: _uglyBg,
|
||||
canvasColor: _uglyBg,
|
||||
|
||||
appBarTheme: const AppBarTheme(
|
||||
elevation: 8,
|
||||
shadowColor: Color(0xFFFF0000),
|
||||
centerTitle: true,
|
||||
backgroundColor: _uglySecondary,
|
||||
foregroundColor: _uglyFg,
|
||||
surfaceTintColor: _uglyError,
|
||||
titleTextStyle: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w900,
|
||||
letterSpacing: 4,
|
||||
color: _uglyFg,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
iconTheme: IconThemeData(color: _uglyBg, size: 30),
|
||||
actionsIconTheme: IconThemeData(color: _uglyFg, size: 30),
|
||||
toolbarHeight: 80,
|
||||
),
|
||||
|
||||
iconButtonTheme: IconButtonThemeData(
|
||||
style: IconButton.styleFrom(
|
||||
backgroundColor: _uglyError,
|
||||
foregroundColor: Colors.black,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(0),
|
||||
topRight: Radius.circular(20),
|
||||
bottomLeft: Radius.circular(20),
|
||||
bottomRight: Radius.circular(0),
|
||||
),
|
||||
),
|
||||
side: const BorderSide(color: _uglyPrimary, width: 3),
|
||||
padding: const EdgeInsets.all(16),
|
||||
),
|
||||
),
|
||||
|
||||
cardTheme: CardThemeData(
|
||||
color: _uglySurface,
|
||||
elevation: 8,
|
||||
shadowColor: _uglyPrimary,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 18),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(4),
|
||||
topRight: Radius.circular(30),
|
||||
bottomLeft: Radius.circular(30),
|
||||
bottomRight: Radius.circular(4),
|
||||
),
|
||||
side: const BorderSide(color: _uglySecondary, width: 4),
|
||||
),
|
||||
),
|
||||
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(
|
||||
elevation: 4,
|
||||
backgroundColor: _uglyPrimary,
|
||||
foregroundColor: const Color(0xFF0000FF),
|
||||
shadowColor: _uglyError,
|
||||
disabledBackgroundColor: _uglyBg,
|
||||
disabledForegroundColor: _uglyError,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 22),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
side: const BorderSide(color: _uglySecondary, width: 3),
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w900,
|
||||
letterSpacing: 3,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
elevation: 6,
|
||||
backgroundColor: _uglyError,
|
||||
foregroundColor: _uglyBg,
|
||||
shadowColor: _uglyPrimary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28, vertical: 20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
side: const BorderSide(color: _uglyFg, width: 4),
|
||||
),
|
||||
textStyle: const TextStyle(
|
||||
fontSize: 17,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: _uglyFg,
|
||||
side: const BorderSide(color: _uglySecondary, width: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 18),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(0)),
|
||||
textStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: _uglyError,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w900, fontSize: 16),
|
||||
),
|
||||
),
|
||||
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: Color(0xFFAAFFAA),
|
||||
hintStyle: TextStyle(
|
||||
color: Color(0xFFFF0000),
|
||||
fontWeight: FontWeight.w900,
|
||||
fontStyle: FontStyle.italic,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 24, vertical: 20),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
bottomRight: Radius.circular(16),
|
||||
),
|
||||
borderSide: BorderSide(color: _uglySecondary, width: 4),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
bottomRight: Radius.circular(16),
|
||||
),
|
||||
borderSide: BorderSide(color: _uglyError, width: 4),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
bottomRight: Radius.circular(16),
|
||||
),
|
||||
borderSide: BorderSide(color: _uglyPrimary, width: 6),
|
||||
),
|
||||
),
|
||||
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: _uglySurface,
|
||||
elevation: 16,
|
||||
shadowColor: _uglyFg,
|
||||
surfaceTintColor: _uglyError,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
side: const BorderSide(color: _uglyPrimary, width: 5),
|
||||
),
|
||||
titleTextStyle: const TextStyle(
|
||||
color: _uglyFg,
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
contentTextStyle: const TextStyle(
|
||||
color: _uglyBg,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.8,
|
||||
),
|
||||
),
|
||||
|
||||
floatingActionButtonTheme: const FloatingActionButtonThemeData(
|
||||
backgroundColor: _uglySecondary,
|
||||
foregroundColor: _uglyFg,
|
||||
elevation: 8,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(30),
|
||||
bottomRight: Radius.circular(30),
|
||||
),
|
||||
side: BorderSide(color: _uglyPrimary, width: 4),
|
||||
),
|
||||
),
|
||||
|
||||
listTileTheme: ListTileThemeData(
|
||||
tileColor: _uglyFg,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
side: const BorderSide(color: _uglySecondary, width: 3),
|
||||
),
|
||||
iconColor: _uglyBg,
|
||||
textColor: _uglySurface,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),
|
||||
),
|
||||
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
backgroundColor: _uglyPrimary,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
elevation: 8,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
side: const BorderSide(color: _uglySecondary, width: 4),
|
||||
),
|
||||
contentTextStyle: const TextStyle(
|
||||
color: Color(0xFF0000FF),
|
||||
fontWeight: FontWeight.w900,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: _uglySecondary,
|
||||
thickness: 5,
|
||||
space: 8,
|
||||
),
|
||||
|
||||
chipTheme: const ChipThemeData(
|
||||
backgroundColor: _uglyError,
|
||||
labelStyle: TextStyle(
|
||||
color: _uglyFg,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(20),
|
||||
bottomLeft: Radius.circular(20),
|
||||
),
|
||||
side: BorderSide(color: _uglyPrimary, width: 3),
|
||||
),
|
||||
),
|
||||
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
color: _uglyFg,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 52,
|
||||
letterSpacing: 5,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
headlineMedium: TextStyle(
|
||||
color: _uglyPrimary,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 34,
|
||||
letterSpacing: 3,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
titleLarge: TextStyle(
|
||||
color: _uglyFg,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 26,
|
||||
letterSpacing: 2,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
titleMedium: TextStyle(
|
||||
color: _uglyPrimary,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 20,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
bodyLarge: TextStyle(
|
||||
color: _uglyFg,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.8,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
bodyMedium: TextStyle(
|
||||
color: _uglyBg,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontSize: 14,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
bodySmall: TextStyle(
|
||||
color: _uglyError,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 12,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
labelLarge: TextStyle(
|
||||
color: _uglySecondary,
|
||||
fontWeight: FontWeight.w900,
|
||||
fontSize: 16,
|
||||
fontFamily: 'monospace',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -115,18 +115,18 @@ class BarScreenViewModel extends ChangeNotifier {
|
||||
await _reloadTabs();
|
||||
}
|
||||
|
||||
Future<void> closeSelectedTab() async {
|
||||
Future<void> closeSelectedTab({String paymentMethod = 'cash'}) async {
|
||||
final tab = selectedTab;
|
||||
|
||||
if (tab == null) return;
|
||||
|
||||
await barTabService.closeTab(tab.id);
|
||||
await barTabService.closeTab(tab.id, paymentMethod: paymentMethod);
|
||||
|
||||
await _reloadTabs();
|
||||
}
|
||||
|
||||
Future<void> closeTab(String tabId) async {
|
||||
await barTabService.closeTab(tabId);
|
||||
Future<void> closeTab(String tabId, {String paymentMethod = 'cash'}) async {
|
||||
await barTabService.closeTab(tabId, paymentMethod: paymentMethod);
|
||||
|
||||
await _reloadTabs();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:drift/drift.dart';
|
||||
import 'package:kooltab2/database/app_database.dart';
|
||||
import 'package:kooltab2/services/bar_tab_service.dart';
|
||||
import 'package:kooltab2/services/pin_lock_service.dart';
|
||||
@@ -274,6 +275,7 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
'Sam', 'Tina', 'Umar', 'Vera', 'Wes', 'Xena',
|
||||
'Yves', 'Zara',
|
||||
];
|
||||
const paymentMethods = ['cash', 'payconiq'];
|
||||
|
||||
await database.transaction(() async {
|
||||
for (var i = 0; i < count; i++) {
|
||||
@@ -295,6 +297,7 @@ class DevMenuViewModel extends ChangeNotifier {
|
||||
originalTabId: uuid.v4(),
|
||||
customerName: customer,
|
||||
closedAt: closedAt,
|
||||
paymentMethod: Value(paymentMethods[(i * 3) % paymentMethods.length]),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:kooltab2/viewmodels/bar_screen_view_model.dart';
|
||||
import 'package:kooltab2/views/widgets/slide_confirm.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../viewmodels/bar_screen_view_model.dart';
|
||||
import '../widgets/slide_confirm.dart';
|
||||
|
||||
Future<void> confirmCloseTab(BuildContext context) async {
|
||||
final viewModel = context.read<BarScreenViewModel>();
|
||||
@@ -9,39 +11,111 @@ Future<void> confirmCloseTab(BuildContext context) async {
|
||||
|
||||
if (tab == null) return;
|
||||
|
||||
String paymentMethod = 'cash';
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertDialog(
|
||||
title: Text('Close ${tab.customerName}ʼs tab?'),
|
||||
content: SizedBox(
|
||||
width: 360,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('Current total: ${tab.formattedTotal}'),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
SlideConfirm(
|
||||
onConfirmed: () {
|
||||
Navigator.of(dialogContext).pop(true);
|
||||
},
|
||||
return StatefulBuilder(
|
||||
builder: (context, setDialogState) {
|
||||
return AlertDialog(
|
||||
title: Text('Close ${tab.customerName}ʼs tab?'),
|
||||
content: SizedBox(
|
||||
width: 360,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text('Current total: ${tab.formattedTotal}'),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
'Payment method',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_PaymentPicker(
|
||||
selected: paymentMethod,
|
||||
onChanged: (value) {
|
||||
setDialogState(() => paymentMethod = value);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
SlideConfirm(
|
||||
onConfirmed: () {
|
||||
Navigator.of(dialogContext).pop(true);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actionsPadding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(dialogContext).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (confirmed != true) return;
|
||||
|
||||
await viewModel.closeSelectedTab();
|
||||
await viewModel.closeSelectedTab(paymentMethod: paymentMethod);
|
||||
}
|
||||
|
||||
class _PaymentPicker extends StatelessWidget {
|
||||
final String selected;
|
||||
final ValueChanged<String> onChanged;
|
||||
|
||||
const _PaymentPicker({
|
||||
required this.selected,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
static const _options = [
|
||||
('cash', 'Cash'),
|
||||
('payconiq', 'Payconiq'),
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _options.map((option) {
|
||||
final (value, label) = option;
|
||||
final isSelected = selected == value;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: ChoiceChip(
|
||||
showCheckmark: false,
|
||||
selected: isSelected,
|
||||
onSelected: (_) => onChanged(value),
|
||||
label: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (value == 'cash')
|
||||
const Icon(
|
||||
Icons.attach_money,
|
||||
size: 16,
|
||||
color: Colors.lightGreen,
|
||||
)
|
||||
else
|
||||
SvgPicture.asset(
|
||||
'assets/icons/payconic.svg',
|
||||
width: 16,
|
||||
height: 16,
|
||||
colorFilter: ColorFilter.mode(Colors.pinkAccent, BlendMode.srcIn),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(label),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,7 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
AppThemeMode.system => 'System',
|
||||
AppThemeMode.light => 'Light',
|
||||
AppThemeMode.dark => 'Dark',
|
||||
AppThemeMode.ugly => 'Ugly',
|
||||
},
|
||||
onTap: () async {
|
||||
final selected = await showModalBottomSheet<AppThemeMode>(
|
||||
@@ -234,6 +235,7 @@ class _SettingsScreenViewState extends State<SettingsScreenView> {
|
||||
AppThemeMode.system => 'System',
|
||||
AppThemeMode.light => 'Light',
|
||||
AppThemeMode.dark => 'Dark',
|
||||
AppThemeMode.ugly => 'Ugly',
|
||||
}),
|
||||
onChanged: (value) =>
|
||||
Navigator.pop(context, value),
|
||||
|
||||
@@ -15,6 +15,13 @@ class ClosedTabCard extends StatefulWidget {
|
||||
class _ClosedTabCardState extends State<ClosedTabCard> {
|
||||
bool _expanded = false;
|
||||
|
||||
IconData _paymentIcon(String method) {
|
||||
return switch (method) {
|
||||
'payconiq' => Icons.qr_code_rounded,
|
||||
_ => Icons.money_rounded,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final closedTab = widget.closedTab;
|
||||
@@ -49,15 +56,51 @@ class _ClosedTabCardState extends State<ClosedTabCard> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
closedTab.customerName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
closedTab.customerName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w800,
|
||||
color: scheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 3,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: scheme.primary.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
_paymentIcon(closedTab.paymentMethod),
|
||||
size: 13,
|
||||
color: scheme.primary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
closedTab.formattedPaymentMethod,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: scheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
@@ -163,4 +206,4 @@ class _ClosedTabItemRow extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user