|
36 | 36 | { |
37 | 37 | @foreach (var ipo in _activeIpos) |
38 | 38 | { |
39 | | - <div class="card mb-2"> |
| 39 | + <div class="card mb-2" style="cursor:pointer" @onclick="() => NavigateToStatus(ipo.ContractIndex)"> |
40 | 40 | <div class="card-body py-2 px-3"> |
41 | 41 | <div class="d-flex justify-content-between align-items-center"> |
42 | 42 | <div> |
43 | 43 | <span class="fw-bold small">Contract #@ipo.ContractIndex</span> |
44 | 44 | <span class="badge bg-secondary ms-1">@ipo.AssetName</span> |
45 | 45 | </div> |
| 46 | + <span class="text-muted small">→</span> |
46 | 47 | </div> |
47 | 48 | <div class="small text-muted mt-1"> |
48 | 49 | <AddressDisplay Address="@QubicContracts.GetContractIdentity((int)ipo.ContractIndex).ToString()" TruncateChars="10" ShowCopy="false" /> |
|
74 | 75 | } |
75 | 76 | </select> |
76 | 77 | } |
77 | | - <input type="number" class="form-control form-control-sm" min="1" max="23" @bind="_statusContractIndex" |
78 | | - placeholder="Or enter contract index (1-23)" /> |
| 78 | + <input type="number" class="form-control form-control-sm" min="1" max="1023" @bind="_statusContractIndex" |
| 79 | + placeholder="Or enter contract index" /> |
79 | 80 | </div> |
80 | 81 | <button class="btn btn-primary w-100" @onclick="LookupStatus" disabled="@_statusLoading"> |
81 | 82 | @(_statusLoading ? "Looking up..." : "Lookup") |
82 | 83 | </button> |
83 | 84 |
|
84 | 85 | @if (Backend.ActiveBackend != QueryBackend.DirectNetwork) |
85 | 86 | { |
86 | | - <div class="alert alert-warning mt-2 small py-1">This feature requires DirectNetwork backend.</div> |
| 87 | + <div class="alert alert-warning mt-2 small py-1 d-flex align-items-center justify-content-between"> |
| 88 | + <span>Requires DirectNetwork backend.</span> |
| 89 | + <button class="btn btn-sm btn-warning ms-2" @onclick="SwitchToDirectNetwork">Switch</button> |
| 90 | + </div> |
87 | 91 | } |
88 | 92 |
|
89 | 93 | @if (_ipoStatus != null) |
|
104 | 108 | @if (Seed.HasSeed && _mySlots.Count > 0) |
105 | 109 | { |
106 | 110 | <div class="card mt-3 border-primary"> |
107 | | - <div class="card-header small fw-bold">My Bids</div> |
| 111 | + <div class="card-header small fw-bold d-flex justify-content-between align-items-center"> |
| 112 | + <span>My Bids</span> |
| 113 | + @if (_mySlots.Any(s => s.Price == _minBid)) |
| 114 | + { |
| 115 | + <span class="badge bg-warning text-dark">⚠ At Risk</span> |
| 116 | + } |
| 117 | + else |
| 118 | + { |
| 119 | + <span class="badge bg-success">✓ Safe</span> |
| 120 | + } |
| 121 | + </div> |
108 | 122 | <div class="card-body py-2 small"> |
109 | | - <div class="mb-2"> |
110 | | - <strong>@_mySlots.Count</strong> slot@(_mySlots.Count != 1 ? "s" : "") |
111 | | - | Total: <strong class="mono">@_mySlots.Sum(s => s.Price).ToString("N0") QU</strong> |
| 123 | + <div class="mb-2 text-muted"> |
| 124 | + @_mySlots.Count slot@(_mySlots.Count != 1 ? "s" : "") · Total: <strong class="mono">@_mySlots.Sum(s => s.Price).ToString("N0") QU</strong> |
112 | 125 | </div> |
113 | 126 | @foreach (var slot in _mySlots) |
114 | 127 | { |
115 | | - <div class="d-flex justify-content-between border-bottom py-1"> |
116 | | - <span class="mono">Slot #@slot.Index</span> |
117 | | - <span class="mono">@slot.Price.ToString("N0") QU</span> |
| 128 | + var rank = _allSlots.FindIndex(s => s.Index == slot.Index) + 1; |
| 129 | + var atRisk = _filledSlots >= QubicConstants.NumberOfComputors && slot.Price == _minBid; |
| 130 | + <div class="border-bottom py-1"> |
| 131 | + <div class="d-flex justify-content-between align-items-center"> |
| 132 | + <span class="mono">Slot #@slot.Index</span> |
| 133 | + <span> |
| 134 | + @if (atRisk) |
| 135 | + { |
| 136 | + <span class="badge bg-warning text-dark me-1">At Risk</span> |
| 137 | + } |
| 138 | + else |
| 139 | + { |
| 140 | + <span class="badge bg-success me-1">Safe</span> |
| 141 | + } |
| 142 | + <span class="mono">#@rank</span> |
| 143 | + </span> |
| 144 | + </div> |
| 145 | + <div class="d-flex justify-content-between text-muted"> |
| 146 | + <AddressDisplay Address="@slot.Identity" TruncateChars="10" ShowCopy="false" /> |
| 147 | + <span class="mono">@slot.Price.ToString("N0") QU</span> |
| 148 | + </div> |
118 | 149 | </div> |
119 | 150 | } |
120 | 151 | </div> |
|
173 | 204 | } |
174 | 205 | </select> |
175 | 206 | } |
176 | | - <input type="number" class="form-control form-control-sm" min="1" max="23" @bind="_bidContractIndex" |
177 | | - placeholder="Or enter contract index (1-23)" /> |
| 207 | + <input type="number" class="form-control form-control-sm" min="1" max="1023" @bind="_bidContractIndex" |
| 208 | + placeholder="Or enter contract index" /> |
178 | 209 | </div> |
179 | 210 | <div class="mb-2"> |
180 | 211 | <label class="small text-muted">Price per Share (QU)</label> |
|
184 | 215 | <label class="small text-muted">Number of Shares (1-676)</label> |
185 | 216 | <input type="number" class="form-control form-control-sm" min="1" max="676" @bind="_bidQuantity" /> |
186 | 217 | </div> |
187 | | - @if (_bidPrice > 0 && _bidQuantity > 0) |
188 | | - { |
189 | | - <div class="small text-muted mb-2">Total cost: <strong>@((_bidPrice * _bidQuantity).ToString("N0")) QU</strong></div> |
190 | | - } |
| 218 | + <div class="alert alert-info py-2 mb-2 d-flex justify-content-between align-items-center small"> |
| 219 | + <span>Total to pay:</span> |
| 220 | + <strong class="mono">@((_bidPrice > 0 && _bidQuantity > 0 ? _bidPrice * _bidQuantity : 0).ToString("N0")) QU</strong> |
| 221 | + </div> |
191 | 222 |
|
192 | 223 | @if (!_bidConfirmed) |
193 | 224 | { |
|
240 | 271 |
|
241 | 272 | private void SetTab(string tab) { _activeTab = tab; _error = null; _result = null; } |
242 | 273 |
|
| 274 | + private void SwitchToDirectNetwork() |
| 275 | + { |
| 276 | + Backend.ActiveBackend = QueryBackend.DirectNetwork; |
| 277 | + StateHasChanged(); |
| 278 | + } |
| 279 | + |
| 280 | + private async Task NavigateToStatus(uint contractIndex) |
| 281 | + { |
| 282 | + _statusContractIndex = contractIndex; |
| 283 | + _activeTab = "Status"; |
| 284 | + _error = null; _result = null; |
| 285 | + await LookupStatus(); |
| 286 | + } |
| 287 | + |
243 | 288 | // -- Browse -- |
244 | 289 | private bool _browseLoading; |
245 | 290 | private IReadOnlyList<IpoInfo>? _activeIpos; |
|
253 | 298 | private long _maxBid; |
254 | 299 | private long _avgBid; |
255 | 300 | private long _totalBid; |
256 | | - private List<(int Index, long Price)> _mySlots = []; |
| 301 | + private List<(int Index, string Identity, long Price)> _mySlots = []; |
257 | 302 | private List<(int Index, string Identity, long Price)> _allSlots = []; |
258 | 303 |
|
259 | 304 | // -- Bid -- |
|
291 | 336 | _filledSlots = 0; _minBid = 0; _maxBid = 0; _avgBid = 0; _totalBid = 0; |
292 | 337 | try |
293 | 338 | { |
294 | | - if (_statusContractIndex < 1 || _statusContractIndex > 23) |
295 | | - { _error = "Contract index must be between 1 and 23."; return; } |
| 339 | + if (_statusContractIndex < 1 || _statusContractIndex > 1023) |
| 340 | + { _error = "Contract index must be between 1 and 1023."; return; } |
296 | 341 |
|
297 | 342 | if (Backend.ActiveBackend != QueryBackend.DirectNetwork) |
298 | | - { _error = "This feature requires the DirectNetwork backend. Switch to DirectNetwork in Settings."; return; } |
| 343 | + { _error = "This feature requires the DirectNetwork backend."; return; } |
299 | 344 |
|
300 | 345 | _ipoStatus = await Backend.GetIpoStatusAsync(_statusContractIndex); |
301 | 346 | ComputeStatusSummary(); |
|
321 | 366 | filled.Add((i, identity.Value.ToString(), price)); |
322 | 367 |
|
323 | 368 | if (myPk != null && _ipoStatus.PublicKeys[i].AsSpan().SequenceEqual(myPk)) |
324 | | - _mySlots.Add((i, price)); |
| 369 | + _mySlots.Add((i, identity.Value.ToString(), price)); |
325 | 370 | } |
326 | 371 |
|
327 | 372 | _filledSlots = filled.Count; |
|
341 | 386 | private async Task PreviewBid() |
342 | 387 | { |
343 | 388 | _error = null; _result = null; |
344 | | - if (_bidContractIndex < 1 || _bidContractIndex > 23) |
345 | | - { _error = "Contract index must be between 1 and 23."; return; } |
| 389 | + if (_bidContractIndex < 1 || _bidContractIndex > 1023) |
| 390 | + { _error = "Contract index must be between 1 and 1023."; return; } |
346 | 391 | if (_bidPrice <= 0) { _error = "Price per share must be positive."; return; } |
347 | 392 | if (_bidQuantity < 1 || _bidQuantity > 676) { _error = "Number of shares must be 1-676."; return; } |
348 | 393 |
|
|
0 commit comments