Fix the template page
This commit is contained in:
		
							parent
							
								
									60bfd9481f
								
							
						
					
					
						commit
						058291fa80
					
				@ -145,22 +145,25 @@ Stream<void> watchTransactionTemplates(Account account) {
 | 
			
		||||
      .transactionTemplates
 | 
			
		||||
      .filter()
 | 
			
		||||
      .account((q) => q.idEqualTo(account.id))
 | 
			
		||||
      .recurringEqualTo(false)
 | 
			
		||||
      .watchLazy(fireImmediately: true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Future<List<TransactionTemplate>> getTransactionTemplates(Account? account) {
 | 
			
		||||
Future<List<TransactionTemplate>> getTransactionTemplates(
 | 
			
		||||
  Account? account,
 | 
			
		||||
) async {
 | 
			
		||||
  if (account == null) {
 | 
			
		||||
    return Future.value([]);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return GetIt.I
 | 
			
		||||
      .get<Isar>()
 | 
			
		||||
      .transactionTemplates
 | 
			
		||||
      .filter()
 | 
			
		||||
      .account((q) => q.idEqualTo(account.id))
 | 
			
		||||
      .recurringEqualTo(false)
 | 
			
		||||
      .findAll();
 | 
			
		||||
  final a =
 | 
			
		||||
      await GetIt.I
 | 
			
		||||
          .get<Isar>()
 | 
			
		||||
          .transactionTemplates
 | 
			
		||||
          .filter()
 | 
			
		||||
          .account((q) => q.idEqualTo(account.id))
 | 
			
		||||
          .findAll();
 | 
			
		||||
 | 
			
		||||
  return a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Stream<void> watchTransactions(Account account) {
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_bloc/flutter_bloc.dart';
 | 
			
		||||
import 'package:get_it/get_it.dart';
 | 
			
		||||
import 'package:grouped_list/grouped_list.dart';
 | 
			
		||||
import 'package:okane/ui/state/core.dart';
 | 
			
		||||
import 'package:okane/ui/utils.dart';
 | 
			
		||||
import 'package:okane/ui/widgets/add_template.dart';
 | 
			
		||||
@ -18,29 +19,45 @@ class TemplateListState extends State<TemplateListPage> {
 | 
			
		||||
    return BlocBuilder<CoreCubit, CoreState>(
 | 
			
		||||
      builder: (context, state) {
 | 
			
		||||
        final account = GetIt.I.get<CoreCubit>().activeAccount;
 | 
			
		||||
        final nonRecurringTemplates =
 | 
			
		||||
            state.transactionTemplates.where((t) => !t.recurring).toList();
 | 
			
		||||
        return Stack(
 | 
			
		||||
          children: [
 | 
			
		||||
            Column(
 | 
			
		||||
              children: [
 | 
			
		||||
                Padding(
 | 
			
		||||
                  padding: EdgeInsets.only(top: 16),
 | 
			
		||||
                  child: ListView.builder(
 | 
			
		||||
                    itemCount: state.recurringTransactions.length,
 | 
			
		||||
                    shrinkWrap: true,
 | 
			
		||||
                    itemBuilder:
 | 
			
		||||
                        (ctx, idx) => ListTile(
 | 
			
		||||
                            title: Text(
 | 
			
		||||
                              state
 | 
			
		||||
                                  .recurringTransactions[idx]
 | 
			
		||||
                                  .template
 | 
			
		||||
                                  .value!
 | 
			
		||||
                                  .name,
 | 
			
		||||
                            ),
 | 
			
		||||
                          ),
 | 
			
		||||
                  ),
 | 
			
		||||
            CustomScrollView(
 | 
			
		||||
              slivers: [
 | 
			
		||||
                SliverToBoxAdapter(child: Text("Non-recurring")),
 | 
			
		||||
                SliverList.builder(
 | 
			
		||||
                  itemCount: nonRecurringTemplates.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final template = nonRecurringTemplates[index];
 | 
			
		||||
                    return ListTile(title: Text(template.name));
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
                SliverToBoxAdapter(child: Text("Recurring")),
 | 
			
		||||
                SliverList.builder(
 | 
			
		||||
                  itemCount: state.recurringTransactions.length,
 | 
			
		||||
                  itemBuilder: (context, index) {
 | 
			
		||||
                    final template = state.recurringTransactions[index];
 | 
			
		||||
                    return ListTile(title: Text(template.template.value!.name));
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ],
 | 
			
		||||
            ),
 | 
			
		||||
            /*Padding(
 | 
			
		||||
              padding: EdgeInsets.only(top: 16),
 | 
			
		||||
              child: ListView.builder(
 | 
			
		||||
                itemCount: state.recurringTransactions.length,
 | 
			
		||||
                shrinkWrap: true,
 | 
			
		||||
                itemBuilder: (ctx, idx) {
 | 
			
		||||
                  print(idx);
 | 
			
		||||
                  return ListTile(
 | 
			
		||||
                    title: Text(
 | 
			
		||||
                      state.recurringTransactions[idx].template.value!.name,
 | 
			
		||||
                    ),
 | 
			
		||||
                  );
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
            ),*/
 | 
			
		||||
            Positioned(
 | 
			
		||||
              right: 16,
 | 
			
		||||
              bottom: 16,
 | 
			
		||||
 | 
			
		||||
@ -181,14 +181,16 @@ class TransactionDetailsPage extends StatelessWidget {
 | 
			
		||||
 | 
			
		||||
                      Padding(
 | 
			
		||||
                        padding: EdgeInsets.symmetric(vertical: 8),
 | 
			
		||||
                      child: Row(
 | 
			
		||||
                        children: [
 | 
			
		||||
                          state.activeTransaction!.amount > 0
 | 
			
		||||
                              ? Icon(Icons.add)
 | 
			
		||||
                              : Icon(Icons.remove),
 | 
			
		||||
                          Text(formatCurrency(state.activeTransaction!.amount)),
 | 
			
		||||
                        ],
 | 
			
		||||
                      ),
 | 
			
		||||
                        child: Row(
 | 
			
		||||
                          children: [
 | 
			
		||||
                            state.activeTransaction!.amount > 0
 | 
			
		||||
                                ? Icon(Icons.add)
 | 
			
		||||
                                : Icon(Icons.remove),
 | 
			
		||||
                            Text(
 | 
			
		||||
                              formatCurrency(state.activeTransaction!.amount),
 | 
			
		||||
                            ),
 | 
			
		||||
                          ],
 | 
			
		||||
                        ),
 | 
			
		||||
                      ),
 | 
			
		||||
                    ],
 | 
			
		||||
                  ),
 | 
			
		||||
 | 
			
		||||
@ -76,6 +76,7 @@ class CoreCubit extends Cubit<CoreState> {
 | 
			
		||||
    _transactionTemplatesStreamSubcription = watchTransactionTemplates(
 | 
			
		||||
      activeAccount!,
 | 
			
		||||
    ).listen((_) async {
 | 
			
		||||
      print("UPDATE");
 | 
			
		||||
      emit(
 | 
			
		||||
        state.copyWith(
 | 
			
		||||
          transactionTemplates: await getTransactionTemplates(activeAccount!),
 | 
			
		||||
 | 
			
		||||
@ -49,34 +49,32 @@ 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]);
 | 
			
		||||
                },
 | 
			
		||||
              ),
 | 
			
		||||
    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]);
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
          );
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -214,7 +214,7 @@ class _AddRecurringTransactionTemplateWidgetState
 | 
			
		||||
                decimal: false,
 | 
			
		||||
              ),
 | 
			
		||||
              decoration: InputDecoration(
 | 
			
		||||
                  hintText: "Amount",
 | 
			
		||||
                hintText: "Amount",
 | 
			
		||||
                icon: Icon(Icons.euro),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ import 'package:get_it/get_it.dart';
 | 
			
		||||
import 'package:okane/database/collections/account.dart';
 | 
			
		||||
import 'package:okane/database/collections/beneficiary.dart';
 | 
			
		||||
import 'package:okane/database/collections/expense_category.dart';
 | 
			
		||||
import 'package:okane/database/collections/recurrent.dart';
 | 
			
		||||
import 'package:okane/database/collections/template.dart';
 | 
			
		||||
import 'package:okane/database/database.dart';
 | 
			
		||||
import 'package:okane/ui/state/core.dart';
 | 
			
		||||
@ -12,6 +13,8 @@ import 'package:okane/ui/utils.dart';
 | 
			
		||||
import 'package:okane/ui/widgets/add_expense_category.dart';
 | 
			
		||||
import 'package:searchfield/searchfield.dart';
 | 
			
		||||
 | 
			
		||||
enum Period { days, weeks, months, years }
 | 
			
		||||
 | 
			
		||||
class AddTransactionTemplateWidget extends StatefulWidget {
 | 
			
		||||
  final VoidCallback onAdd;
 | 
			
		||||
 | 
			
		||||
@ -41,6 +44,11 @@ class _AddTransactionTemplateWidgetState
 | 
			
		||||
 | 
			
		||||
  ExpenseCategory? _expenseCategory;
 | 
			
		||||
 | 
			
		||||
  bool _isRecurring = false;
 | 
			
		||||
 | 
			
		||||
  Period _selectedPeriod = Period.weeks;
 | 
			
		||||
  int _periodSize = 1;
 | 
			
		||||
 | 
			
		||||
  String getBeneficiaryName(Beneficiary item) {
 | 
			
		||||
    return switch (item.type) {
 | 
			
		||||
      BeneficiaryType.account => "${item.name} (Account)",
 | 
			
		||||
@ -51,6 +59,7 @@ class _AddTransactionTemplateWidgetState
 | 
			
		||||
  Future<void> _submit(BuildContext context) async {
 | 
			
		||||
    final beneficiaryName = _beneficiaryTextController.text;
 | 
			
		||||
    if (_selectedBeneficiary == null && beneficiaryName.isEmpty) {
 | 
			
		||||
      print("No beneficiary");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    if (_templateNameController.text.isEmpty) {
 | 
			
		||||
@ -103,15 +112,31 @@ class _AddTransactionTemplateWidgetState
 | 
			
		||||
      TransactionDirection.receive => 1,
 | 
			
		||||
    };
 | 
			
		||||
    final amount = factor * double.parse(_amountTextController.text).abs();
 | 
			
		||||
    final transaction =
 | 
			
		||||
    final template =
 | 
			
		||||
        TransactionTemplate()
 | 
			
		||||
          ..name = _templateNameController.text
 | 
			
		||||
          ..account.value = widget.activeAccountItem
 | 
			
		||||
          ..beneficiary.value = beneficiary
 | 
			
		||||
          ..expenseCategory.value = _expenseCategory
 | 
			
		||||
          ..recurring = false
 | 
			
		||||
          ..recurring = _isRecurring
 | 
			
		||||
          ..amount = amount;
 | 
			
		||||
    await upsertTransactionTemplate(transaction);
 | 
			
		||||
    await upsertTransactionTemplate(template);
 | 
			
		||||
 | 
			
		||||
    if (_isRecurring) {
 | 
			
		||||
      final days = switch (_selectedPeriod) {
 | 
			
		||||
        Period.days => _periodSize,
 | 
			
		||||
        Period.weeks => _periodSize * 7,
 | 
			
		||||
        Period.months => _periodSize * 31,
 | 
			
		||||
        Period.years => _periodSize * 365,
 | 
			
		||||
      };
 | 
			
		||||
      final recurringTransaction =
 | 
			
		||||
          RecurringTransaction()
 | 
			
		||||
            ..account.value = widget.activeAccountItem
 | 
			
		||||
            ..template.value = template
 | 
			
		||||
            ..lastExecution = null
 | 
			
		||||
            ..days = days;
 | 
			
		||||
      await upsertRecurringTransaction(recurringTransaction);
 | 
			
		||||
    }
 | 
			
		||||
    widget.onAdd();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -197,8 +222,8 @@ class _AddTransactionTemplateWidgetState
 | 
			
		||||
                decimal: false,
 | 
			
		||||
              ),
 | 
			
		||||
              decoration: InputDecoration(
 | 
			
		||||
                  hintText: "Amount",
 | 
			
		||||
                  icon: Icon(Icons.euro),
 | 
			
		||||
                hintText: "Amount",
 | 
			
		||||
                icon: Icon(Icons.euro),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
@ -209,20 +234,105 @@ class _AddTransactionTemplateWidgetState
 | 
			
		||||
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: EdgeInsets.only(left: 16),
 | 
			
		||||
              child: OutlinedButton(
 | 
			
		||||
                onPressed: () async {
 | 
			
		||||
                  final category = await showDialogOrModal(
 | 
			
		||||
                    context: context,
 | 
			
		||||
                    builder: (_) => AddExpenseCategory(),
 | 
			
		||||
                  );
 | 
			
		||||
                  if (category == null) {
 | 
			
		||||
                    return;
 | 
			
		||||
                  }
 | 
			
		||||
                child: OutlinedButton(
 | 
			
		||||
                  onPressed: () async {
 | 
			
		||||
                    final category = await showDialogOrModal(
 | 
			
		||||
                      context: context,
 | 
			
		||||
                      builder: (_) => AddExpenseCategory(),
 | 
			
		||||
                    );
 | 
			
		||||
                    if (category == null) {
 | 
			
		||||
                      return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                  setState(() => _expenseCategory = category);
 | 
			
		||||
                },
 | 
			
		||||
                child: Text(_expenseCategory?.name ?? "None"),
 | 
			
		||||
                    setState(() => _expenseCategory = category);
 | 
			
		||||
                  },
 | 
			
		||||
                  child: Text(_expenseCategory?.name ?? "None"),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              Text("Is recurring"),
 | 
			
		||||
              Padding(
 | 
			
		||||
                padding: EdgeInsets.only(left: 16),
 | 
			
		||||
                child: Switch(
 | 
			
		||||
                  value: _isRecurring,
 | 
			
		||||
                  onChanged: (value) {
 | 
			
		||||
                    setState(() {
 | 
			
		||||
                      _isRecurring = value;
 | 
			
		||||
                    });
 | 
			
		||||
                  },
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
          Padding(
 | 
			
		||||
            padding: EdgeInsets.only(left: 16, right: 16, top: 16),
 | 
			
		||||
            child: SegmentedButton<Period>(
 | 
			
		||||
              segments: [
 | 
			
		||||
                ButtonSegment(value: Period.days, label: Text("Days")),
 | 
			
		||||
                ButtonSegment(value: Period.weeks, label: Text("Weeks")),
 | 
			
		||||
                ButtonSegment(value: Period.months, label: Text("Months")),
 | 
			
		||||
                ButtonSegment(value: Period.years, label: Text("Years")),
 | 
			
		||||
              ],
 | 
			
		||||
              selected: <Period>{_selectedPeriod},
 | 
			
		||||
              multiSelectionEnabled: false,
 | 
			
		||||
              onSelectionChanged:
 | 
			
		||||
                  _isRecurring
 | 
			
		||||
                      ? (selection) {
 | 
			
		||||
                        setState(() => _selectedPeriod = selection.first);
 | 
			
		||||
                      }
 | 
			
		||||
                      : null,
 | 
			
		||||
            ),
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
          Row(
 | 
			
		||||
            children: [
 | 
			
		||||
              IconButton(
 | 
			
		||||
                icon: Icon(Icons.remove),
 | 
			
		||||
                onPressed:
 | 
			
		||||
                    _isRecurring
 | 
			
		||||
                        ? () {
 | 
			
		||||
                          if (_periodSize <= 1) {
 | 
			
		||||
                            return;
 | 
			
		||||
                          }
 | 
			
		||||
 | 
			
		||||
                          setState(() {
 | 
			
		||||
                            _periodSize--;
 | 
			
		||||
                          });
 | 
			
		||||
                        }
 | 
			
		||||
                        : null,
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
              SizedBox(
 | 
			
		||||
                width: 100,
 | 
			
		||||
                child: Center(
 | 
			
		||||
                  child: Text(
 | 
			
		||||
                    switch (_selectedPeriod) {
 | 
			
		||||
                      Period.days => "$_periodSize days",
 | 
			
		||||
                      Period.weeks => "$_periodSize weeks",
 | 
			
		||||
                      Period.months => "$_periodSize months",
 | 
			
		||||
                      Period.years => "$_periodSize years",
 | 
			
		||||
                    },
 | 
			
		||||
                    style: TextStyle(
 | 
			
		||||
                      color: _isRecurring ? Colors.black : Colors.grey,
 | 
			
		||||
                    ),
 | 
			
		||||
                  ),
 | 
			
		||||
                ),
 | 
			
		||||
              ),
 | 
			
		||||
 | 
			
		||||
              IconButton(
 | 
			
		||||
                icon: Icon(Icons.add),
 | 
			
		||||
                onPressed:
 | 
			
		||||
                    _isRecurring
 | 
			
		||||
                        ? () {
 | 
			
		||||
                          setState(() {
 | 
			
		||||
                            _periodSize++;
 | 
			
		||||
                          });
 | 
			
		||||
                        }
 | 
			
		||||
                        : null,
 | 
			
		||||
              ),
 | 
			
		||||
            ],
 | 
			
		||||
          ),
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user