This commit is contained in:
PapaTutuWawa 2025-05-09 20:23:45 +02:00
parent 5a2dbf8962
commit 60bfd9481f
6 changed files with 234 additions and 190 deletions

View File

@ -28,8 +28,7 @@ class TemplateListState extends State<TemplateListPage> {
itemCount: state.recurringTransactions.length,
shrinkWrap: true,
itemBuilder:
(ctx, idx) => Card(
child: ListTile(
(ctx, idx) => ListTile(
title: Text(
state
.recurringTransactions[idx]
@ -40,7 +39,6 @@ class TemplateListState extends State<TemplateListPage> {
),
),
),
),
],
),
Positioned(

View File

@ -77,6 +77,7 @@ class TransactionDetailsPage extends StatelessWidget {
child: ListView(
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StreamBuilder(
stream: watchBeneficiaryObject(

View File

@ -1,10 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:okane/database/collections/template.dart';
import 'package:okane/screen.dart';
import 'package:okane/ui/state/core.dart';
Future<T?> showDialogOrModal<T>({
required BuildContext context,
required WidgetBuilder builder,
bool showDragHandle = true,
bool horizontalPaddingOnMobile = true,
}) {
final screenSize = getScreenSize(context);
final width = MediaQuery.sizeOf(context).shortestSide;
@ -18,6 +22,8 @@ Future<T?> showDialogOrModal<T>({
(context) => Padding(
padding: EdgeInsets.only(
bottom: 32 + MediaQuery.of(context).viewInsets.bottom,
left: horizontalPaddingOnMobile ? 16 : 0,
right: horizontalPaddingOnMobile ? 16 : 0,
),
child: builder(context),
),
@ -26,6 +32,8 @@ Future<T?> showDialogOrModal<T>({
context: context,
builder:
(context) => Dialog(
child: Padding(
padding: EdgeInsets.only(top: 16),
child: Container(
constraints: BoxConstraints(maxWidth: width * 0.7),
child: Padding(
@ -35,9 +43,43 @@ Future<T?> showDialogOrModal<T>({
),
),
),
),
};
}
Future<TransactionTemplate?> selectTransactionTemplate(BuildContext context) {
return showDialogOrModal<TransactionTemplate>(
context: context,
builder: (context) {
return BlocBuilder<CoreCubit, CoreState>(
builder: (context, state) {
if (state.transactionTemplates.isEmpty) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Text("No templates defined"),
);
}
return ListView.builder(
itemCount: state.transactionTemplates.length,
itemBuilder:
(context, index) => ListTile(
title: Text(
state.transactionTemplates[index].name,
),
onTap: () {
Navigator.of(
context,
).pop(state.transactionTemplates[index]);
},
),
);
},
);
},
);
}
DateTime toMidnight(DateTime t) {
return DateTime(t.year, t.month, t.day);
}

View File

@ -151,44 +151,6 @@ class _AddRecurringTransactionTemplateWidgetState
decoration: InputDecoration(label: Text("Template name")),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SearchField<Beneficiary>(
suggestions:
beneficiaries
.where((el) {
final bloc = GetIt.I.get<CoreCubit>();
if (el.type == BeneficiaryType.account) {
return el.account.value?.id != bloc.activeAccount?.id;
}
return true;
})
.map((el) {
return SearchFieldListItem(
getBeneficiaryName(el),
item: el,
);
})
.toList(),
hint: "Beneficiary",
controller: _beneficiaryTextController,
selectedValue: _selectedBeneficiary,
onSuggestionTap: (beneficiary) {
setState(() => _selectedBeneficiary = beneficiary);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _amountTextController,
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: false,
),
decoration: InputDecoration(hintText: "Amount"),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
@ -213,6 +175,51 @@ class _AddRecurringTransactionTemplateWidgetState
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SearchField<Beneficiary>(
suggestions:
beneficiaries
.where((el) {
final bloc = GetIt.I.get<CoreCubit>();
if (el.type == BeneficiaryType.account) {
return el.account.value?.id != bloc.activeAccount?.id;
}
return true;
})
.map((el) {
return SearchFieldListItem(
getBeneficiaryName(el),
item: el,
);
})
.toList(),
hint: switch (_selectedDirection) {
TransactionDirection.send => "Payee",
TransactionDirection.receive => "Payer",
},
controller: _beneficiaryTextController,
selectedValue: _selectedBeneficiary,
onSuggestionTap: (beneficiary) {
setState(() => _selectedBeneficiary = beneficiary);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _amountTextController,
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: false,
),
decoration: InputDecoration(
hintText: "Amount",
icon: Icon(Icons.euro),
),
),
),
Padding(
padding: EdgeInsets.only(left: 16, right: 16, top: 16),
child: SegmentedButton<Period>(

View File

@ -39,7 +39,7 @@ class _AddTransactionTemplateWidgetState
TransactionDirection _selectedDirection = TransactionDirection.send;
ExpenseCategory? _expenseCategory = null;
ExpenseCategory? _expenseCategory;
String getBeneficiaryName(Beneficiary item) {
return switch (item.type) {
@ -129,68 +129,6 @@ class _AddTransactionTemplateWidgetState
decoration: InputDecoration(label: Text("Template name")),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: BlocBuilder<CoreCubit, CoreState>(
builder:
(context, state) => SearchField<Beneficiary>(
suggestions:
state.beneficiaries
.where((el) {
final bloc = GetIt.I.get<CoreCubit>();
if (el.type == BeneficiaryType.account) {
return el.account.value?.id !=
bloc.activeAccount?.id;
}
return true;
})
.map((el) {
return SearchFieldListItem(
getBeneficiaryName(el),
item: el,
);
})
.toList(),
hint: "Beneficiary",
controller: _beneficiaryTextController,
selectedValue: _selectedBeneficiary,
onSuggestionTap: (beneficiary) {
setState(() => _selectedBeneficiary = beneficiary);
},
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _amountTextController,
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: false,
),
decoration: InputDecoration(hintText: "Amount"),
),
),
Row(
children: [
Text("Expense category"),
OutlinedButton(
onPressed: () async {
final category = await showDialogOrModal(
context: context,
builder: (_) => AddExpenseCategory(),
);
if (category == null) {
return;
}
setState(() => _expenseCategory = category);
},
child: Text(_expenseCategory?.name ?? "None"),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
@ -215,6 +153,80 @@ class _AddTransactionTemplateWidgetState
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: BlocBuilder<CoreCubit, CoreState>(
builder:
(context, state) => SearchField<Beneficiary>(
suggestions:
state.beneficiaries
.where((el) {
final bloc = GetIt.I.get<CoreCubit>();
if (el.type == BeneficiaryType.account) {
return el.account.value?.id !=
bloc.activeAccount?.id;
}
return true;
})
.map((el) {
return SearchFieldListItem(
getBeneficiaryName(el),
item: el,
);
})
.toList(),
hint: switch (_selectedDirection) {
TransactionDirection.send => "Payee",
TransactionDirection.receive => "Payer",
},
controller: _beneficiaryTextController,
selectedValue: _selectedBeneficiary,
onSuggestionTap: (beneficiary) {
setState(() => _selectedBeneficiary = beneficiary);
},
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _amountTextController,
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: false,
),
decoration: InputDecoration(
hintText: "Amount",
icon: Icon(Icons.euro),
),
),
),
Row(
children: [
Text("Expense category"),
Padding(
padding: EdgeInsets.only(left: 16),
child: OutlinedButton(
onPressed: () async {
final category = await showDialogOrModal(
context: context,
builder: (_) => AddExpenseCategory(),
);
if (category == null) {
return;
}
setState(() => _expenseCategory = category);
},
child: Text(_expenseCategory?.name ?? "None"),
),
),
],
),
Align(
alignment: Alignment.centerRight,
child: OutlinedButton(

View File

@ -134,33 +134,7 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
children: [
OutlinedButton(
onPressed: () async {
final template = await showDialogOrModal<Transaction>(
context: context,
builder: (context) {
return BlocBuilder<CoreCubit, CoreState>(
builder: (context, state) {
if (state.transactionTemplates.isEmpty) {
return Text("No templates defined");
}
return ListView.builder(
itemCount: state.transactionTemplates.length,
itemBuilder:
(context, index) => ListTile(
title: Text(
state.transactionTemplates[index].name,
),
onTap: () {
Navigator.of(
context,
).pop(state.transactionTemplates[index]);
},
),
);
},
);
},
);
final template = await selectTransactionTemplate(context);
if (template == null) {
return;
}
@ -180,6 +154,30 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
},
child: Text("Use template"),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SegmentedButton<TransactionDirection>(
segments: [
ButtonSegment(
value: TransactionDirection.send,
label: Text("Send"),
icon: Icon(Icons.remove),
),
ButtonSegment(
value: TransactionDirection.receive,
label: Text("Receive"),
icon: Icon(Icons.add),
),
],
selected: <TransactionDirection>{_selectedDirection},
multiSelectionEnabled: false,
onSelectionChanged: (selection) {
setState(() => _selectedDirection = selection.first);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: BlocBuilder<CoreCubit, CoreState>(
@ -215,29 +213,6 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: SegmentedButton<TransactionDirection>(
segments: [
ButtonSegment(
value: TransactionDirection.send,
label: Text("Send"),
icon: Icon(Icons.remove),
),
ButtonSegment(
value: TransactionDirection.receive,
label: Text("Receive"),
icon: Icon(Icons.add),
),
],
selected: <TransactionDirection>{_selectedDirection},
multiSelectionEnabled: false,
onSelectionChanged: (selection) {
setState(() => _selectedDirection = selection.first);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
@ -246,7 +221,10 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
signed: false,
decimal: false,
),
decoration: InputDecoration(hintText: "Amount"),
decoration: InputDecoration(
hintText: "Amount",
icon: Icon(Icons.euro),
),
),
),
@ -256,7 +234,9 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text("Date"),
OutlinedButton(
Padding(
padding: EdgeInsets.only(left: 16),
child: OutlinedButton(
onPressed: () async {
final dt = await showDatePicker(
context: context,
@ -277,6 +257,7 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
],
),
),
),
],
),
),
@ -284,7 +265,9 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
Row(
children: [
Text("Expense category"),
OutlinedButton(
Padding(
padding: EdgeInsets.only(left: 16),
child: OutlinedButton(
onPressed: () async {
final category = await showDialogOrModal(
context: context,
@ -298,6 +281,7 @@ class _AddTransactionWidgetState extends State<AddTransactionWidget> {
},
child: Text(_expenseCategory?.name ?? "None"),
),
),
],
),