1
+ @page " /portfolios/{portfolioId:int}"
2
+ @using Model
3
+ @using Services
4
+ @using Utils
5
+ @using CryptoStatsSource
6
+ @using CryptoStatsSource .model
7
+ @using System .Diagnostics
8
+ @inject Microsoft .AspNetCore .Components .NavigationManager NavigationManager
9
+ @inject IPortfolioService PortfolioService
10
+ @inject IPortfolioEntryService PortfolioEntryService
11
+ @inject IMarketOrderService MarketOrderService ;
12
+ @inject ICryptoStatsSource CryptoStatsSource ;
13
+ @inject ISummaryService SummaryService ;
14
+ @inject ICryptoNameResolver CryptoNameResolver ;
15
+
16
+ <style >
17
+ .demo-mat-card {
18
+ margin-bottom : 2em ;
19
+ }
20
+
21
+ .demo-mat-card-content {
22
+ padding : 1rem ;
23
+ }
24
+
25
+ .clear-margin {
26
+ margin : 0px ;
27
+ }
28
+
29
+ .app-fab--absolute {
30
+ position : fixed ;
31
+ bottom : 1rem ;
32
+ right : 1rem ;
33
+ }
34
+
35
+ </style >
36
+ <div class =" mat-layout-grid mat-layout-grid-align-center" >
37
+ <div class =" mat-layout-grid-inner center" >
38
+
39
+ <div class =" mat-layout-grid-cell mat-layout-grid-cell-span-3" ></div >
40
+ <div class =" mat-layout-grid-cell mat-layout-grid-cell-span-6" >
41
+ @if (activePortfolio != null )
42
+ {
43
+ <MatCard class =" demo-mat-card" >
44
+ <MatCardContent >
45
+ <div class =" demo-mat-card-content" >
46
+ <MatHeadline6 class =" clear-margin" >
47
+ <MatChipSet Style =" align-items: center" >
48
+ <MatH5 Class =" clear-margin" >@activePortfolio.Name </MatH5 >
49
+ <MatChip Style =" vertical-align: center" Label =" @CurrencyUtils.GetCurrencyLabel(activePortfolio.Currency)" />
50
+ </MatChipSet >
51
+ </MatHeadline6 >
52
+ </div >
53
+
54
+ <MatBody2 class =" demo-mat-card-content clear-margin" >
55
+ <div class =" mat-layout-grid" >
56
+ <div class =" mat-layout-grid-inner" style =" align-items : center " >
57
+ <div class =" mat-layout-grid-cell mat-layout-grid-cell-span-6" >
58
+ <MatH4 Class =" clear-margin" >@( CurrencyUtils .Format (portfolioSummary .MarketValue , activePortfolio .Currency )) </MatH4 >
59
+ </div >
60
+ <div class =" mat-layout-grid-cell mat-layout-grid-cell-span-6" style =" text-align : end " >
61
+ @( portfolioSummary .RelativeChange * 100 m ) %
62
+ </div >
63
+ </div >
64
+ </div >
65
+ </MatBody2 >
66
+ </MatCardContent >
67
+ </MatCard >
68
+ <MatTable Items =" @portfolioEntryRows" Striped =" true" AllowSelection =" true" RowClass =" tester" class =" mat-elevation-z5" ShowPaging =" false" PageSize =" 9999" SelectionChanged =" SelectionChangedEvent" >
69
+ <MatTableHeader >
70
+ <th >Coin </th >
71
+ <th >Price </th >
72
+ <th >Change (1h )</th >
73
+ <th >Holdings </th >
74
+ </MatTableHeader >
75
+ <MatTableRow >
76
+ <td >@context.symbol.ToUpper() </td >
77
+ <td >@( CurrencyUtils .Format (context .currentPrice , activePortfolio .Currency )) </td >
78
+ <td style =' color : @(context.relativeChange >= 0 ? " #17a104" : " #FF0000" )' >@context.relativeChange % </td >
79
+ <td >@context.percentage % </td >
80
+ </MatTableRow >
81
+ </MatTable >
82
+ }
83
+ else
84
+ {
85
+ <MatProgressBar Indeterminate =" true" ></MatProgressBar >
86
+ }
87
+ </div >
88
+ <div class =" mat-layout-grid-cell mat-layout-grid-cell-span-3" ></div >
89
+ </div >
90
+ </div >
91
+ <MatFAB Class =" app-fab--absolute" Icon =" @MatIconNames.Add" Label =" Add a new entry" OnClick =' () => { NavigationManager.NavigateTo($"newportfolioentry/{activePortfolio.Id}"); }' ></MatFAB >
92
+
93
+
94
+ @code
95
+ {
96
+ [Parameter ]
97
+ public int PortfolioId { get ; set ; }
98
+
99
+ protected Portfolio activePortfolio ;
100
+
101
+ protected ISummaryService .Summary portfolioSummary = new (1341 m , 1 . 8 m , 9982 . 489 m , 1000 m );
102
+
103
+ protected List <PortfolioEntry > activePortfolioEntries = new List <PortfolioEntry >()
104
+ {
105
+ new (" btc" , 1 , 1 ),
106
+ new (" ada" , 1 , 2 ),
107
+ new (" eth" , 1 , 3 ),
108
+ new (" ltc" , 1 , 4 ),
109
+ new (" link" , 1 , 5 ),
110
+ };
111
+
112
+ protected List <decimal > portfolioHoldings = new ()
113
+ {
114
+ 44 . 8886 m ,
115
+ 28 . 18 m ,
116
+ 10 . 116 m ,
117
+ 9 . 38 m ,
118
+ 2 . 70 m ,
119
+ };
120
+
121
+ protected List <PortfolioEntryRow > portfolioEntryRows = new ()
122
+ {
123
+ new (" btc" , 57644 . 42 m , 1 . 35 m , 44 . 76 m ),
124
+ new (" ada" , 1 . 36 m , 0 . 58 m , 28 . 18 m ),
125
+ new (" eth" , 3279 . 64 m , 10 . 95 m , 27 . 11 m ),
126
+ new (" ltc" , 291 . 55 m , 7 . 20 m , 9 . 38 m ),
127
+ new (" link" , 42 . 20 m , - 5 . 19 m , 2 . 70 m )
128
+ };
129
+
130
+ protected record PortfolioEntryRow (string symbol , decimal currentPrice , decimal relativeChange , decimal percentage );
131
+
132
+ protected override void OnInitialized ()
133
+ {
134
+ activePortfolio = PortfolioService .GetPortfolio (PortfolioId );
135
+ activePortfolioEntries = PortfolioEntryService .GetPortfolioEntries (PortfolioId );
136
+ }
137
+
138
+ protected override async Task OnInitializedAsync ()
139
+ {
140
+ var marketEntries = (await CryptoStatsSource .GetMarketEntries (
141
+ CurrencyUtils .GetCurrencyLabel (activePortfolio .Currency ).ToLower (),
142
+ await Task .WhenAll (activePortfolioEntries .Select (async entry => (await CryptoNameResolver .Resolve (entry .Symbol )).ToLower ()))
143
+ )).ToDictionary (entry => entry .Symbol , entry => entry );
144
+
145
+
146
+ List <ISummaryService .Summary > summaries = new List <ISummaryService .Summary >();
147
+ foreach (var portfolioEntry in activePortfolioEntries )
148
+ {
149
+ var marketEntry = marketEntries .GetValueOrDefault (portfolioEntry .Symbol );
150
+
151
+ if (marketEntry == null )
152
+ {
153
+ // TODO market entry is null
154
+ }
155
+ var entryOrders = MarketOrderService .GetPortfolioEntryOrders (portfolioEntry .Id );
156
+ summaries .Add (SummaryService .GetPortfolioEntrySummary (entryOrders , marketEntry .CurrentPrice ));
157
+ }
158
+ portfolioSummary = SummaryService .GetPortfolioSummary (summaries );
159
+
160
+ if (portfolioSummary .Cost == 0 )
161
+ {
162
+ portfolioSummary = portfolioSummary with {
163
+ RelativeChange = 0
164
+ };
165
+ }
166
+
167
+ var totalMarketValue = summaries .Sum (summary => summary .MarketValue );
168
+
169
+
170
+ portfolioEntryRows = summaries .Zip (activePortfolioEntries ).Select ((summary ) => new PortfolioEntryRow (summary .Second .Symbol , marketEntries [summary .Second .Symbol ].CurrentPrice , new decimal (marketEntries [summary .Second .Symbol ].PriceChangePercentage24H ), totalMarketValue > 0 ? (summary .First .MarketValue / totalMarketValue ) * 100 : 0 )).ToList ();
171
+ }
172
+
173
+ public void SelectionChangedEvent (object row )
174
+ {
175
+ if (row == null )
176
+ {
177
+ }
178
+ else
179
+ {
180
+ NavigationManager .NavigateTo ($" entrydetail" );
181
+ }
182
+ }
183
+
184
+ }
0 commit comments