@meddleware/dao-ui 0.1.19 → 0.1.21
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/README.md +3 -2
- package/package.json +29 -7
- package/src/App.vue +1 -1
- package/src/DaoView.vue +8 -17
- package/src/components/ProposalRow.vue +53 -24
- package/src/composables/useGates.ts +1 -1
- package/src/config.ts +2 -2
- package/src/styles/qt.css +45 -4
- package/src/tabs/GovernanceTab.vue +25 -12
- package/src/tabs/HistoryTab.vue +65 -49
- package/src/tabs/OverviewTab.vue +3 -3
- package/src/tabs/ProposalsTab.vue +9 -14
- package/src/tabs/TreasuryTab.vue +3 -3
- package/vite.config.ts +5 -0
- package/src/components/StatusBar.vue +0 -37
- package/src/components/TabBar.vue +0 -19
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@ The **Meddleware DAO console** — a standalone Vue 3 SPA (and embeddable librar
|
|
|
4
4
|
platform's on-chain state on Sui: treasury balance, commission policy, the community access gates the
|
|
5
5
|
treasury controls, and a live feed of access-gate activity.
|
|
6
6
|
|
|
7
|
-
Deployed at `sui-dao.meddleware.co.uk
|
|
8
|
-
(`
|
|
7
|
+
Deployed at `sui-dao.meddleware.co.uk`. It will also be embedded inline in the tools hub
|
|
8
|
+
(`dash.meddleware.co.uk`) once governance is available; until then the hub shows the read-only
|
|
9
|
+
Treasury console (`@meddleware/treasury-ui`) in its place.
|
|
9
10
|
|
|
10
11
|
## What it shows
|
|
11
12
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/dao-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"homepage": "https://docs.meddleware.co.uk/blockchain/sui/dao/",
|
|
5
5
|
"description": "Meddleware DAO console — treasury overview, proposals, and governance on Sui.",
|
|
6
6
|
"author": "Meddleware <dev@meddleware.co.uk>",
|
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
"preview": "vite preview",
|
|
24
24
|
"type-check": "vue-tsc --noEmit",
|
|
25
25
|
"test": "vitest run",
|
|
26
|
-
"test:watch": "vitest"
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"lint:css": "stylelint \"src/**/*.{css,vue}\"",
|
|
28
|
+
"lint:js": "eslint .",
|
|
29
|
+
"lint": "npm run lint:css && npm run lint:js && npm run lint:html",
|
|
30
|
+
"lint:html": "html-validate \"src/**/*.vue\""
|
|
27
31
|
},
|
|
28
32
|
"exports": {
|
|
29
33
|
".": {
|
|
@@ -40,23 +44,41 @@
|
|
|
40
44
|
"access": "public"
|
|
41
45
|
},
|
|
42
46
|
"dependencies": {
|
|
43
|
-
"@meddleware/design-tokens": "^0.1.
|
|
44
|
-
"@meddleware/ui": "^0.1.
|
|
45
|
-
"@meddleware/wallet-adapter": "^0.0.
|
|
46
|
-
"@mysten/sui": "^2.
|
|
47
|
+
"@meddleware/design-tokens": "^0.1.7",
|
|
48
|
+
"@meddleware/ui": "^0.1.28",
|
|
49
|
+
"@meddleware/wallet-adapter": "^0.0.10",
|
|
50
|
+
"@mysten/sui": "^2.33.1",
|
|
47
51
|
"vue": "^3.5.43"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
54
|
"@types/node": "~24.12.2",
|
|
51
55
|
"@vitejs/plugin-vue": "^6.0.9",
|
|
56
|
+
"@vue/eslint-config-typescript": "^14.9.0",
|
|
52
57
|
"@vue/test-utils": "^2.5.1",
|
|
58
|
+
"autoprefixer": "^10.4.20",
|
|
59
|
+
"eslint": "^10.11.0",
|
|
60
|
+
"eslint-plugin-vue": "^10.11.1",
|
|
61
|
+
"eslint-plugin-vuejs-accessibility": "^2.6.0",
|
|
62
|
+
"html-validate": "^11.16.0",
|
|
63
|
+
"html-validate-vue": "^9.3.1",
|
|
64
|
+
"jiti": "^2.7.0",
|
|
53
65
|
"jsdom": "^29.1.1",
|
|
66
|
+
"postcss": "^8.4.49",
|
|
67
|
+
"postcss-html": "^2.0.0",
|
|
68
|
+
"stylelint": "^17.15.0",
|
|
69
|
+
"stylelint-config-standard": "^40.0.0",
|
|
54
70
|
"typescript": "~6.0.0",
|
|
55
71
|
"vite": "^8.3.0",
|
|
56
72
|
"vitest": "~5.0.1",
|
|
73
|
+
"vue-eslint-parser": "^10.4.1",
|
|
57
74
|
"vue-tsc": "~3.3.11"
|
|
58
75
|
},
|
|
59
76
|
"engines": {
|
|
60
77
|
"node": "^22.18.0 || >=24.12.0"
|
|
61
|
-
}
|
|
78
|
+
},
|
|
79
|
+
"browserslist": [
|
|
80
|
+
"> 0.5%",
|
|
81
|
+
"last 2 versions",
|
|
82
|
+
"not dead"
|
|
83
|
+
]
|
|
62
84
|
}
|
package/src/App.vue
CHANGED
package/src/DaoView.vue
CHANGED
|
@@ -7,9 +7,7 @@
|
|
|
7
7
|
// DaoView is consumed as a library by the dashboard or any other host.
|
|
8
8
|
import './styles/qt.css'
|
|
9
9
|
import { ref, computed } from 'vue'
|
|
10
|
-
import { UiToolbar,
|
|
11
|
-
import TabBar from './components/TabBar.vue'
|
|
12
|
-
import StatusBar from './components/StatusBar.vue'
|
|
10
|
+
import { AppTabNav, UiStatusBar, UiTabPanel, UiToolbar, type AppTab } from '@meddleware/ui'
|
|
13
11
|
import OverviewTab from './tabs/OverviewTab.vue'
|
|
14
12
|
import TreasuryTab from './tabs/TreasuryTab.vue'
|
|
15
13
|
import ProposalsTab from './tabs/ProposalsTab.vue'
|
|
@@ -17,10 +15,9 @@ import GovernanceTab from './tabs/GovernanceTab.vue'
|
|
|
17
15
|
import HistoryTab from './tabs/HistoryTab.vue'
|
|
18
16
|
import { useDaoEvents } from './composables/useDaoEvents.js'
|
|
19
17
|
import { useEpoch } from './composables/useEpoch.js'
|
|
18
|
+
import { NETWORK } from './config.js'
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const TABS: Tab[] = [
|
|
20
|
+
const TABS: AppTab[] = [
|
|
24
21
|
{ id: 'overview', label: 'Overview' },
|
|
25
22
|
{ id: 'treasury', label: 'Treasury' },
|
|
26
23
|
{ id: 'proposals', label: 'Proposals' },
|
|
@@ -46,20 +43,14 @@ const { epoch } = useEpoch()
|
|
|
46
43
|
|
|
47
44
|
<template>
|
|
48
45
|
<div class="dao-view">
|
|
49
|
-
<UiToolbar
|
|
50
|
-
<UiToolbarButton @click="activeTab = 'overview'">🏛 Meddleware DAO</UiToolbarButton>
|
|
51
|
-
</UiToolbar>
|
|
46
|
+
<UiToolbar :actions="[{ id: 'home', label: '🏛 Meddleware DAO' }]" @action="activeTab = 'overview'" />
|
|
52
47
|
|
|
53
|
-
<
|
|
48
|
+
<AppTabNav v-model="activeTab" :tabs="TABS" id-prefix="dao" variant="raised" aria-label="DAO sections" />
|
|
54
49
|
|
|
55
|
-
<
|
|
50
|
+
<UiTabPanel id-prefix="dao" :tab="activeTab" class="dao-content">
|
|
56
51
|
<component :is="activeComponent" />
|
|
57
|
-
</
|
|
52
|
+
</UiTabPanel>
|
|
58
53
|
|
|
59
|
-
<
|
|
60
|
-
:epoch="epoch"
|
|
61
|
-
:last-refresh="lastRefresh"
|
|
62
|
-
:error="!!eventsError"
|
|
63
|
-
/>
|
|
54
|
+
<UiStatusBar :network="NETWORK" :healthy="!eventsError" :epoch="epoch" :last-refresh="lastRefresh" />
|
|
64
55
|
</div>
|
|
65
56
|
</template>
|
|
@@ -33,36 +33,65 @@ function formatSui(mist: bigint): string {
|
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
35
|
<template>
|
|
36
|
-
<UiPanel
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
</div>
|
|
42
|
-
<span class="dao-muted" style="white-space: nowrap; font-family: var(--mw-font-mono); font-size: 0.75rem">
|
|
36
|
+
<UiPanel class="proposal">
|
|
37
|
+
<header class="proposal__head">
|
|
38
|
+
<UiBadge :variant="badgeVariant">{{ proposal.status }}</UiBadge>
|
|
39
|
+
<h3 class="proposal__title">{{ proposal.title }}</h3>
|
|
40
|
+
<small class="proposal__epochs">
|
|
43
41
|
<template v-if="epochsLeft !== null && epochsLeft > 0">{{ epochsLeft }} epochs left</template>
|
|
44
42
|
<template v-else-if="epochsLeft !== null && epochsLeft <= 0">Expired</template>
|
|
45
|
-
</
|
|
46
|
-
</
|
|
43
|
+
</small>
|
|
44
|
+
</header>
|
|
47
45
|
|
|
48
|
-
<p class="dao-muted
|
|
46
|
+
<p class="dao-muted proposal__desc">{{ proposal.description }}</p>
|
|
49
47
|
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
</
|
|
54
|
-
|
|
55
|
-
<span>{{ formatSui(proposal.contributedMist) }} SUI raised</span>
|
|
56
|
-
<span>{{ pct }}% of {{ formatSui(proposal.targetMist) }} SUI target</span>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
48
|
+
<progress class="dao-progress" :value="pct" max="100" aria-label="Funding progress">{{ pct }}%</progress>
|
|
49
|
+
<p class="proposal__totals">
|
|
50
|
+
<span>{{ formatSui(proposal.contributedMist) }} SUI raised</span>
|
|
51
|
+
<span>{{ pct }}% of {{ formatSui(proposal.targetMist) }} SUI target</span>
|
|
52
|
+
</p>
|
|
59
53
|
|
|
60
|
-
<UiToolbarButton
|
|
61
|
-
disabled
|
|
62
|
-
title="Vault DAO governance module launching soon"
|
|
63
|
-
style="margin-top: 6px; opacity: 0.5; cursor: not-allowed"
|
|
64
|
-
>
|
|
54
|
+
<UiToolbarButton disabled title="Vault DAO governance module launching soon" class="proposal__contribute">
|
|
65
55
|
Contribute
|
|
66
56
|
</UiToolbarButton>
|
|
67
57
|
</UiPanel>
|
|
68
58
|
</template>
|
|
59
|
+
|
|
60
|
+
<style scoped>
|
|
61
|
+
.proposal {
|
|
62
|
+
margin-bottom: 8px;
|
|
63
|
+
}
|
|
64
|
+
.proposal__head {
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: flex-start;
|
|
67
|
+
gap: 6px;
|
|
68
|
+
margin-bottom: 6px;
|
|
69
|
+
}
|
|
70
|
+
.proposal__title {
|
|
71
|
+
margin: 0;
|
|
72
|
+
font-size: 0.85rem;
|
|
73
|
+
font-weight: 700;
|
|
74
|
+
}
|
|
75
|
+
.proposal__epochs {
|
|
76
|
+
margin-inline-start: auto;
|
|
77
|
+
padding-inline-start: 8px;
|
|
78
|
+
white-space: nowrap;
|
|
79
|
+
font-family: var(--mw-font-mono);
|
|
80
|
+
font-size: 0.75rem;
|
|
81
|
+
color: var(--muted);
|
|
82
|
+
}
|
|
83
|
+
.proposal__desc {
|
|
84
|
+
margin: 0 0 8px;
|
|
85
|
+
}
|
|
86
|
+
.proposal__totals {
|
|
87
|
+
display: flex;
|
|
88
|
+
justify-content: space-between;
|
|
89
|
+
margin: 2px 0 4px;
|
|
90
|
+
font-size: 0.72rem;
|
|
91
|
+
color: var(--muted);
|
|
92
|
+
font-family: var(--mw-font-mono);
|
|
93
|
+
}
|
|
94
|
+
.proposal__contribute {
|
|
95
|
+
margin-top: 6px;
|
|
96
|
+
}
|
|
97
|
+
</style>
|
|
@@ -42,7 +42,7 @@ export function useGates() {
|
|
|
42
42
|
|
|
43
43
|
// Fetch each Gate shared object referenced by the AdminCap's gate_id.
|
|
44
44
|
const gateList = await Promise.all(
|
|
45
|
-
(objects ?? []).map(async (o:
|
|
45
|
+
(objects ?? []).map(async (o: { json?: { gate_id?: unknown } | null; object?: { json?: { gate_id?: unknown } | null } | null }): Promise<Gate | null> => {
|
|
46
46
|
const capFields = o?.json ?? o?.object?.json
|
|
47
47
|
const gateId = String(capFields?.gate_id ?? '')
|
|
48
48
|
if (!gateId || gateId === 'undefined') return null
|
package/src/config.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const RPC_URLS: Record<Network, string> = {
|
|
|
14
14
|
export const ACCESS_GATE_PACKAGE_ID: Record<Network, string> = {
|
|
15
15
|
testnet:
|
|
16
16
|
import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_TESTNET ||
|
|
17
|
-
'
|
|
17
|
+
'0x1a81ca177db039585e575beeeee4759466e55910e936a6733e38dbb65025eea4',
|
|
18
18
|
mainnet: import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_MAINNET || '',
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ export const ACCESS_GATE_PACKAGE_ID: Record<Network, string> = {
|
|
|
22
22
|
export const PLATFORM_CONFIG_ID: Record<Network, string> = {
|
|
23
23
|
testnet:
|
|
24
24
|
import.meta.env.VITE_PLATFORM_CONFIG_ID_TESTNET ||
|
|
25
|
-
'
|
|
25
|
+
'0xe3b949cabe9a0574c03dfc924fb3f96e6f959f2bb86d053ed6229a241c3a23f7',
|
|
26
26
|
mainnet: import.meta.env.VITE_PLATFORM_CONFIG_ID_MAINNET || '',
|
|
27
27
|
}
|
|
28
28
|
|
package/src/styles/qt.css
CHANGED
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
gap: 10px;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
@media (
|
|
52
|
+
@media (width <= 640px) {
|
|
53
53
|
.dao-overview-cols {
|
|
54
54
|
grid-template-columns: 1fr;
|
|
55
55
|
}
|
|
@@ -57,21 +57,32 @@
|
|
|
57
57
|
|
|
58
58
|
/* ── Proposal progress bar ───────────────────────────────────────────────── */
|
|
59
59
|
|
|
60
|
+
/* A native <progress>: engines paint the track/value with their own pseudo-elements. */
|
|
60
61
|
.dao-progress {
|
|
62
|
+
display: block;
|
|
63
|
+
width: 100%;
|
|
61
64
|
height: 6px;
|
|
65
|
+
margin: 3px 0;
|
|
66
|
+
appearance: none;
|
|
62
67
|
background: var(--lift);
|
|
63
68
|
border: 1px solid var(--border);
|
|
64
69
|
border-radius: 1px;
|
|
65
70
|
overflow: hidden;
|
|
66
|
-
margin: 3px 0;
|
|
67
71
|
}
|
|
68
72
|
|
|
69
|
-
.dao-
|
|
70
|
-
|
|
73
|
+
.dao-progress::-webkit-progress-bar {
|
|
74
|
+
background: var(--lift);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dao-progress::-webkit-progress-value {
|
|
71
78
|
background: var(--accent);
|
|
72
79
|
transition: width 0.3s ease;
|
|
73
80
|
}
|
|
74
81
|
|
|
82
|
+
.dao-progress::-moz-progress-bar {
|
|
83
|
+
background: var(--accent);
|
|
84
|
+
}
|
|
85
|
+
|
|
75
86
|
/* ── Info tooltip marker ─────────────────────────────────────────────────── */
|
|
76
87
|
|
|
77
88
|
.dao-info {
|
|
@@ -102,6 +113,24 @@
|
|
|
102
113
|
margin-top: 10px;
|
|
103
114
|
}
|
|
104
115
|
|
|
116
|
+
/* ── Layout utilities ────────────────────────────────────────────────────── */
|
|
117
|
+
|
|
118
|
+
/* Vertical stack of panels within a tab. */
|
|
119
|
+
.dao-stack {
|
|
120
|
+
display: flex;
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
gap: 10px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.dao-stack--tight {
|
|
126
|
+
gap: 8px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Key/value readouts stay compact rather than spanning a wide panel. */
|
|
130
|
+
.dao-stat-grid {
|
|
131
|
+
max-width: 480px;
|
|
132
|
+
}
|
|
133
|
+
|
|
105
134
|
/* ── Misc text utilities ─────────────────────────────────────────────────── */
|
|
106
135
|
|
|
107
136
|
.dao-mono {
|
|
@@ -109,6 +138,18 @@
|
|
|
109
138
|
font-size: 0.8rem;
|
|
110
139
|
}
|
|
111
140
|
|
|
141
|
+
.dao-mono--sm {
|
|
142
|
+
font-size: 0.7rem;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.dao-nowrap {
|
|
146
|
+
white-space: nowrap;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.dao-flush {
|
|
150
|
+
margin: 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
112
153
|
.dao-muted {
|
|
113
154
|
color: var(--muted);
|
|
114
155
|
font-size: 0.78rem;
|
|
@@ -50,20 +50,20 @@ watch(
|
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<template>
|
|
53
|
-
<div
|
|
53
|
+
<div class="dao-stack gov">
|
|
54
54
|
<UiPanel title="Platform Parameters">
|
|
55
|
-
<UiStatGrid
|
|
55
|
+
<UiStatGrid class="dao-stat-grid">
|
|
56
56
|
<UiStatRow label="Commission rate">{{ commissionPct }}</UiStatRow>
|
|
57
57
|
<UiStatRow label="Max commission cap">10.00% (1000 bps — on-chain hard cap)</UiStatRow>
|
|
58
58
|
<UiStatRow label="Treasury address" align="left">
|
|
59
59
|
<CopyableAddress v-if="config?.treasury" :address="config.treasury">
|
|
60
60
|
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
|
|
61
61
|
</CopyableAddress>
|
|
62
|
-
<span v-else class="dao-mono
|
|
62
|
+
<span v-else class="dao-mono dao-mono--sm">—</span>
|
|
63
63
|
</UiStatRow>
|
|
64
64
|
</UiStatGrid>
|
|
65
65
|
|
|
66
|
-
<p class="dao-muted
|
|
66
|
+
<p class="dao-muted gov__footnote">
|
|
67
67
|
Parameters are governed by <code>PlatformAdminCap</code> on-chain.
|
|
68
68
|
Commission is charged on every NFT access purchase:
|
|
69
69
|
<code>commission_bps / 10000 × price</code> routes to treasury;
|
|
@@ -74,31 +74,31 @@ watch(
|
|
|
74
74
|
<UiPanel title="Admin Actions">
|
|
75
75
|
<!-- Not connected -->
|
|
76
76
|
<template v-if="!account">
|
|
77
|
-
<p class="dao-muted"
|
|
77
|
+
<p class="dao-muted">
|
|
78
78
|
Connect a wallet via the header to manage platform settings.
|
|
79
79
|
</p>
|
|
80
80
|
</template>
|
|
81
81
|
|
|
82
82
|
<!-- Connected, checking -->
|
|
83
83
|
<template v-else-if="capState === 'checking'">
|
|
84
|
-
<p class="dao-muted"
|
|
84
|
+
<p class="dao-muted">
|
|
85
85
|
Checking capabilities for {{ account.address.slice(0, 10) }}…
|
|
86
86
|
</p>
|
|
87
87
|
</template>
|
|
88
88
|
|
|
89
89
|
<!-- Connected, has PlatformAdminCap -->
|
|
90
90
|
<template v-else-if="capState === 'found'">
|
|
91
|
-
<p class="dao-muted"
|
|
92
|
-
<span
|
|
91
|
+
<p class="dao-muted">
|
|
92
|
+
<span class="gov__ok" aria-hidden="true">●</span>
|
|
93
93
|
<code>PlatformAdminCap</code> detected. Admin controls will appear here when implemented.
|
|
94
94
|
</p>
|
|
95
95
|
</template>
|
|
96
96
|
|
|
97
97
|
<!-- Connected, no cap -->
|
|
98
98
|
<template v-else-if="capState === 'not-found'">
|
|
99
|
-
<p class="dao-muted"
|
|
100
|
-
<span>●</span>
|
|
101
|
-
Connected: {{ account.address.slice(0, 10) }}…{{ account.address.slice(-6) }}<br
|
|
99
|
+
<p class="dao-muted">
|
|
100
|
+
<span aria-hidden="true">●</span>
|
|
101
|
+
Connected: {{ account.address.slice(0, 10) }}…{{ account.address.slice(-6) }}<br />
|
|
102
102
|
This wallet does not hold <code>PlatformAdminCap</code> or <code>DaoAdminCap</code>.
|
|
103
103
|
Privileged actions are not available.
|
|
104
104
|
</p>
|
|
@@ -106,10 +106,23 @@ watch(
|
|
|
106
106
|
|
|
107
107
|
<!-- Error -->
|
|
108
108
|
<template v-else-if="capState === 'error'">
|
|
109
|
-
<p class="dao-muted"
|
|
109
|
+
<p class="dao-muted">
|
|
110
110
|
Unable to verify capabilities. Check your connection and try again.
|
|
111
111
|
</p>
|
|
112
112
|
</template>
|
|
113
113
|
</UiPanel>
|
|
114
114
|
</div>
|
|
115
115
|
</template>
|
|
116
|
+
|
|
117
|
+
<style scoped>
|
|
118
|
+
.gov p {
|
|
119
|
+
margin: 0;
|
|
120
|
+
}
|
|
121
|
+
.gov .gov__footnote {
|
|
122
|
+
margin-top: 10px;
|
|
123
|
+
font-size: 0.72rem;
|
|
124
|
+
}
|
|
125
|
+
.gov__ok {
|
|
126
|
+
color: var(--ok);
|
|
127
|
+
}
|
|
128
|
+
</style>
|
package/src/tabs/HistoryTab.vue
CHANGED
|
@@ -39,56 +39,72 @@ function badgeVariant(type: DaoEvent['type']): 'active' | 'closed' {
|
|
|
39
39
|
</script>
|
|
40
40
|
|
|
41
41
|
<template>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</div>
|
|
42
|
+
<header class="history__head">
|
|
43
|
+
<h2 class="dao-section-title">On-Chain Events</h2>
|
|
44
|
+
<UiToolbarButton :disabled="loading" @click="reload">
|
|
45
|
+
{{ loading ? 'Loading…' : 'Refresh' }}
|
|
46
|
+
</UiToolbarButton>
|
|
47
|
+
</header>
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
<p v-if="error" class="dao-muted">{{ error }}</p>
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
51
|
+
<UiDataTable v-if="pageEvents.length || loading" :empty="'No events'">
|
|
52
|
+
<template #head>
|
|
53
|
+
<th>Type</th>
|
|
54
|
+
<th>
|
|
55
|
+
Address
|
|
56
|
+
<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>
|
|
57
|
+
</th>
|
|
58
|
+
<th>
|
|
59
|
+
Block
|
|
60
|
+
<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>
|
|
61
|
+
</th>
|
|
62
|
+
<th>
|
|
63
|
+
Transaction
|
|
64
|
+
<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>
|
|
65
|
+
</th>
|
|
66
|
+
</template>
|
|
67
|
+
<tr v-for="ev in pageEvents" :key="ev.txDigest">
|
|
68
|
+
<td><UiBadge :variant="badgeVariant(ev.type)">{{ eventLabel[ev.type] }}</UiBadge></td>
|
|
69
|
+
<td class="dao-mono history__cell history__cell--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>
|
|
75
|
+
<td class="dao-mono history__cell">
|
|
76
|
+
{{ ev.checkpoint ? '#' + ev.checkpoint : '—' }}
|
|
77
|
+
</td>
|
|
78
|
+
<td class="dao-mono history__cell history__cell--nowrap">
|
|
79
|
+
<CopyableAddress :address="ev.txDigest" label="Copy transaction">
|
|
80
|
+
<ExplorerLink :href="suiExplorerUrl('txblock', ev.txDigest, NETWORK)" :value="ev.txDigest" />
|
|
81
|
+
</CopyableAddress>
|
|
82
|
+
</td>
|
|
83
|
+
</tr>
|
|
84
|
+
</UiDataTable>
|
|
85
|
+
<p v-else-if="!loading" class="dao-placeholder">No events found.</p>
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
</div>
|
|
87
|
+
<nav v-if="totalPages > 1" class="dao-pagination" aria-label="Event pages">
|
|
88
|
+
<UiToolbarButton :disabled="page === 1" @click="prevPage">← Prev</UiToolbarButton>
|
|
89
|
+
<span class="dao-muted">Page {{ page }} / {{ totalPages }}</span>
|
|
90
|
+
<UiToolbarButton :disabled="page === totalPages" @click="nextPage">Next →</UiToolbarButton>
|
|
91
|
+
</nav>
|
|
94
92
|
</template>
|
|
93
|
+
|
|
94
|
+
<style scoped>
|
|
95
|
+
.history__head {
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: space-between;
|
|
99
|
+
margin-bottom: 8px;
|
|
100
|
+
}
|
|
101
|
+
.history__head .dao-section-title {
|
|
102
|
+
margin: 0;
|
|
103
|
+
}
|
|
104
|
+
.history__cell {
|
|
105
|
+
font-size: 0.72rem;
|
|
106
|
+
}
|
|
107
|
+
.history__cell--nowrap {
|
|
108
|
+
white-space: nowrap;
|
|
109
|
+
}
|
|
110
|
+
</style>
|
package/src/tabs/OverviewTab.vue
CHANGED
|
@@ -42,7 +42,7 @@ const accessesConsumed = computed(() => events.value.filter((e) => e.type === 'A
|
|
|
42
42
|
<template>
|
|
43
43
|
<div class="dao-overview-cols">
|
|
44
44
|
<!-- Left: treasury summary -->
|
|
45
|
-
<div
|
|
45
|
+
<div class="dao-stack dao-stack--tight">
|
|
46
46
|
<UiPanel title="Treasury Balance">
|
|
47
47
|
<UiStatGrid>
|
|
48
48
|
<UiStatRow label="SUI balance"><AmountCell :mist="balance" /></UiStatRow>
|
|
@@ -51,7 +51,7 @@ const accessesConsumed = computed(() => events.value.filter((e) => e.type === 'A
|
|
|
51
51
|
<CopyableAddress v-if="config?.treasury" :address="config.treasury">
|
|
52
52
|
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
|
|
53
53
|
</CopyableAddress>
|
|
54
|
-
<span v-else class="dao-mono
|
|
54
|
+
<span v-else class="dao-mono dao-mono--sm">—</span>
|
|
55
55
|
</UiStatRow>
|
|
56
56
|
</UiStatGrid>
|
|
57
57
|
</UiPanel>
|
|
@@ -67,7 +67,7 @@ const accessesConsumed = computed(() => events.value.filter((e) => e.type === 'A
|
|
|
67
67
|
|
|
68
68
|
<!-- Right: recent activity -->
|
|
69
69
|
<UiPanel title="Recent Activity">
|
|
70
|
-
<p v-if="eventsLoading" class="dao-muted
|
|
70
|
+
<p v-if="eventsLoading" class="dao-muted dao-flush">Loading events…</p>
|
|
71
71
|
<UiActivityFeed v-else-if="events.length">
|
|
72
72
|
<UiActivityItem
|
|
73
73
|
v-for="ev in events"
|
|
@@ -10,18 +10,13 @@ const { epoch } = useEpoch()
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<template>
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</template>
|
|
23
|
-
<div v-else class="dao-placeholder">
|
|
24
|
-
No proposals yet.
|
|
25
|
-
</div>
|
|
26
|
-
</div>
|
|
13
|
+
<template v-if="proposals.length > 0">
|
|
14
|
+
<h2 class="dao-section-title">Active & Pending Proposals</h2>
|
|
15
|
+
<ul role="list">
|
|
16
|
+
<li v-for="p in proposals" :key="p.id">
|
|
17
|
+
<ProposalRow :proposal="p" :current-epoch="epoch" />
|
|
18
|
+
</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</template>
|
|
21
|
+
<p v-else class="dao-placeholder">No proposals yet.</p>
|
|
27
22
|
</template>
|
package/src/tabs/TreasuryTab.vue
CHANGED
|
@@ -32,12 +32,12 @@ function formatPrice(mist: bigint): string {
|
|
|
32
32
|
</script>
|
|
33
33
|
|
|
34
34
|
<template>
|
|
35
|
-
<div
|
|
35
|
+
<div class="dao-stack">
|
|
36
36
|
<UiPanel title="Treasury">
|
|
37
37
|
<p v-if="cfgLoading" class="dao-muted">Loading…</p>
|
|
38
38
|
<p v-else-if="cfgErr" class="dao-muted">{{ cfgErr }}</p>
|
|
39
39
|
<template v-else-if="config">
|
|
40
|
-
<UiStatGrid
|
|
40
|
+
<UiStatGrid class="dao-stat-grid">
|
|
41
41
|
<UiStatRow label="Commission rate">{{ commissionPct }} ({{ config.commissionBps }} bps)</UiStatRow>
|
|
42
42
|
<UiStatRow label="Current balance"><AmountCell :mist="balance" /></UiStatRow>
|
|
43
43
|
<UiStatRow label="Treasury address" align="left">
|
|
@@ -61,7 +61,7 @@ function formatPrice(mist: bigint): string {
|
|
|
61
61
|
<tr v-for="gate in gates" :key="gate.id">
|
|
62
62
|
<td>{{ gate.name }}</td>
|
|
63
63
|
<td class="dao-amount">{{ formatPrice(gate.price) }}</td>
|
|
64
|
-
<td class="dao-mono
|
|
64
|
+
<td class="dao-mono dao-mono--sm dao-nowrap">
|
|
65
65
|
<CopyableAddress :address="gate.id" label="Copy object ID">
|
|
66
66
|
<ExplorerLink :href="suiExplorerUrl('object', gate.id, NETWORK)" :value="gate.id" />
|
|
67
67
|
</CopyableAddress>
|
package/vite.config.ts
CHANGED
|
@@ -3,4 +3,9 @@ import vue from '@vitejs/plugin-vue'
|
|
|
3
3
|
|
|
4
4
|
export default defineConfig({
|
|
5
5
|
plugins: [vue()],
|
|
6
|
+
optimizeDeps: {
|
|
7
|
+
// @meddleware/wallet-adapter ships TS + .vue source and holds the shared wallet singleton; if it
|
|
8
|
+
// were pre-bundled, its .vue files (served raw) would load a second copy of the singleton in dev.
|
|
9
|
+
exclude: ['@meddleware/wallet-adapter'],
|
|
10
|
+
},
|
|
6
11
|
})
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
// Bottom status bar: network + health dot (error-driven), current epoch, and a
|
|
3
|
-
// relative "refreshed Ns ago" timestamp. Built on the shared UiStatusBar/UiStatusDot.
|
|
4
|
-
import { computed } from 'vue'
|
|
5
|
-
import { UiStatusBar, UiStatusDot } from '@meddleware/ui'
|
|
6
|
-
import { NETWORK } from '../config.js'
|
|
7
|
-
|
|
8
|
-
const props = defineProps<{
|
|
9
|
-
epoch: number | null
|
|
10
|
-
lastRefresh: Date | null
|
|
11
|
-
error?: boolean
|
|
12
|
-
}>()
|
|
13
|
-
|
|
14
|
-
const dotStatus = computed<'ok' | 'error'>(() => (props.error ? 'error' : 'ok'))
|
|
15
|
-
|
|
16
|
-
function timeAgo(d: Date): string {
|
|
17
|
-
const s = Math.floor((Date.now() - d.getTime()) / 1000)
|
|
18
|
-
if (s < 5) return 'just now'
|
|
19
|
-
if (s < 60) return `${s}s ago`
|
|
20
|
-
return `${Math.floor(s / 60)}m ago`
|
|
21
|
-
}
|
|
22
|
-
</script>
|
|
23
|
-
|
|
24
|
-
<template>
|
|
25
|
-
<UiStatusBar>
|
|
26
|
-
<span class="mw-statusbar__item">
|
|
27
|
-
<UiStatusDot :status="dotStatus" />
|
|
28
|
-
{{ NETWORK.charAt(0).toUpperCase() + NETWORK.slice(1) }}
|
|
29
|
-
</span>
|
|
30
|
-
<span class="mw-statusbar__sep">│</span>
|
|
31
|
-
<span v-if="epoch !== null" class="mw-statusbar__item">Epoch {{ epoch }}</span>
|
|
32
|
-
<span v-else class="mw-statusbar__item dao-muted">Epoch —</span>
|
|
33
|
-
<span class="mw-statusbar__sep">│</span>
|
|
34
|
-
<span v-if="lastRefresh" class="mw-statusbar__item">Refreshed {{ timeAgo(lastRefresh) }}</span>
|
|
35
|
-
<span v-else class="mw-statusbar__item dao-muted">Loading…</span>
|
|
36
|
-
</UiStatusBar>
|
|
37
|
-
</template>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
// Thin wrapper over AppTabNav variant="raised" from @meddleware/ui.
|
|
3
|
-
// Keeps dao-ui's internal import path stable while using the shared component.
|
|
4
|
-
import { AppTabNav } from '@meddleware/ui'
|
|
5
|
-
export type { AppTab as Tab } from '@meddleware/ui'
|
|
6
|
-
|
|
7
|
-
defineProps<{ tabs: { id: string; label: string }[]; modelValue: string }>()
|
|
8
|
-
const emit = defineEmits<{ 'update:modelValue': [id: string] }>()
|
|
9
|
-
</script>
|
|
10
|
-
|
|
11
|
-
<template>
|
|
12
|
-
<AppTabNav
|
|
13
|
-
:tabs="tabs"
|
|
14
|
-
:model-value="modelValue"
|
|
15
|
-
variant="raised"
|
|
16
|
-
aria-label="DAO sections"
|
|
17
|
-
@update:model-value="emit('update:modelValue', $event)"
|
|
18
|
-
/>
|
|
19
|
-
</template>
|