@meddleware/dao-ui 0.1.0

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/src/config.ts ADDED
@@ -0,0 +1,28 @@
1
+ export type Network = 'testnet' | 'mainnet'
2
+
3
+ export const NETWORK: Network = (import.meta.env.VITE_NETWORK as Network) || 'testnet'
4
+
5
+ export const RPC_URLS: Record<Network, string> = {
6
+ testnet: import.meta.env.VITE_RPC_TESTNET || 'https://fullnode.testnet.sui.io:443',
7
+ mainnet: import.meta.env.VITE_RPC_MAINNET || 'https://fullnode.mainnet.sui.io:443',
8
+ }
9
+
10
+ /** Published access_gate package ID. Governs which events/objects are queried. */
11
+ export const ACCESS_GATE_PACKAGE_ID: Record<Network, string> = {
12
+ testnet:
13
+ import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_TESTNET ||
14
+ '0x0bedd0b27d993d3292ca6a5315f7562de8bc0ff3752b445b4c53252c76f2d20d',
15
+ mainnet: import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_MAINNET || '',
16
+ }
17
+
18
+ /** PlatformConfig shared object ID — holds commission_bps + treasury address. */
19
+ export const PLATFORM_CONFIG_ID: Record<Network, string> = {
20
+ testnet:
21
+ import.meta.env.VITE_PLATFORM_CONFIG_ID_TESTNET ||
22
+ '0x7c5aed0ce7f29a4dfb60657858df31c12410a67098b4bcdd1d8cb1e531be4884',
23
+ mainnet: import.meta.env.VITE_PLATFORM_CONFIG_ID_MAINNET || '',
24
+ }
25
+
26
+ export const PACKAGE_ID = ACCESS_GATE_PACKAGE_ID[NETWORK]
27
+ export const CONFIG_ID = PLATFORM_CONFIG_ID[NETWORK]
28
+ export const RPC_URL = RPC_URLS[NETWORK]
package/src/env.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ interface ImportMetaEnv {
4
+ readonly VITE_NETWORK?: string
5
+ readonly VITE_RPC_TESTNET?: string
6
+ readonly VITE_RPC_MAINNET?: string
7
+ readonly VITE_ACCESS_GATE_PACKAGE_ID_TESTNET?: string
8
+ readonly VITE_ACCESS_GATE_PACKAGE_ID_MAINNET?: string
9
+ readonly VITE_PLATFORM_CONFIG_ID_TESTNET?: string
10
+ readonly VITE_PLATFORM_CONFIG_ID_MAINNET?: string
11
+ }
12
+
13
+ interface ImportMeta {
14
+ readonly env: ImportMetaEnv
15
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { default as DaoView } from './DaoView.vue'
package/src/main.ts ADDED
@@ -0,0 +1,7 @@
1
+ import '@meddleware/design-tokens/tokens.css'
2
+ import '@meddleware/ui/base.css'
3
+ import './styles/qt.css'
4
+ import { createApp } from 'vue'
5
+ import App from './App.vue'
6
+
7
+ createApp(App).mount('#app')
@@ -0,0 +1,356 @@
1
+ /*
2
+ * Desktop-application (Bitcoin-Qt / Monero-Qt) aesthetic layer.
3
+ * Layered on top of @meddleware/design-tokens — uses CSS vars from the token set.
4
+ * Applied globally via main.ts; does not affect host documents when DaoView is embedded
5
+ * because the overrides are scoped to .dao-view and its descendants.
6
+ */
7
+
8
+ /* ── Layout shell ────────────────────────────────────────────────────────── */
9
+
10
+ .dao-view {
11
+ display: flex;
12
+ flex-direction: column;
13
+ min-height: 0;
14
+ flex: 1;
15
+ font-size: 0.875rem;
16
+ color: var(--text);
17
+ }
18
+
19
+ /* ── Toolbar ─────────────────────────────────────────────────────────────── */
20
+
21
+ .dao-toolbar {
22
+ display: flex;
23
+ align-items: center;
24
+ gap: 4px;
25
+ padding: 4px 8px;
26
+ border-bottom: 1px solid var(--border);
27
+ background: var(--surface);
28
+ flex-shrink: 0;
29
+ }
30
+
31
+ .dao-toolbar__btn {
32
+ display: inline-flex;
33
+ align-items: center;
34
+ gap: 5px;
35
+ padding: 3px 10px;
36
+ height: 26px;
37
+ font-size: 0.8rem;
38
+ background: var(--lift);
39
+ border: 1px solid var(--border);
40
+ border-radius: 2px;
41
+ color: var(--text);
42
+ cursor: pointer;
43
+ white-space: nowrap;
44
+ transition: background 0.1s;
45
+ }
46
+
47
+ .dao-toolbar__btn:hover {
48
+ background: var(--surface);
49
+ border-color: var(--accent);
50
+ }
51
+
52
+ .dao-toolbar__sep {
53
+ width: 1px;
54
+ height: 18px;
55
+ background: var(--border);
56
+ margin: 0 4px;
57
+ }
58
+
59
+ /* ── Tab bar ─────────────────────────────────────────────────────────────── */
60
+
61
+ .dao-tabs {
62
+ display: flex;
63
+ align-items: flex-end;
64
+ border-bottom: 1px solid var(--border);
65
+ background: var(--surface);
66
+ flex-shrink: 0;
67
+ overflow-x: auto;
68
+ scrollbar-width: none;
69
+ }
70
+
71
+ .dao-tabs::-webkit-scrollbar { display: none; }
72
+
73
+ .dao-tab {
74
+ padding: 7px 16px 6px;
75
+ font-size: 0.8rem;
76
+ font-weight: 500;
77
+ color: var(--muted);
78
+ background: transparent;
79
+ border: none;
80
+ border-bottom: 2px solid transparent;
81
+ cursor: pointer;
82
+ white-space: nowrap;
83
+ transition: color 0.1s, border-color 0.1s;
84
+ letter-spacing: 0.01em;
85
+ }
86
+
87
+ .dao-tab:hover {
88
+ color: var(--text);
89
+ }
90
+
91
+ .dao-tab--active {
92
+ color: var(--text);
93
+ border-bottom-color: var(--accent);
94
+ }
95
+
96
+ /* ── Tab content area ────────────────────────────────────────────────────── */
97
+
98
+ .dao-content {
99
+ flex: 1;
100
+ overflow-y: auto;
101
+ padding: 12px;
102
+ background: var(--bg);
103
+ }
104
+
105
+ /* ── Status bar ──────────────────────────────────────────────────────────── */
106
+
107
+ .dao-statusbar {
108
+ display: flex;
109
+ align-items: center;
110
+ gap: 16px;
111
+ padding: 3px 12px;
112
+ font-size: 0.72rem;
113
+ color: var(--muted);
114
+ background: var(--surface);
115
+ border-top: 1px solid var(--border);
116
+ flex-shrink: 0;
117
+ font-family: var(--font-mono);
118
+ }
119
+
120
+ .dao-statusbar__dot {
121
+ display: inline-block;
122
+ width: 7px;
123
+ height: 7px;
124
+ border-radius: 50%;
125
+ margin-right: 4px;
126
+ flex-shrink: 0;
127
+ }
128
+
129
+ .dao-statusbar__dot--ok { background: var(--ok); }
130
+ .dao-statusbar__dot--warn { background: #b08d2a; }
131
+ .dao-statusbar__dot--error { background: var(--danger); }
132
+
133
+ .dao-statusbar__item {
134
+ display: flex;
135
+ align-items: center;
136
+ }
137
+
138
+ .dao-statusbar__sep {
139
+ color: var(--border);
140
+ }
141
+
142
+ /* ── Panel ───────────────────────────────────────────────────────────────── */
143
+
144
+ .dao-panel {
145
+ border: 1px solid var(--border);
146
+ border-radius: 2px;
147
+ background: var(--surface);
148
+ overflow: hidden;
149
+ }
150
+
151
+ .dao-panel__head {
152
+ padding: 5px 10px;
153
+ font-size: 0.72rem;
154
+ font-weight: 600;
155
+ letter-spacing: 0.06em;
156
+ text-transform: uppercase;
157
+ color: var(--muted);
158
+ background: var(--lift);
159
+ border-bottom: 1px solid var(--border);
160
+ }
161
+
162
+ .dao-panel__body {
163
+ padding: 10px;
164
+ }
165
+
166
+ /* ── Data table ──────────────────────────────────────────────────────────── */
167
+
168
+ .dao-table {
169
+ width: 100%;
170
+ border-collapse: collapse;
171
+ font-size: 0.8rem;
172
+ }
173
+
174
+ .dao-table th {
175
+ text-align: left;
176
+ padding: 5px 8px;
177
+ font-size: 0.7rem;
178
+ font-weight: 600;
179
+ letter-spacing: 0.05em;
180
+ text-transform: uppercase;
181
+ color: var(--muted);
182
+ border-bottom: 1px solid var(--border);
183
+ white-space: nowrap;
184
+ }
185
+
186
+ .dao-table td {
187
+ padding: 5px 8px;
188
+ border-bottom: 1px solid var(--border);
189
+ vertical-align: middle;
190
+ }
191
+
192
+ .dao-table tr:last-child td {
193
+ border-bottom: none;
194
+ }
195
+
196
+ .dao-table tr:nth-child(even) td {
197
+ background: var(--lift);
198
+ }
199
+
200
+ /* ── Amount cells (monospace, right-aligned) ─────────────────────────────── */
201
+
202
+ .dao-amount {
203
+ font-family: var(--font-mono);
204
+ font-size: 0.8rem;
205
+ text-align: right;
206
+ white-space: nowrap;
207
+ }
208
+
209
+ .dao-amount--muted {
210
+ color: var(--muted);
211
+ }
212
+
213
+ /* ── Stat grid (Overview key/value pairs) ────────────────────────────────── */
214
+
215
+ .dao-stat-grid {
216
+ display: grid;
217
+ grid-template-columns: auto 1fr;
218
+ gap: 3px 12px;
219
+ font-size: 0.8rem;
220
+ }
221
+
222
+ .dao-stat-grid__label {
223
+ color: var(--muted);
224
+ white-space: nowrap;
225
+ }
226
+
227
+ .dao-stat-grid__value {
228
+ font-family: var(--font-mono);
229
+ color: var(--text);
230
+ text-align: right;
231
+ }
232
+
233
+ /* ── Two-column overview layout ──────────────────────────────────────────── */
234
+
235
+ .dao-overview-cols {
236
+ display: grid;
237
+ grid-template-columns: 1fr 1fr;
238
+ gap: 10px;
239
+ }
240
+
241
+ @media (max-width: 640px) {
242
+ .dao-overview-cols { grid-template-columns: 1fr; }
243
+ }
244
+
245
+ /* ── Proposal progress bar ───────────────────────────────────────────────── */
246
+
247
+ .dao-progress {
248
+ height: 6px;
249
+ background: var(--lift);
250
+ border: 1px solid var(--border);
251
+ border-radius: 1px;
252
+ overflow: hidden;
253
+ margin: 3px 0;
254
+ }
255
+
256
+ .dao-progress__fill {
257
+ height: 100%;
258
+ background: var(--accent);
259
+ transition: width 0.3s ease;
260
+ }
261
+
262
+ /* ── Activity feed ───────────────────────────────────────────────────────── */
263
+
264
+ .dao-feed {
265
+ list-style: none;
266
+ padding: 0;
267
+ margin: 0;
268
+ font-size: 0.78rem;
269
+ }
270
+
271
+ .dao-feed__item {
272
+ display: flex;
273
+ align-items: flex-start;
274
+ gap: 8px;
275
+ padding: 4px 0;
276
+ border-bottom: 1px solid var(--border);
277
+ color: var(--muted);
278
+ }
279
+
280
+ .dao-feed__item:last-child {
281
+ border-bottom: none;
282
+ }
283
+
284
+ .dao-feed__dot {
285
+ width: 6px;
286
+ height: 6px;
287
+ border-radius: 50%;
288
+ background: var(--accent);
289
+ margin-top: 4px;
290
+ flex-shrink: 0;
291
+ }
292
+
293
+ .dao-feed__type {
294
+ color: var(--text);
295
+ font-weight: 500;
296
+ white-space: nowrap;
297
+ }
298
+
299
+ .dao-feed__time {
300
+ margin-left: auto;
301
+ white-space: nowrap;
302
+ font-family: var(--font-mono);
303
+ font-size: 0.72rem;
304
+ }
305
+
306
+ /* ── Misc ────────────────────────────────────────────────────────────────── */
307
+
308
+ .dao-mono {
309
+ font-family: var(--font-mono);
310
+ font-size: 0.8rem;
311
+ }
312
+
313
+ .dao-muted {
314
+ color: var(--muted);
315
+ font-size: 0.78rem;
316
+ }
317
+
318
+ .dao-badge {
319
+ display: inline-block;
320
+ padding: 1px 6px;
321
+ border-radius: 2px;
322
+ font-size: 0.7rem;
323
+ font-weight: 600;
324
+ letter-spacing: 0.04em;
325
+ text-transform: uppercase;
326
+ }
327
+
328
+ .dao-badge--active { background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok); }
329
+ .dao-badge--closed { background: color-mix(in srgb, var(--muted) 20%, transparent); color: var(--muted); }
330
+ .dao-badge--pending { background: color-mix(in srgb, #b08d2a 20%, transparent); color: #b08d2a; }
331
+
332
+ .dao-section-title {
333
+ font-size: 0.72rem;
334
+ font-weight: 600;
335
+ letter-spacing: 0.06em;
336
+ text-transform: uppercase;
337
+ color: var(--muted);
338
+ margin: 0 0 6px;
339
+ }
340
+
341
+ .dao-notice {
342
+ padding: 8px 10px;
343
+ border: 1px solid #b08d2a;
344
+ border-radius: 2px;
345
+ background: color-mix(in srgb, #b08d2a 10%, transparent);
346
+ color: #b08d2a;
347
+ font-size: 0.78rem;
348
+ margin-bottom: 10px;
349
+ }
350
+
351
+ .dao-placeholder {
352
+ text-align: center;
353
+ padding: 24px 16px;
354
+ color: var(--muted);
355
+ font-size: 0.8rem;
356
+ }
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import Panel from '../components/Panel.vue'
4
+ import { usePlatformConfig } from '../composables/usePlatformConfig.js'
5
+
6
+ const { config } = usePlatformConfig()
7
+
8
+ const commissionPct = computed(() =>
9
+ config.value ? (config.value.commissionBps / 100).toFixed(2) + '%' : '—',
10
+ )
11
+ </script>
12
+
13
+ <template>
14
+ <div style="display: flex; flex-direction: column; gap: 10px">
15
+ <Panel title="On-Chain Parameters (access_gate)">
16
+ <div class="dao-stat-grid" style="max-width: 420px">
17
+ <span class="dao-stat-grid__label">Commission rate</span>
18
+ <span class="dao-stat-grid__value dao-mono">{{ commissionPct }}</span>
19
+
20
+ <span class="dao-stat-grid__label">Max commission cap</span>
21
+ <span class="dao-stat-grid__value dao-mono">10.00% (1000 bps — on-chain hard cap)</span>
22
+
23
+ <span class="dao-stat-grid__label">Treasury address</span>
24
+ <span
25
+ class="dao-stat-grid__value dao-mono"
26
+ style="font-size: 0.72rem; text-align: left; word-break: break-all"
27
+ >
28
+ {{ config?.treasury ?? '—' }}
29
+ </span>
30
+ </div>
31
+
32
+ <p class="dao-muted" style="margin: 10px 0 0; font-size: 0.72rem">
33
+ Parameters are governed by <code>PlatformAdminCap</code> on-chain.
34
+ Commission is split on every NFT purchase: <code>commission_bps / 10000 × price</code>
35
+ routes to treasury; the remainder goes to the gate operator.
36
+ </p>
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.
43
+ </p>
44
+ </Panel>
45
+
46
+ <Panel title="Admin Actions">
47
+ <p class="dao-muted" style="margin: 0; font-size: 0.78rem">
48
+ Privileged actions (update commission, change treasury, pause gates) require the
49
+ <code>PlatformAdminCap</code> or <code>DaoAdminCap</code>.
50
+ Connect a wallet holding the relevant capability to enable these controls.
51
+ </p>
52
+ </Panel>
53
+ </div>
54
+ </template>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import DataTable from '../components/DataTable.vue'
3
+ import { useDaoEvents } from '../composables/useDaoEvents.js'
4
+ import type { DaoEvent } from '../composables/useDaoEvents.js'
5
+
6
+ const { events, loading, error, reload } = useDaoEvents(50)
7
+
8
+ const eventLabel: Record<DaoEvent['type'], string> = {
9
+ AccessMinted: 'Access Sold',
10
+ AccessConsumed: 'Access Used',
11
+ GateCreated: 'Gate Created',
12
+ AccessBurned: 'Access Burned',
13
+ }
14
+
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
+ </script>
29
+
30
+ <template>
31
+ <div>
32
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px">
33
+ <p class="dao-section-title" style="margin: 0">On-Chain Events</p>
34
+ <button class="dao-toolbar__btn" :disabled="loading" @click="reload">
35
+ {{ loading ? 'Loading…' : 'Refresh' }}
36
+ </button>
37
+ </div>
38
+
39
+ <p v-if="error" class="dao-muted">{{ error }}</p>
40
+
41
+ <DataTable v-if="events.length || loading" :empty="'No events'">
42
+ <template #head>
43
+ <th>Type</th>
44
+ <th>Address</th>
45
+ <th>Time (UTC)</th>
46
+ <th>Tx</th>
47
+ </template>
48
+ <tr v-for="ev in events" :key="ev.txDigest + ev.timestampMs">
49
+ <td>
50
+ <span
51
+ class="dao-badge"
52
+ :class="{
53
+ 'dao-badge--active': ev.type === 'AccessMinted',
54
+ 'dao-badge--pending': ev.type === 'GateCreated',
55
+ 'dao-badge--closed': ev.type === 'AccessConsumed' || ev.type === 'AccessBurned',
56
+ }"
57
+ >{{ eventLabel[ev.type] }}</span>
58
+ </td>
59
+ <td class="dao-mono" style="font-size: 0.72rem">{{ shortAddr(ev.address) }}</td>
60
+ <td class="dao-mono" style="font-size: 0.72rem">{{ shortTs(ev.timestampMs) }}</td>
61
+ <td class="dao-mono" style="font-size: 0.72rem">
62
+ <a :href="explorerUrl(ev.txDigest)" target="_blank" rel="noopener" style="color: var(--accent)">
63
+ {{ ev.txDigest.slice(0, 8) }}…
64
+ </a>
65
+ </td>
66
+ </tr>
67
+ </DataTable>
68
+ <p v-else-if="!loading" class="dao-placeholder">No events found.</p>
69
+ </div>
70
+ </template>
@@ -0,0 +1,88 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import Panel from '../components/Panel.vue'
4
+ import AmountCell from '../components/AmountCell.vue'
5
+ import { usePlatformConfig } from '../composables/usePlatformConfig.js'
6
+ import { useTreasury } from '../composables/useTreasury.js'
7
+ import { useDaoEvents } from '../composables/useDaoEvents.js'
8
+ import type { DaoEvent } from '../composables/useDaoEvents.js'
9
+
10
+ const { config } = usePlatformConfig()
11
+ const { balance } = useTreasury(() => config.value?.treasury ?? null)
12
+ 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
+ }
21
+
22
+ const eventLabel: Record<DaoEvent['type'], string> = {
23
+ AccessMinted: 'Access sold',
24
+ AccessConsumed: 'Access used',
25
+ GateCreated: 'Gate created',
26
+ AccessBurned: 'Access burned',
27
+ }
28
+
29
+ const commissionPct = computed(() =>
30
+ config.value ? (config.value.commissionBps / 100).toFixed(2) + '%' : '—',
31
+ )
32
+ </script>
33
+
34
+ <template>
35
+ <div class="dao-overview-cols">
36
+ <!-- Left: treasury summary -->
37
+ <div style="display: flex; flex-direction: column; gap: 8px">
38
+ <Panel title="Treasury Balance">
39
+ <div class="dao-stat-grid">
40
+ <span class="dao-stat-grid__label">SUI balance</span>
41
+ <AmountCell class="dao-stat-grid__value" :mist="balance" />
42
+
43
+ <span class="dao-stat-grid__label">Commission rate</span>
44
+ <span class="dao-stat-grid__value dao-mono">{{ commissionPct }}</span>
45
+
46
+ <span class="dao-stat-grid__label">Treasury address</span>
47
+ <span
48
+ class="dao-stat-grid__value dao-mono"
49
+ style="font-size: 0.7rem; word-break: break-all; text-align: left"
50
+ :title="config?.treasury"
51
+ >
52
+ {{ config?.treasury ? config.treasury.slice(0, 10) + '…' + config.treasury.slice(-6) : '—' }}
53
+ </span>
54
+ </div>
55
+ </Panel>
56
+
57
+ <Panel title="Platform Stats">
58
+ <div class="dao-stat-grid">
59
+ <span class="dao-stat-grid__label">Strategy NAV</span>
60
+ <span class="dao-stat-grid__value dao-muted">—</span>
61
+
62
+ <span class="dao-stat-grid__label">Buyback/burn</span>
63
+ <span class="dao-stat-grid__value dao-muted">—</span>
64
+
65
+ <span class="dao-stat-grid__label">Fee distributor</span>
66
+ <span class="dao-stat-grid__value dao-muted">—</span>
67
+ </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
+ </Panel>
73
+ </div>
74
+
75
+ <!-- Right: recent activity -->
76
+ <Panel title="Recent Activity">
77
+ <p v-if="eventsLoading" class="dao-muted" style="margin: 0">Loading events…</p>
78
+ <ul v-else-if="events.length" class="dao-feed">
79
+ <li v-for="ev in events" :key="ev.txDigest + ev.timestampMs" class="dao-feed__item">
80
+ <span class="dao-feed__dot" />
81
+ <span class="dao-feed__type">{{ eventLabel[ev.type] }}</span>
82
+ <span class="dao-feed__time">{{ timeAgo(ev.timestampMs) }}</span>
83
+ </li>
84
+ </ul>
85
+ <p v-else class="dao-placeholder">No recent events.</p>
86
+ </Panel>
87
+ </div>
88
+ </template>
@@ -0,0 +1,25 @@
1
+ <script setup lang="ts">
2
+ import ProposalRow from '../components/ProposalRow.vue'
3
+ import { useProposals } from '../composables/useProposals.js'
4
+ import { useEpoch } from '../composables/useEpoch.js'
5
+
6
+ const { proposals } = useProposals()
7
+ const { epoch } = useEpoch()
8
+ </script>
9
+
10
+ <template>
11
+ <div>
12
+ <div class="dao-notice">
13
+ Governance proposals are currently scaffolded with illustrative data.
14
+ On-chain voting and contributions will be enabled when <code>vault_dao</code> is deployed.
15
+ </div>
16
+
17
+ <p class="dao-section-title">Active &amp; Pending Proposals</p>
18
+ <ProposalRow
19
+ v-for="p in proposals"
20
+ :key="p.id"
21
+ :proposal="p"
22
+ :current-epoch="epoch"
23
+ />
24
+ </div>
25
+ </template>