lfqian commited on
Commit
6ddefe6
·
1 Parent(s): 664a45e

feat: add refresh button to reload latest data from Supabase

Browse files
Files changed (1) hide show
  1. src/views/LiveView.vue +55 -0
src/views/LiveView.vue CHANGED
@@ -6,6 +6,14 @@
6
  <AssetTabs v-model="asset" :ordered-assets="orderedAssets" />
7
  </div>
8
  <div class="toolbar__right">
 
 
 
 
 
 
 
 
9
  <div class="mode">
10
  <button class="mode__btn" :class="{ 'is-active': mode==='usd' }" @click="mode='usd'">$</button>
11
  <button class="mode__btn" :class="{ 'is-active': mode==='pct' }" @click="mode='pct'">%</button>
@@ -124,6 +132,7 @@ const ASSET_CUTOFF = { BTC: '2025-08-01' }
124
  const mode = ref('usd')
125
  const asset = ref('BTC')
126
  const rowsRef = ref([])
 
127
  let allDecisions = []
128
  const cards = shallowRef([])
129
 
@@ -146,6 +155,23 @@ onMounted(async () => {
146
  }
147
  })
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  /* ---------- helpers ---------- */
150
  const fmtUSD = (n) => (n ?? 0).toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 })
151
  const signedMoney = (n) => `${n >= 0 ? '+' : '−'}${fmtUSD(Math.abs(n))}`
@@ -335,6 +361,35 @@ watch(
335
 
336
  /* toolbar */
337
  .toolbar { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 0 10px; background: #ffffff; color: #0f172a; border-bottom: 1px solid #E7ECF3; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  .mode__btn { height: 30px; min-width: 40px; padding: 0 10px; border-radius: 10px; border: 1px solid #D7DDE7; background: #ffffff; font-weight: 700; color: #0f172a; }
339
  .mode__btn.is-active { background: #0f172a; color: #ffffff; border-color: #0f172a; }
340
 
 
6
  <AssetTabs v-model="asset" :ordered-assets="orderedAssets" />
7
  </div>
8
  <div class="toolbar__right">
9
+ <button
10
+ class="refresh-btn"
11
+ @click="refreshData"
12
+ :disabled="refreshing"
13
+ title="Refresh data from database"
14
+ >
15
+ <i class="pi pi-refresh" :class="{ 'spinning': refreshing }"></i>
16
+ </button>
17
  <div class="mode">
18
  <button class="mode__btn" :class="{ 'is-active': mode==='usd' }" @click="mode='usd'">$</button>
19
  <button class="mode__btn" :class="{ 'is-active': mode==='pct' }" @click="mode='pct'">%</button>
 
132
  const mode = ref('usd')
133
  const asset = ref('BTC')
134
  const rowsRef = ref([])
135
+ const refreshing = ref(false)
136
  let allDecisions = []
137
  const cards = shallowRef([])
138
 
 
155
  }
156
  })
157
 
158
+ /* ---------- refresh data ---------- */
159
+ async function refreshData() {
160
+ if (refreshing.value) return
161
+ refreshing.value = true
162
+ try {
163
+ console.log('[Live] Force refreshing data from Supabase...')
164
+ await dataService.forceRefresh()
165
+ rowsRef.value = Array.isArray(dataService.tableRows) ? dataService.tableRows : []
166
+ allDecisions = getAllDecisions() || []
167
+ console.log('[Live] Data refreshed successfully')
168
+ } catch (e) {
169
+ console.error('[Live] Error refreshing data:', e)
170
+ } finally {
171
+ refreshing.value = false
172
+ }
173
+ }
174
+
175
  /* ---------- helpers ---------- */
176
  const fmtUSD = (n) => (n ?? 0).toLocaleString(undefined, { style: 'currency', currency: 'USD', maximumFractionDigits: 2 })
177
  const signedMoney = (n) => `${n >= 0 ? '+' : '−'}${fmtUSD(Math.abs(n))}`
 
361
 
362
  /* toolbar */
363
  .toolbar { position: sticky; top: 0; z-index: 10; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 8px 0 10px; background: #ffffff; color: #0f172a; border-bottom: 1px solid #E7ECF3; }
364
+ .toolbar__right { display: flex; align-items: center; gap: 12px; }
365
+ .refresh-btn {
366
+ height: 30px;
367
+ width: 30px;
368
+ border-radius: 10px;
369
+ border: 1px solid #D7DDE7;
370
+ background: #ffffff;
371
+ color: #0f172a;
372
+ cursor: pointer;
373
+ display: flex;
374
+ align-items: center;
375
+ justify-content: center;
376
+ transition: all 0.2s ease;
377
+ }
378
+ .refresh-btn:hover:not(:disabled) {
379
+ background: #f8f9fa;
380
+ border-color: #0f172a;
381
+ }
382
+ .refresh-btn:disabled {
383
+ opacity: 0.5;
384
+ cursor: not-allowed;
385
+ }
386
+ .refresh-btn i.spinning {
387
+ animation: spin 1s linear infinite;
388
+ }
389
+ @keyframes spin {
390
+ from { transform: rotate(0deg); }
391
+ to { transform: rotate(360deg); }
392
+ }
393
  .mode__btn { height: 30px; min-width: 40px; padding: 0 10px; border-radius: 10px; border: 1px solid #D7DDE7; background: #ffffff; font-weight: 700; color: #0f172a; }
394
  .mode__btn.is-active { background: #0f172a; color: #ffffff; border-color: #0f172a; }
395