@marcos_feitoza/personal-finance-frontend-feature-investments 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.1.1](https://github.com/MarcosOps/personal-finance-frontend-feature-investments/compare/v1.1.0...v1.1.1) (2025-12-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add AppDialogs.showDeleteConfirmationDialog ([29c41f8](https://github.com/MarcosOps/personal-finance-frontend-feature-investments/commit/29c41f80530906111efe5fac1c3c493c78a29a62))
|
|
7
|
+
|
|
1
8
|
# [1.1.0](https://github.com/MarcosOps/personal-finance-frontend-feature-investments/compare/v1.0.1...v1.1.0) (2025-11-28)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -6,6 +6,7 @@ import 'package:personal_finance_frontend_core_ui/widgets/crypto_trade_form.dart
|
|
|
6
6
|
import 'package:personal_finance_frontend_core_ui/widgets/app_widgets.dart';
|
|
7
7
|
import 'package:provider/provider.dart';
|
|
8
8
|
import 'package:personal_finance_frontend_core_services/providers/auth_provider.dart';
|
|
9
|
+
import 'package:personal_finance_frontend_core_ui/utils/app_dialogs.dart';
|
|
9
10
|
|
|
10
11
|
class CryptoAccountScreen extends StatefulWidget {
|
|
11
12
|
final String accountName;
|
|
@@ -333,25 +334,7 @@ class _CryptoAccountScreenState extends State<CryptoAccountScreen> {
|
|
|
333
334
|
}
|
|
334
335
|
|
|
335
336
|
Future<void> _confirmAndDeleteTrade(int tradeId) async {
|
|
336
|
-
final bool? confirm = await
|
|
337
|
-
context: context,
|
|
338
|
-
builder: (BuildContext context) {
|
|
339
|
-
return AlertDialog(
|
|
340
|
-
title: const Text('Confirm Deletion'),
|
|
341
|
-
content: const Text('Are you sure you want to delete this trade? This action cannot be undone.'),
|
|
342
|
-
actions: <Widget>[
|
|
343
|
-
TextButton(
|
|
344
|
-
onPressed: () => Navigator.of(context).pop(false),
|
|
345
|
-
child: const Text('Cancel'),
|
|
346
|
-
),
|
|
347
|
-
TextButton(
|
|
348
|
-
onPressed: () => Navigator.of(context).pop(true),
|
|
349
|
-
child: const Text('Delete'),
|
|
350
|
-
),
|
|
351
|
-
],
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
);
|
|
337
|
+
final bool? confirm = await AppDialogs.showDeleteConfirmationDialog(context, 'this trade');
|
|
355
338
|
|
|
356
339
|
if (confirm == true) {
|
|
357
340
|
if (_token == null) {
|
|
@@ -379,4 +362,4 @@ class _CryptoAccountScreenState extends State<CryptoAccountScreen> {
|
|
|
379
362
|
}
|
|
380
363
|
}
|
|
381
364
|
}
|
|
382
|
-
}
|
|
365
|
+
}
|
|
@@ -76,9 +76,12 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
76
76
|
setState(() => _isLoading = true);
|
|
77
77
|
try {
|
|
78
78
|
final futures = <Future>[
|
|
79
|
-
_transactionService.getTrades(
|
|
80
|
-
|
|
81
|
-
_transactionService.
|
|
79
|
+
_transactionService.getTrades(
|
|
80
|
+
investmentAccount: widget.accountName, token: _token),
|
|
81
|
+
_transactionService.getAccountBalance(widget.accountName,
|
|
82
|
+
token: _token),
|
|
83
|
+
_transactionService.getAssets(
|
|
84
|
+
investmentAccount: widget.accountName, token: _token),
|
|
82
85
|
_transactionService.getTotalPortfolioBookCost(token: _token),
|
|
83
86
|
];
|
|
84
87
|
if (widget.showDividends) {
|
|
@@ -233,10 +236,12 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
233
236
|
data['portfolio_allocation_percent'] = (_totalPortfolioBookCost > 0)
|
|
234
237
|
? (bookCost / _totalPortfolioBookCost) * 100
|
|
235
238
|
: 0.0;
|
|
236
|
-
|
|
239
|
+
|
|
237
240
|
// Calculate Market Value, Unrealized P/L, Total Return
|
|
238
241
|
final livePrice = _livePrices[symbol];
|
|
239
|
-
double marketValue = livePrice != null
|
|
242
|
+
double marketValue = livePrice != null
|
|
243
|
+
? shares * livePrice
|
|
244
|
+
: bookCost; // Fallback to bookCost if no live price
|
|
240
245
|
double unrealizedPL = marketValue - bookCost;
|
|
241
246
|
double totalReturn = unrealizedPL + totalDividends;
|
|
242
247
|
|
|
@@ -244,8 +249,10 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
244
249
|
data['unrealized_pl'] = unrealizedPL;
|
|
245
250
|
data['total_return'] = totalReturn;
|
|
246
251
|
|
|
247
|
-
data['percent_unrealized_pl'] =
|
|
248
|
-
|
|
252
|
+
data['percent_unrealized_pl'] =
|
|
253
|
+
(bookCost > 0) ? (unrealizedPL / bookCost) * 100 : 0.0;
|
|
254
|
+
data['percent_total_return'] =
|
|
255
|
+
(bookCost > 0) ? (totalReturn / bookCost) * 100 : 0.0;
|
|
249
256
|
|
|
250
257
|
result.add(data);
|
|
251
258
|
});
|
|
@@ -312,12 +319,12 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
312
319
|
children: [
|
|
313
320
|
Expanded(
|
|
314
321
|
child: TradeForm(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
322
|
+
accountName: widget.accountName,
|
|
323
|
+
portfolioSummary: _portfolioSummary,
|
|
324
|
+
assets: _assets,
|
|
325
|
+
onTradeCreated: (_) => _fetchData(),
|
|
326
|
+
token: _token,
|
|
327
|
+
)),
|
|
321
328
|
if (widget.showDividends) ...[
|
|
322
329
|
const SizedBox(width: 16),
|
|
323
330
|
Expanded(
|
|
@@ -401,16 +408,17 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
401
408
|
final livePrice = _livePrices[symbol];
|
|
402
409
|
final marketValue = position['market_value'] as double;
|
|
403
410
|
final unrealizedPL = position['unrealized_pl'] as double;
|
|
404
|
-
final percentUnrealizedPL =
|
|
411
|
+
final percentUnrealizedPL =
|
|
412
|
+
position['percent_unrealized_pl'] as double;
|
|
405
413
|
final totalDividends = position['total_dividends'] as double;
|
|
406
414
|
final totalReturn = position['total_return'] as double;
|
|
407
415
|
final percentTotalReturn = position['percent_total_return'] as double;
|
|
408
|
-
final accountAllocationPercent =
|
|
409
|
-
|
|
410
|
-
|
|
416
|
+
final accountAllocationPercent =
|
|
417
|
+
position['account_allocation_percent'] as double;
|
|
418
|
+
final portfolioAllocationPercent =
|
|
419
|
+
position['portfolio_allocation_percent'] as double;
|
|
411
420
|
|
|
412
|
-
final plColor =
|
|
413
|
-
(unrealizedPL ?? 0) >= 0 ? Colors.green : Colors.red;
|
|
421
|
+
final plColor = (unrealizedPL ?? 0) >= 0 ? Colors.green : Colors.red;
|
|
414
422
|
final totalReturnColor =
|
|
415
423
|
(totalReturn ?? 0) >= 0 ? Colors.green : Colors.red;
|
|
416
424
|
|
|
@@ -423,12 +431,16 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
423
431
|
? Text(_formatCurrency(livePrice))
|
|
424
432
|
: const Text('N/A')),
|
|
425
433
|
DataCell(Text(_formatCurrency(marketValue))),
|
|
426
|
-
DataCell(Text(_formatCurrency(unrealizedPL),
|
|
427
|
-
|
|
434
|
+
DataCell(Text(_formatCurrency(unrealizedPL),
|
|
435
|
+
style: TextStyle(color: plColor))),
|
|
436
|
+
DataCell(Text('${percentUnrealizedPL.toStringAsFixed(2)}%',
|
|
437
|
+
style: TextStyle(color: plColor))),
|
|
428
438
|
if (widget.showDividends) ...[
|
|
429
439
|
DataCell(Text(_formatCurrency(totalDividends))),
|
|
430
|
-
DataCell(Text(_formatCurrency(totalReturn),
|
|
431
|
-
|
|
440
|
+
DataCell(Text(_formatCurrency(totalReturn),
|
|
441
|
+
style: TextStyle(color: totalReturnColor))),
|
|
442
|
+
DataCell(Text('${percentTotalReturn.toStringAsFixed(2)}%',
|
|
443
|
+
style: TextStyle(color: totalReturnColor))),
|
|
432
444
|
],
|
|
433
445
|
DataCell(Text('${accountAllocationPercent.toStringAsFixed(2)}%')),
|
|
434
446
|
DataCell(Text('${portfolioAllocationPercent.toStringAsFixed(2)}%')),
|
|
@@ -543,9 +555,10 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
543
555
|
}).toList(),
|
|
544
556
|
);
|
|
545
557
|
}
|
|
546
|
-
|
|
558
|
+
|
|
547
559
|
Future<void> _confirmAndDeleteTrade(int tradeId) async {
|
|
548
|
-
final bool? confirm =
|
|
560
|
+
final bool? confirm =
|
|
561
|
+
await AppDialogs.showDeleteConfirmationDialog(context, 'this trade');
|
|
549
562
|
|
|
550
563
|
if (confirm == true) {
|
|
551
564
|
if (_token == null) {
|
|
@@ -555,7 +568,8 @@ class _InvestmentAccountScreenState extends State<InvestmentAccountScreen> {
|
|
|
555
568
|
return;
|
|
556
569
|
}
|
|
557
570
|
try {
|
|
558
|
-
final success =
|
|
571
|
+
final success =
|
|
572
|
+
await _transactionService.deleteTrade(tradeId, token: _token);
|
|
559
573
|
if (success) {
|
|
560
574
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
561
575
|
const SnackBar(content: Text('Trade deleted successfully.')),
|
|
@@ -4,6 +4,7 @@ import 'package:personal_finance_frontend_core_services/services/transaction_ser
|
|
|
4
4
|
import 'package:personal_finance_frontend_core_ui/widgets/rrsp_contribution_form.dart';
|
|
5
5
|
import 'package:provider/provider.dart';
|
|
6
6
|
import 'package:personal_finance_frontend_core_services/providers/auth_provider.dart';
|
|
7
|
+
import 'package:personal_finance_frontend_core_ui/utils/app_dialogs.dart'; // Added import for AppDialogs
|
|
7
8
|
|
|
8
9
|
class RrspSunLifeScreen extends StatefulWidget {
|
|
9
10
|
const RrspSunLifeScreen({Key? key}) : super(key: key);
|
|
@@ -150,29 +151,32 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
|
|
|
150
151
|
),
|
|
151
152
|
body: _isLoading
|
|
152
153
|
? const Center(child: CircularProgressIndicator())
|
|
153
|
-
:
|
|
154
|
-
|
|
155
|
-
child:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
154
|
+
: RefreshIndicator(
|
|
155
|
+
onRefresh: _fetchData,
|
|
156
|
+
child: SingleChildScrollView(
|
|
157
|
+
padding: const EdgeInsets.all(16.0),
|
|
158
|
+
child: Column(
|
|
159
|
+
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
160
|
+
children: [
|
|
161
|
+
Text('Summary',
|
|
162
|
+
style: Theme.of(context).textTheme.titleLarge),
|
|
163
|
+
const SizedBox(height: 8),
|
|
164
|
+
_buildSinteticoTable(),
|
|
165
|
+
const SizedBox(height: 24),
|
|
166
|
+
RrspContributionForm(
|
|
167
|
+
investmentAccount: 'RRSP Sun Life',
|
|
168
|
+
onContributionLogged: () => _fetchData(),
|
|
169
|
+
token: _token,
|
|
170
|
+
),
|
|
171
|
+
const SizedBox(height: 24),
|
|
172
|
+
Text(
|
|
173
|
+
'Contribution History',
|
|
174
|
+
style: Theme.of(context).textTheme.titleLarge,
|
|
175
|
+
),
|
|
176
|
+
const SizedBox(height: 8),
|
|
177
|
+
_buildContributionTable(),
|
|
178
|
+
],
|
|
179
|
+
),
|
|
176
180
|
),
|
|
177
181
|
),
|
|
178
182
|
);
|
|
@@ -290,24 +294,9 @@ class _RrspSunLifeScreenState extends State<RrspSunLifeScreen> {
|
|
|
290
294
|
}
|
|
291
295
|
|
|
292
296
|
Future<void> _confirmAndDeleteContribution(int contributionId) async {
|
|
293
|
-
final bool? confirm = await
|
|
294
|
-
context
|
|
295
|
-
|
|
296
|
-
return AlertDialog(
|
|
297
|
-
title: const Text('Confirm Deletion'),
|
|
298
|
-
content: const Text('Are you sure you want to delete this contribution? This action cannot be undone.'),
|
|
299
|
-
actions: <Widget>[
|
|
300
|
-
TextButton(
|
|
301
|
-
onPressed: () => Navigator.of(context).pop(false),
|
|
302
|
-
child: const Text('Cancel'),
|
|
303
|
-
),
|
|
304
|
-
TextButton(
|
|
305
|
-
onPressed: () => Navigator.of(context).pop(true),
|
|
306
|
-
child: const Text('Delete'),
|
|
307
|
-
),
|
|
308
|
-
],
|
|
309
|
-
);
|
|
310
|
-
},
|
|
297
|
+
final bool? confirm = await AppDialogs.showDeleteConfirmationDialog(
|
|
298
|
+
context,
|
|
299
|
+
'this contribution', // A generic string for the item being deleted
|
|
311
300
|
);
|
|
312
301
|
|
|
313
302
|
if (confirm == true) {
|