Implement status picker and payment dialog for rent status updates

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2026-03-20 00:12:48 +01:00
parent d1d1d9fb95
commit df7705a224
2 changed files with 205 additions and 0 deletions
+143
View File
@@ -8,6 +8,140 @@ import 'package:rental_income_tracker/state/rent_controller.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Future<PaymentStatus?> _showStatusPicker(
BuildContext context,
PaymentStatus current,
) async {
return showModalBottomSheet<PaymentStatus>(
context: context,
builder: (context) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: const Text('Paid on time'),
selected: current == PaymentStatus.onTime,
onTap: () => Navigator.of(context).pop(PaymentStatus.onTime),
),
ListTile(
title: const Text('Paid late'),
selected: current == PaymentStatus.late,
onTap: () => Navigator.of(context).pop(PaymentStatus.late),
),
ListTile(
title: const Text('Not paid'),
selected: current == PaymentStatus.notPaid,
onTap: () => Navigator.of(context).pop(PaymentStatus.notPaid),
),
ListTile(
title: const Text('Pending'),
selected: current == PaymentStatus.pending,
onTap: () => Navigator.of(context).pop(PaymentStatus.pending),
),
],
),
);
},
);
}
Future<double?> _showPaidUsdDialog(
BuildContext context, {
required double initialValue,
}) async {
final controller = TextEditingController(
text: initialValue > 0 ? initialValue.toStringAsFixed(2) : '',
);
return showDialog<double>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Enter paid amount (USD)'),
content: TextField(
controller: controller,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
decoration: const InputDecoration(labelText: 'USD amount'),
autofocus: true,
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel'),
),
FilledButton(
onPressed: () {
final value = double.tryParse(controller.text.trim());
if (value == null || value <= 0) {
return;
}
Navigator.of(context).pop(value);
},
child: const Text('Save'),
),
],
);
},
);
}
Future<void> _handleStatusTap(
BuildContext context,
RentController controller,
RentTableRow row,
) async {
if (row.status == PaymentStatus.notOccupied) {
if (!context.mounted) {
return;
}
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'Use Settings -> Unit occupied to manage not-occupied months.',
),
),
);
return;
}
final nextStatus = await _showStatusPicker(context, row.status);
if (!context.mounted) {
return;
}
if (nextStatus == null || nextStatus == row.status) {
return;
}
double? paidUsd;
if (nextStatus == PaymentStatus.onTime ||
nextStatus == PaymentStatus.late) {
paidUsd = await _showPaidUsdDialog(
context,
initialValue: row.usdAmount ?? controller.settings.rentUsd,
);
if (paidUsd == null) {
return;
}
}
await controller.setMonthStatusManually(
year: row.year,
month: row.month,
status: nextStatus,
paidUsd: paidUsd,
);
if (!context.mounted) {
return;
}
if (controller.errorMessage != null) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(controller.errorMessage!)));
}
}
@override
Widget build(BuildContext context) {
return Consumer<RentController>(
@@ -95,8 +229,17 @@ class HomeScreen extends StatelessWidget {
_statusIcon(row.status),
const SizedBox(width: 8),
Text(_statusLabel(row.status)),
const SizedBox(width: 8),
const Icon(Icons.edit, size: 16),
],
),
onTap: () async {
await _handleStatusTap(
context,
controller,
row,
);
},
),
DataCell(
Text(