@meddleware/dao-ui 0.1.2 β 0.1.5
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/package.json +2 -2
- package/src/DaoView.vue +1 -5
- package/src/composables/useDaoEvents.ts +10 -4
- package/src/composables/useGates.ts +2 -2
- package/src/styles/qt.css +33 -0
- package/src/tabs/GovernanceTab.vue +82 -22
- package/src/tabs/HistoryTab.vue +47 -24
- package/src/tabs/OverviewTab.vue +21 -27
- package/src/tabs/TreasuryTab.vue +21 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/dao-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Meddleware DAO console β treasury overview, proposals, and governance on Sui.",
|
|
5
5
|
"author": "Meddleware <dev@meddleware.co.uk>",
|
|
6
6
|
"license": "0BSD",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@meddleware/design-tokens": "^0.1.2",
|
|
43
|
-
"@meddleware/ui": "^0.1.
|
|
43
|
+
"@meddleware/ui": "^0.1.11",
|
|
44
44
|
"@meddleware/wallet-adapter": "^0.0.5",
|
|
45
45
|
"@mysten/sui": "^2.30.0",
|
|
46
46
|
"vue": "^3.5.40"
|
package/src/DaoView.vue
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// qt.css is imported here (not just in main.ts) so the styles are included when
|
|
7
7
|
// DaoView is consumed as a library by the dashboard or any other host.
|
|
8
8
|
import './styles/qt.css'
|
|
9
|
-
import { ref,
|
|
9
|
+
import { ref, computed } from 'vue'
|
|
10
10
|
import TabBar from './components/TabBar.vue'
|
|
11
11
|
import StatusBar from './components/StatusBar.vue'
|
|
12
12
|
import OverviewTab from './tabs/OverviewTab.vue'
|
|
@@ -47,10 +47,6 @@ const { epoch } = useEpoch()
|
|
|
47
47
|
<div class="dao-view">
|
|
48
48
|
<div class="dao-toolbar">
|
|
49
49
|
<button class="dao-toolbar__btn" @click="activeTab = 'overview'">π Meddleware DAO</button>
|
|
50
|
-
<span class="dao-toolbar__sep" />
|
|
51
|
-
<span class="dao-muted" style="font-size: 0.75rem; padding: 0 4px">
|
|
52
|
-
access_gate Β· testnet
|
|
53
|
-
</span>
|
|
54
50
|
</div>
|
|
55
51
|
|
|
56
52
|
<TabBar :tabs="TABS" v-model="activeTab" />
|
|
@@ -5,7 +5,7 @@ import { PACKAGE_ID } from '../config.js'
|
|
|
5
5
|
export interface DaoEvent {
|
|
6
6
|
type: 'AccessMinted' | 'AccessConsumed' | 'GateCreated' | 'AccessBurned'
|
|
7
7
|
txDigest: string
|
|
8
|
-
|
|
8
|
+
checkpoint: string | null
|
|
9
9
|
address?: string
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -47,14 +47,20 @@ export function useDaoEvents(limit = 20) {
|
|
|
47
47
|
all.push({
|
|
48
48
|
type: label as DaoEvent['type'],
|
|
49
49
|
txDigest: e.transactionDigest,
|
|
50
|
-
|
|
50
|
+
checkpoint: e.checkpoint ?? null,
|
|
51
51
|
address: String(f.recipient ?? f.creator ?? f.sender ?? ''),
|
|
52
52
|
})
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
//
|
|
57
|
-
|
|
56
|
+
// Sort globally by checkpoint descending so the merged list is newest-first across all types.
|
|
57
|
+
all.sort((a, b) => {
|
|
58
|
+
const ca = BigInt(a.checkpoint ?? '0')
|
|
59
|
+
const cb = BigInt(b.checkpoint ?? '0')
|
|
60
|
+
if (cb > ca) return 1
|
|
61
|
+
if (cb < ca) return -1
|
|
62
|
+
return 0
|
|
63
|
+
})
|
|
58
64
|
events.value = all.slice(0, limit)
|
|
59
65
|
lastRefresh.value = new Date()
|
|
60
66
|
} catch (e) {
|
|
@@ -9,7 +9,7 @@ export interface Gate {
|
|
|
9
9
|
paused: boolean
|
|
10
10
|
frozen: boolean
|
|
11
11
|
txDigest: string
|
|
12
|
-
|
|
12
|
+
checkpoint: string | null
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function useGates() {
|
|
@@ -37,7 +37,7 @@ export function useGates() {
|
|
|
37
37
|
paused: false,
|
|
38
38
|
frozen: false,
|
|
39
39
|
txDigest: e.transactionDigest,
|
|
40
|
-
|
|
40
|
+
checkpoint: e.checkpoint ?? null,
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
43
|
} catch (e) {
|
package/src/styles/qt.css
CHANGED
|
@@ -131,6 +131,9 @@
|
|
|
131
131
|
border-top: 1px solid var(--border);
|
|
132
132
|
flex-shrink: 0;
|
|
133
133
|
font-family: var(--font-mono);
|
|
134
|
+
position: sticky;
|
|
135
|
+
bottom: 0;
|
|
136
|
+
z-index: 1;
|
|
134
137
|
}
|
|
135
138
|
|
|
136
139
|
.dao-statusbar__dot {
|
|
@@ -319,6 +322,36 @@
|
|
|
319
322
|
font-size: 0.72rem;
|
|
320
323
|
}
|
|
321
324
|
|
|
325
|
+
/* ββ Info tooltip marker βββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
|
326
|
+
|
|
327
|
+
.dao-info {
|
|
328
|
+
display: inline-flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: center;
|
|
331
|
+
width: 13px;
|
|
332
|
+
height: 13px;
|
|
333
|
+
border-radius: 50%;
|
|
334
|
+
border: 1px solid var(--muted);
|
|
335
|
+
color: var(--muted);
|
|
336
|
+
font-size: 0.58rem;
|
|
337
|
+
font-weight: 700;
|
|
338
|
+
font-style: normal;
|
|
339
|
+
cursor: help;
|
|
340
|
+
vertical-align: middle;
|
|
341
|
+
margin-left: 3px;
|
|
342
|
+
text-decoration: none;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* ββ Pagination ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
|
346
|
+
|
|
347
|
+
.dao-pagination {
|
|
348
|
+
display: flex;
|
|
349
|
+
align-items: center;
|
|
350
|
+
gap: 10px;
|
|
351
|
+
justify-content: center;
|
|
352
|
+
margin-top: 10px;
|
|
353
|
+
}
|
|
354
|
+
|
|
322
355
|
/* ββ Misc ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */
|
|
323
356
|
|
|
324
357
|
.dao-mono {
|
|
@@ -1,19 +1,52 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from 'vue'
|
|
2
|
+
import { ref, watch, computed } from 'vue'
|
|
3
3
|
import Panel from '../components/Panel.vue'
|
|
4
4
|
import { usePlatformConfig } from '../composables/usePlatformConfig.js'
|
|
5
|
+
import { useWallet, getSuiClient } from '../wallet.js'
|
|
6
|
+
import { PACKAGE_ID } from '../config.js'
|
|
7
|
+
import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
|
|
8
|
+
import { NETWORK } from '../config.js'
|
|
5
9
|
|
|
6
10
|
const { config } = usePlatformConfig()
|
|
11
|
+
const { account } = useWallet()
|
|
7
12
|
|
|
8
13
|
const commissionPct = computed(() =>
|
|
9
14
|
config.value ? (config.value.commissionBps / 100).toFixed(2) + '%' : 'β',
|
|
10
15
|
)
|
|
16
|
+
|
|
17
|
+
type CapState = 'idle' | 'checking' | 'found' | 'not-found' | 'error'
|
|
18
|
+
const capState = ref<CapState>('idle')
|
|
19
|
+
|
|
20
|
+
async function checkCaps(address: string) {
|
|
21
|
+
if (!PACKAGE_ID) { capState.value = 'not-found'; return }
|
|
22
|
+
capState.value = 'checking'
|
|
23
|
+
try {
|
|
24
|
+
const client = getSuiClient()
|
|
25
|
+
const res = await client.listOwnedObjects({
|
|
26
|
+
owner: address,
|
|
27
|
+
type: `${PACKAGE_ID}::access_gate::PlatformAdminCap`,
|
|
28
|
+
limit: 1,
|
|
29
|
+
})
|
|
30
|
+
capState.value = res.objects.length > 0 ? 'found' : 'not-found'
|
|
31
|
+
} catch {
|
|
32
|
+
capState.value = 'error'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
watch(
|
|
37
|
+
() => account.value?.address,
|
|
38
|
+
(addr) => {
|
|
39
|
+
if (addr) void checkCaps(addr)
|
|
40
|
+
else capState.value = 'idle'
|
|
41
|
+
},
|
|
42
|
+
{ immediate: true },
|
|
43
|
+
)
|
|
11
44
|
</script>
|
|
12
45
|
|
|
13
46
|
<template>
|
|
14
47
|
<div style="display: flex; flex-direction: column; gap: 10px">
|
|
15
|
-
<Panel title="
|
|
16
|
-
<div class="dao-stat-grid" style="max-width:
|
|
48
|
+
<Panel title="Platform Parameters">
|
|
49
|
+
<div class="dao-stat-grid" style="max-width: 480px">
|
|
17
50
|
<span class="dao-stat-grid__label">Commission rate</span>
|
|
18
51
|
<span class="dao-stat-grid__value dao-mono">{{ commissionPct }}</span>
|
|
19
52
|
|
|
@@ -21,34 +54,61 @@ const commissionPct = computed(() =>
|
|
|
21
54
|
<span class="dao-stat-grid__value dao-mono">10.00% (1000 bps β on-chain hard cap)</span>
|
|
22
55
|
|
|
23
56
|
<span class="dao-stat-grid__label">Treasury address</span>
|
|
24
|
-
<span
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
57
|
+
<span class="dao-stat-grid__value" style="text-align: left">
|
|
58
|
+
<CopyableAddress v-if="config?.treasury" :address="config.treasury">
|
|
59
|
+
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
|
|
60
|
+
</CopyableAddress>
|
|
61
|
+
<span v-else class="dao-mono" style="font-size: 0.7rem">β</span>
|
|
29
62
|
</span>
|
|
30
63
|
</div>
|
|
31
64
|
|
|
32
65
|
<p class="dao-muted" style="margin: 10px 0 0; font-size: 0.72rem">
|
|
33
66
|
Parameters are governed by <code>PlatformAdminCap</code> on-chain.
|
|
34
|
-
Commission is
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</Panel>
|
|
38
|
-
|
|
39
|
-
<Panel title="Vault DAO Parameters">
|
|
40
|
-
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
41
|
-
DAO-governed vault parameters (skim fee, buyback ratio, allocation caps, fee recipients)
|
|
42
|
-
will appear here when <code>vault_config</code> and <code>vault_dao</code> are deployed.
|
|
67
|
+
Commission is charged on every NFT access purchase:
|
|
68
|
+
<code>commission_bps / 10000 Γ price</code> routes to treasury;
|
|
69
|
+
the remainder goes to the gate operator.
|
|
43
70
|
</p>
|
|
44
71
|
</Panel>
|
|
45
72
|
|
|
46
73
|
<Panel title="Admin Actions">
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
74
|
+
<!-- Not connected -->
|
|
75
|
+
<template v-if="!account">
|
|
76
|
+
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
77
|
+
Connect a wallet via the header to manage platform settings.
|
|
78
|
+
</p>
|
|
79
|
+
</template>
|
|
80
|
+
|
|
81
|
+
<!-- Connected, checking -->
|
|
82
|
+
<template v-else-if="capState === 'checking'">
|
|
83
|
+
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
84
|
+
Checking capabilities for {{ account.address.slice(0, 10) }}β¦
|
|
85
|
+
</p>
|
|
86
|
+
</template>
|
|
87
|
+
|
|
88
|
+
<!-- Connected, has PlatformAdminCap -->
|
|
89
|
+
<template v-else-if="capState === 'found'">
|
|
90
|
+
<p class="dao-muted" style="margin: 0 0 8px; font-size: 0.78rem">
|
|
91
|
+
<span style="color: var(--ok)">β</span>
|
|
92
|
+
<code>PlatformAdminCap</code> detected. Admin controls will appear here when implemented.
|
|
93
|
+
</p>
|
|
94
|
+
</template>
|
|
95
|
+
|
|
96
|
+
<!-- Connected, no cap -->
|
|
97
|
+
<template v-else-if="capState === 'not-found'">
|
|
98
|
+
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
99
|
+
<span>β</span>
|
|
100
|
+
Connected: {{ account.address.slice(0, 10) }}β¦{{ account.address.slice(-6) }}<br>
|
|
101
|
+
This wallet does not hold <code>PlatformAdminCap</code> or <code>DaoAdminCap</code>.
|
|
102
|
+
Privileged actions are not available.
|
|
103
|
+
</p>
|
|
104
|
+
</template>
|
|
105
|
+
|
|
106
|
+
<!-- Error -->
|
|
107
|
+
<template v-else-if="capState === 'error'">
|
|
108
|
+
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
109
|
+
Unable to verify capabilities. Check your connection and try again.
|
|
110
|
+
</p>
|
|
111
|
+
</template>
|
|
52
112
|
</Panel>
|
|
53
113
|
</div>
|
|
54
114
|
</template>
|
package/src/tabs/HistoryTab.vue
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { ref, computed } from 'vue'
|
|
2
3
|
import DataTable from '../components/DataTable.vue'
|
|
3
4
|
import { useDaoEvents } from '../composables/useDaoEvents.js'
|
|
5
|
+
import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
|
|
6
|
+
import { NETWORK } from '../config.js'
|
|
4
7
|
import type { DaoEvent } from '../composables/useDaoEvents.js'
|
|
5
8
|
|
|
6
|
-
const { events, loading, error, reload } = useDaoEvents(
|
|
9
|
+
const { events, loading, error, reload } = useDaoEvents(200)
|
|
10
|
+
|
|
11
|
+
const PAGE_SIZE = 20
|
|
12
|
+
const page = ref(1)
|
|
13
|
+
|
|
14
|
+
const totalPages = computed(() => Math.max(1, Math.ceil(events.value.length / PAGE_SIZE)))
|
|
15
|
+
const pageEvents = computed(() =>
|
|
16
|
+
events.value.slice((page.value - 1) * PAGE_SIZE, page.value * PAGE_SIZE),
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
function prevPage() { if (page.value > 1) page.value-- }
|
|
20
|
+
function nextPage() { if (page.value < totalPages.value) page.value++ }
|
|
7
21
|
|
|
8
22
|
const eventLabel: Record<DaoEvent['type'], string> = {
|
|
9
23
|
AccessMinted: 'Access Sold',
|
|
@@ -12,19 +26,6 @@ const eventLabel: Record<DaoEvent['type'], string> = {
|
|
|
12
26
|
AccessBurned: 'Access Burned',
|
|
13
27
|
}
|
|
14
28
|
|
|
15
|
-
function shortTs(ms: number): string {
|
|
16
|
-
if (!ms) return 'β'
|
|
17
|
-
return new Date(ms).toISOString().replace('T', ' ').slice(0, 19) + ' UTC'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function shortAddr(addr: string | undefined): string {
|
|
21
|
-
if (!addr || addr === 'undefined') return 'β'
|
|
22
|
-
return addr.slice(0, 10) + 'β¦' + addr.slice(-6)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function explorerUrl(digest: string): string {
|
|
26
|
-
return `https://suiscan.xyz/testnet/tx/${digest}`
|
|
27
|
-
}
|
|
28
29
|
</script>
|
|
29
30
|
|
|
30
31
|
<template>
|
|
@@ -38,14 +39,23 @@ function explorerUrl(digest: string): string {
|
|
|
38
39
|
|
|
39
40
|
<p v-if="error" class="dao-muted">{{ error }}</p>
|
|
40
41
|
|
|
41
|
-
<DataTable v-if="
|
|
42
|
+
<DataTable v-if="pageEvents.length || loading" :empty="'No events'">
|
|
42
43
|
<template #head>
|
|
43
44
|
<th>Type</th>
|
|
44
|
-
<th>
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
<th>
|
|
46
|
+
Address
|
|
47
|
+
<abbr class="dao-info" title="The wallet address associated with this event (recipient, creator, or initiator depending on event type). Click to view in explorer; click the copy icon to copy the full address.">?</abbr>
|
|
48
|
+
</th>
|
|
49
|
+
<th>
|
|
50
|
+
Block
|
|
51
|
+
<abbr class="dao-info" title="Sui checkpoint sequence number β equivalent to a block height in other blockchains. Each checkpoint finalizes a batch of transactions; higher numbers are more recent.">?</abbr>
|
|
52
|
+
</th>
|
|
53
|
+
<th>
|
|
54
|
+
Transaction
|
|
55
|
+
<abbr class="dao-info" title="Transaction digest β a unique identifier for the transaction that emitted this event. Click to view in explorer; click the copy icon to copy the full digest.">?</abbr>
|
|
56
|
+
</th>
|
|
47
57
|
</template>
|
|
48
|
-
<tr v-for="ev in
|
|
58
|
+
<tr v-for="ev in pageEvents" :key="ev.txDigest">
|
|
49
59
|
<td>
|
|
50
60
|
<span
|
|
51
61
|
class="dao-badge"
|
|
@@ -56,15 +66,28 @@ function explorerUrl(digest: string): string {
|
|
|
56
66
|
}"
|
|
57
67
|
>{{ eventLabel[ev.type] }}</span>
|
|
58
68
|
</td>
|
|
59
|
-
<td class="dao-mono" style="font-size: 0.72rem
|
|
60
|
-
|
|
69
|
+
<td class="dao-mono" style="font-size: 0.72rem; white-space: nowrap">
|
|
70
|
+
<CopyableAddress v-if="ev.address && ev.address !== 'undefined'" :address="ev.address">
|
|
71
|
+
<ExplorerLink :href="suiExplorerUrl('account', ev.address, NETWORK)" :value="ev.address" />
|
|
72
|
+
</CopyableAddress>
|
|
73
|
+
<span v-else>β</span>
|
|
74
|
+
</td>
|
|
61
75
|
<td class="dao-mono" style="font-size: 0.72rem">
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
76
|
+
{{ ev.checkpoint ? '#' + ev.checkpoint : 'β' }}
|
|
77
|
+
</td>
|
|
78
|
+
<td class="dao-mono" style="font-size: 0.72rem; white-space: nowrap">
|
|
79
|
+
<CopyableAddress :address="ev.txDigest" label="Copy transaction">
|
|
80
|
+
<ExplorerLink :href="suiExplorerUrl('txblock', ev.txDigest, NETWORK)" :value="ev.txDigest" />
|
|
81
|
+
</CopyableAddress>
|
|
65
82
|
</td>
|
|
66
83
|
</tr>
|
|
67
84
|
</DataTable>
|
|
68
85
|
<p v-else-if="!loading" class="dao-placeholder">No events found.</p>
|
|
86
|
+
|
|
87
|
+
<div v-if="totalPages > 1" class="dao-pagination">
|
|
88
|
+
<button class="dao-toolbar__btn" :disabled="page === 1" @click="prevPage">β Prev</button>
|
|
89
|
+
<span class="dao-muted" style="font-size: 0.78rem">Page {{ page }} / {{ totalPages }}</span>
|
|
90
|
+
<button class="dao-toolbar__btn" :disabled="page === totalPages" @click="nextPage">Next β</button>
|
|
91
|
+
</div>
|
|
69
92
|
</div>
|
|
70
93
|
</template>
|
package/src/tabs/OverviewTab.vue
CHANGED
|
@@ -5,19 +5,15 @@ import AmountCell from '../components/AmountCell.vue'
|
|
|
5
5
|
import { usePlatformConfig } from '../composables/usePlatformConfig.js'
|
|
6
6
|
import { useTreasury } from '../composables/useTreasury.js'
|
|
7
7
|
import { useDaoEvents } from '../composables/useDaoEvents.js'
|
|
8
|
+
import { useGates } from '../composables/useGates.js'
|
|
9
|
+
import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
|
|
10
|
+
import { NETWORK } from '../config.js'
|
|
8
11
|
import type { DaoEvent } from '../composables/useDaoEvents.js'
|
|
9
12
|
|
|
10
13
|
const { config } = usePlatformConfig()
|
|
11
14
|
const { balance } = useTreasury(() => config.value?.treasury ?? null)
|
|
12
15
|
const { events, loading: eventsLoading } = useDaoEvents(12)
|
|
13
|
-
|
|
14
|
-
function timeAgo(ms: number): string {
|
|
15
|
-
const s = Math.floor((Date.now() - ms) / 1000)
|
|
16
|
-
if (s < 5) return 'just now'
|
|
17
|
-
if (s < 60) return `${s}s ago`
|
|
18
|
-
if (s < 3600) return `${Math.floor(s / 60)}m ago`
|
|
19
|
-
return `${Math.floor(s / 3600)}h ago`
|
|
20
|
-
}
|
|
16
|
+
const { gates, loading: gatesLoading } = useGates()
|
|
21
17
|
|
|
22
18
|
const eventLabel: Record<DaoEvent['type'], string> = {
|
|
23
19
|
AccessMinted: 'Access sold',
|
|
@@ -29,6 +25,9 @@ const eventLabel: Record<DaoEvent['type'], string> = {
|
|
|
29
25
|
const commissionPct = computed(() =>
|
|
30
26
|
config.value ? (config.value.commissionBps / 100).toFixed(2) + '%' : 'β',
|
|
31
27
|
)
|
|
28
|
+
|
|
29
|
+
const accessesMinted = computed(() => events.value.filter((e) => e.type === 'AccessMinted').length)
|
|
30
|
+
const accessesConsumed = computed(() => events.value.filter((e) => e.type === 'AccessConsumed').length)
|
|
32
31
|
</script>
|
|
33
32
|
|
|
34
33
|
<template>
|
|
@@ -44,31 +43,26 @@ const commissionPct = computed(() =>
|
|
|
44
43
|
<span class="dao-stat-grid__value dao-mono">{{ commissionPct }}</span>
|
|
45
44
|
|
|
46
45
|
<span class="dao-stat-grid__label">Treasury address</span>
|
|
47
|
-
<span
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{{ config?.treasury ? config.treasury.slice(0, 10) + 'β¦' + config.treasury.slice(-6) : 'β' }}
|
|
46
|
+
<span class="dao-stat-grid__value" style="text-align: left">
|
|
47
|
+
<CopyableAddress v-if="config?.treasury" :address="config.treasury">
|
|
48
|
+
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
|
|
49
|
+
</CopyableAddress>
|
|
50
|
+
<span v-else class="dao-mono" style="font-size: 0.7rem">β</span>
|
|
53
51
|
</span>
|
|
54
52
|
</div>
|
|
55
53
|
</Panel>
|
|
56
54
|
|
|
57
|
-
<Panel title="Platform
|
|
55
|
+
<Panel title="Platform Activity">
|
|
58
56
|
<div class="dao-stat-grid">
|
|
59
|
-
<span class="dao-stat-grid__label">
|
|
60
|
-
<span class="dao-stat-grid__value dao-
|
|
57
|
+
<span class="dao-stat-grid__label">Active gates</span>
|
|
58
|
+
<span class="dao-stat-grid__value dao-mono">{{ gatesLoading ? 'β¦' : gates.length }}</span>
|
|
61
59
|
|
|
62
|
-
<span class="dao-stat-grid__label">
|
|
63
|
-
<span class="dao-stat-grid__value dao-
|
|
60
|
+
<span class="dao-stat-grid__label">Accesses minted</span>
|
|
61
|
+
<span class="dao-stat-grid__value dao-mono">{{ accessesMinted || 'β' }}</span>
|
|
64
62
|
|
|
65
|
-
<span class="dao-stat-grid__label">
|
|
66
|
-
<span class="dao-stat-grid__value dao-
|
|
63
|
+
<span class="dao-stat-grid__label">Accesses consumed</span>
|
|
64
|
+
<span class="dao-stat-grid__value dao-mono">{{ accessesConsumed || 'β' }}</span>
|
|
67
65
|
</div>
|
|
68
|
-
<p class="dao-muted" style="margin: 8px 0 0; font-size: 0.72rem">
|
|
69
|
-
Strategy NAV, buyback and fee distribution data will appear here when
|
|
70
|
-
<code>vault_core</code> and <code>vault_fee_distributor</code> are deployed.
|
|
71
|
-
</p>
|
|
72
66
|
</Panel>
|
|
73
67
|
</div>
|
|
74
68
|
|
|
@@ -76,10 +70,10 @@ const commissionPct = computed(() =>
|
|
|
76
70
|
<Panel title="Recent Activity">
|
|
77
71
|
<p v-if="eventsLoading" class="dao-muted" style="margin: 0">Loading eventsβ¦</p>
|
|
78
72
|
<ul v-else-if="events.length" class="dao-feed">
|
|
79
|
-
<li v-for="ev in events" :key="ev.txDigest
|
|
73
|
+
<li v-for="ev in events" :key="ev.txDigest" class="dao-feed__item">
|
|
80
74
|
<span class="dao-feed__dot" />
|
|
81
75
|
<span class="dao-feed__type">{{ eventLabel[ev.type] }}</span>
|
|
82
|
-
<span class="dao-feed__time">{{
|
|
76
|
+
<span class="dao-feed__time">ckpt {{ ev.checkpoint ?? '?' }}</span>
|
|
83
77
|
</li>
|
|
84
78
|
</ul>
|
|
85
79
|
<p v-else class="dao-placeholder">No recent events.</p>
|
package/src/tabs/TreasuryTab.vue
CHANGED
|
@@ -6,6 +6,8 @@ import DataTable from '../components/DataTable.vue'
|
|
|
6
6
|
import { usePlatformConfig } from '../composables/usePlatformConfig.js'
|
|
7
7
|
import { useTreasury } from '../composables/useTreasury.js'
|
|
8
8
|
import { useGates } from '../composables/useGates.js'
|
|
9
|
+
import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
|
|
10
|
+
import { NETWORK } from '../config.js'
|
|
9
11
|
|
|
10
12
|
const { config, loading: cfgLoading, error: cfgErr } = usePlatformConfig()
|
|
11
13
|
const { balance } = useTreasury(() => config.value?.treasury ?? null)
|
|
@@ -19,15 +21,11 @@ function formatPrice(mist: bigint): string {
|
|
|
19
21
|
const n = Number(mist) / 1e9
|
|
20
22
|
return n >= 0.0001 ? n.toFixed(4) + ' SUI' : n.toFixed(9).replace(/0+$/, '') + ' SUI'
|
|
21
23
|
}
|
|
22
|
-
|
|
23
|
-
function shortDate(ms: number): string {
|
|
24
|
-
return ms ? new Date(ms).toISOString().slice(0, 10) : 'β'
|
|
25
|
-
}
|
|
26
24
|
</script>
|
|
27
25
|
|
|
28
26
|
<template>
|
|
29
27
|
<div style="display: flex; flex-direction: column; gap: 10px">
|
|
30
|
-
<Panel title="
|
|
28
|
+
<Panel title="Treasury">
|
|
31
29
|
<p v-if="cfgLoading" class="dao-muted">Loadingβ¦</p>
|
|
32
30
|
<p v-else-if="cfgErr" class="dao-muted">{{ cfgErr }}</p>
|
|
33
31
|
<template v-else-if="config">
|
|
@@ -35,40 +33,42 @@ function shortDate(ms: number): string {
|
|
|
35
33
|
<span class="dao-stat-grid__label">Commission rate</span>
|
|
36
34
|
<span class="dao-stat-grid__value dao-mono">{{ commissionPct }} ({{ config.commissionBps }} bps)</span>
|
|
37
35
|
|
|
38
|
-
<span class="dao-stat-grid__label">Treasury address</span>
|
|
39
|
-
<span class="dao-stat-grid__value dao-mono" style="font-size: 0.72rem; text-align: left; word-break: break-all">
|
|
40
|
-
{{ config.treasury }}
|
|
41
|
-
</span>
|
|
42
|
-
|
|
43
36
|
<span class="dao-stat-grid__label">Current balance</span>
|
|
44
37
|
<AmountCell class="dao-stat-grid__value" :mist="balance" />
|
|
38
|
+
|
|
39
|
+
<span class="dao-stat-grid__label">Treasury address</span>
|
|
40
|
+
<span class="dao-stat-grid__value" style="text-align: left">
|
|
41
|
+
<CopyableAddress :address="config.treasury">
|
|
42
|
+
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
|
|
43
|
+
</CopyableAddress>
|
|
44
|
+
</span>
|
|
45
45
|
</div>
|
|
46
46
|
</template>
|
|
47
47
|
<p v-else class="dao-muted">PlatformConfig not loaded.</p>
|
|
48
48
|
</Panel>
|
|
49
49
|
|
|
50
|
-
<Panel title="Fee Distribution">
|
|
51
|
-
<p class="dao-muted" style="margin: 0; font-size: 0.78rem">
|
|
52
|
-
Fee distributor data (buyback/burn ratios, distribution history) will appear here when
|
|
53
|
-
<code>vault_fee_distributor</code> is deployed.
|
|
54
|
-
</p>
|
|
55
|
-
</Panel>
|
|
56
|
-
|
|
57
50
|
<Panel title="Community Gates">
|
|
58
51
|
<p v-if="gatesLoading" class="dao-muted">Loading gatesβ¦</p>
|
|
59
52
|
<DataTable v-else-if="gates.length" :empty="'No gates found'">
|
|
60
53
|
<template #head>
|
|
61
54
|
<th>Gate name</th>
|
|
62
55
|
<th>Price</th>
|
|
63
|
-
<th>
|
|
56
|
+
<th>
|
|
57
|
+
Block
|
|
58
|
+
<abbr class="dao-info" title="Sui checkpoint sequence number at which this gate was created. Equivalent to a block height in other blockchains.">?</abbr>
|
|
59
|
+
</th>
|
|
64
60
|
<th>Object ID</th>
|
|
65
61
|
</template>
|
|
66
62
|
<tr v-for="gate in gates" :key="gate.id">
|
|
67
63
|
<td>{{ gate.name }}</td>
|
|
68
64
|
<td class="dao-amount">{{ formatPrice(gate.price) }}</td>
|
|
69
|
-
<td class="dao-mono" style="font-size: 0.72rem">
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
<td class="dao-mono" style="font-size: 0.72rem">
|
|
66
|
+
{{ gate.checkpoint ? '#' + gate.checkpoint : 'β' }}
|
|
67
|
+
</td>
|
|
68
|
+
<td class="dao-mono" style="font-size: 0.7rem; white-space: nowrap">
|
|
69
|
+
<CopyableAddress :address="gate.id" label="Copy object ID">
|
|
70
|
+
<ExplorerLink :href="suiExplorerUrl('object', gate.id, NETWORK)" :value="gate.id" />
|
|
71
|
+
</CopyableAddress>
|
|
72
72
|
</td>
|
|
73
73
|
</tr>
|
|
74
74
|
</DataTable>
|