@meddleware/dao-ui 0.1.18 → 0.1.20

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/styles/qt.css CHANGED
@@ -1,11 +1,16 @@
1
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.
2
+ * dao-ui console — app-local utility classes.
3
+ *
4
+ * The structural primitives (panel, toolbar, status bar, data table, stat grid,
5
+ * badge, activity feed) now come from @meddleware/ui as scoped components
6
+ * (UiPanel, UiToolbar, UiStatusBar, UiDataTable, UiStatGrid/UiStatRow, UiBadge,
7
+ * UiActivityFeed/UiActivityItem). What remains here are small text/layout
8
+ * utilities that are specific to this console's tabs.
9
+ *
10
+ * Applied globally via DaoView.vue (so it works when embedded as a library).
6
11
  */
7
12
 
8
- /* ── Layout shell ────────────────────────────────────────────────────────── */
13
+ /* ── Shell layout ────────────────────────────────────────────────────────── */
9
14
 
10
15
  .dao-view {
11
16
  display: flex;
@@ -16,48 +21,6 @@
16
21
  color: var(--text);
17
22
  }
18
23
 
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 content area ────────────────────────────────────────────────────── */
60
-
61
24
  .dao-content {
62
25
  flex: 1;
63
26
  overflow-y: auto;
@@ -65,108 +28,10 @@
65
28
  background: var(--bg);
66
29
  }
67
30
 
68
- /* ── Status bar ──────────────────────────────────────────────────────────── */
69
-
70
- .dao-statusbar {
71
- display: flex;
72
- align-items: center;
73
- gap: 16px;
74
- padding: 3px 12px;
75
- font-size: 0.72rem;
76
- color: var(--muted);
77
- background: var(--surface);
78
- border-top: 1px solid var(--border);
79
- flex-shrink: 0;
80
- font-family: var(--font-mono);
81
- position: sticky;
82
- bottom: 0;
83
- z-index: 1;
84
- }
85
-
86
- .dao-statusbar__dot {
87
- display: inline-block;
88
- width: 7px;
89
- height: 7px;
90
- border-radius: 50%;
91
- margin-right: 4px;
92
- flex-shrink: 0;
93
- }
94
-
95
- .dao-statusbar__dot--ok { background: var(--ok); }
96
- .dao-statusbar__dot--warn { background: #b08d2a; }
97
- .dao-statusbar__dot--error { background: var(--danger); }
98
-
99
- .dao-statusbar__item {
100
- display: flex;
101
- align-items: center;
102
- }
103
-
104
- .dao-statusbar__sep {
105
- color: var(--border);
106
- }
107
-
108
- /* ── Panel ───────────────────────────────────────────────────────────────── */
109
-
110
- .dao-panel {
111
- border: 1px solid var(--border);
112
- border-radius: 2px;
113
- background: var(--surface);
114
- overflow: hidden;
115
- }
116
-
117
- .dao-panel__head {
118
- padding: 5px 10px;
119
- font-size: 0.72rem;
120
- font-weight: 600;
121
- letter-spacing: 0.06em;
122
- text-transform: uppercase;
123
- color: var(--muted);
124
- background: var(--lift);
125
- border-bottom: 1px solid var(--border);
126
- }
127
-
128
- .dao-panel__body {
129
- padding: 10px;
130
- }
131
-
132
- /* ── Data table ──────────────────────────────────────────────────────────── */
133
-
134
- .dao-table {
135
- width: 100%;
136
- border-collapse: collapse;
137
- font-size: 0.8rem;
138
- }
139
-
140
- .dao-table th {
141
- text-align: left;
142
- padding: 5px 8px;
143
- font-size: 0.7rem;
144
- font-weight: 600;
145
- letter-spacing: 0.05em;
146
- text-transform: uppercase;
147
- color: var(--muted);
148
- border-bottom: 1px solid var(--border);
149
- white-space: nowrap;
150
- }
151
-
152
- .dao-table td {
153
- padding: 5px 8px;
154
- border-bottom: 1px solid var(--border);
155
- vertical-align: middle;
156
- }
157
-
158
- .dao-table tr:last-child td {
159
- border-bottom: none;
160
- }
161
-
162
- .dao-table tr:nth-child(even) td {
163
- background: var(--lift);
164
- }
165
-
166
31
  /* ── Amount cells (monospace, right-aligned) ─────────────────────────────── */
167
32
 
168
33
  .dao-amount {
169
- font-family: var(--font-mono);
34
+ font-family: var(--mw-font-mono);
170
35
  font-size: 0.8rem;
171
36
  text-align: right;
172
37
  white-space: nowrap;
@@ -176,26 +41,6 @@
176
41
  color: var(--muted);
177
42
  }
178
43
 
179
- /* ── Stat grid (Overview key/value pairs) ────────────────────────────────── */
180
-
181
- .dao-stat-grid {
182
- display: grid;
183
- grid-template-columns: auto 1fr;
184
- gap: 3px 12px;
185
- font-size: 0.8rem;
186
- }
187
-
188
- .dao-stat-grid__label {
189
- color: var(--muted);
190
- white-space: nowrap;
191
- }
192
-
193
- .dao-stat-grid__value {
194
- font-family: var(--font-mono);
195
- color: var(--text);
196
- text-align: right;
197
- }
198
-
199
44
  /* ── Two-column overview layout ──────────────────────────────────────────── */
200
45
 
201
46
  .dao-overview-cols {
@@ -204,69 +49,38 @@
204
49
  gap: 10px;
205
50
  }
206
51
 
207
- @media (max-width: 640px) {
208
- .dao-overview-cols { grid-template-columns: 1fr; }
52
+ @media (width <= 640px) {
53
+ .dao-overview-cols {
54
+ grid-template-columns: 1fr;
55
+ }
209
56
  }
210
57
 
211
58
  /* ── Proposal progress bar ───────────────────────────────────────────────── */
212
59
 
60
+ /* A native <progress>: engines paint the track/value with their own pseudo-elements. */
213
61
  .dao-progress {
62
+ display: block;
63
+ width: 100%;
214
64
  height: 6px;
65
+ margin: 3px 0;
66
+ appearance: none;
215
67
  background: var(--lift);
216
68
  border: 1px solid var(--border);
217
69
  border-radius: 1px;
218
70
  overflow: hidden;
219
- margin: 3px 0;
220
71
  }
221
72
 
222
- .dao-progress__fill {
223
- height: 100%;
224
- background: var(--accent);
225
- transition: width 0.3s ease;
226
- }
227
-
228
- /* ── Activity feed ───────────────────────────────────────────────────────── */
229
-
230
- .dao-feed {
231
- list-style: none;
232
- padding: 0;
233
- margin: 0;
234
- font-size: 0.78rem;
235
- }
236
-
237
- .dao-feed__item {
238
- display: flex;
239
- align-items: flex-start;
240
- gap: 8px;
241
- padding: 4px 0;
242
- border-bottom: 1px solid var(--border);
243
- color: var(--muted);
244
- }
245
-
246
- .dao-feed__item:last-child {
247
- border-bottom: none;
73
+ .dao-progress::-webkit-progress-bar {
74
+ background: var(--lift);
248
75
  }
249
76
 
250
- .dao-feed__dot {
251
- width: 6px;
252
- height: 6px;
253
- border-radius: 50%;
77
+ .dao-progress::-webkit-progress-value {
254
78
  background: var(--accent);
255
- margin-top: 4px;
256
- flex-shrink: 0;
257
- }
258
-
259
- .dao-feed__type {
260
- color: var(--text);
261
- font-weight: 500;
262
- white-space: nowrap;
79
+ transition: width 0.3s ease;
263
80
  }
264
81
 
265
- .dao-feed__time {
266
- margin-left: auto;
267
- white-space: nowrap;
268
- font-family: var(--font-mono);
269
- font-size: 0.72rem;
82
+ .dao-progress::-moz-progress-bar {
83
+ background: var(--accent);
270
84
  }
271
85
 
272
86
  /* ── Info tooltip marker ─────────────────────────────────────────────────── */
@@ -299,31 +113,47 @@
299
113
  margin-top: 10px;
300
114
  }
301
115
 
302
- /* ── Misc ────────────────────────────────────────────────────────────────── */
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
+
134
+ /* ── Misc text utilities ─────────────────────────────────────────────────── */
303
135
 
304
136
  .dao-mono {
305
- font-family: var(--font-mono);
137
+ font-family: var(--mw-font-mono);
306
138
  font-size: 0.8rem;
307
139
  }
308
140
 
309
- .dao-muted {
310
- color: var(--muted);
311
- font-size: 0.78rem;
141
+ .dao-mono--sm {
142
+ font-size: 0.7rem;
312
143
  }
313
144
 
314
- .dao-badge {
315
- display: inline-block;
316
- padding: 1px 6px;
317
- border-radius: 2px;
318
- font-size: 0.7rem;
319
- font-weight: 600;
320
- letter-spacing: 0.04em;
321
- text-transform: uppercase;
145
+ .dao-nowrap {
146
+ white-space: nowrap;
147
+ }
148
+
149
+ .dao-flush {
150
+ margin: 0;
322
151
  }
323
152
 
324
- .dao-badge--active { background: color-mix(in srgb, var(--ok) 20%, transparent); color: var(--ok); }
325
- .dao-badge--closed { background: color-mix(in srgb, var(--muted) 20%, transparent); color: var(--muted); }
326
- .dao-badge--pending { background: color-mix(in srgb, #b08d2a 20%, transparent); color: #b08d2a; }
153
+ .dao-muted {
154
+ color: var(--muted);
155
+ font-size: 0.78rem;
156
+ }
327
157
 
328
158
  .dao-section-title {
329
159
  font-size: 0.72rem;
@@ -336,10 +166,10 @@
336
166
 
337
167
  .dao-notice {
338
168
  padding: 8px 10px;
339
- border: 1px solid #b08d2a;
169
+ border: 1px solid var(--warning);
340
170
  border-radius: 2px;
341
- background: color-mix(in srgb, #b08d2a 10%, transparent);
342
- color: #b08d2a;
171
+ background: color-mix(in srgb, var(--warning) 10%, transparent);
172
+ color: var(--warning);
343
173
  font-size: 0.78rem;
344
174
  margin-bottom: 10px;
345
175
  }
@@ -1,10 +1,16 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, watch, computed } from 'vue'
3
- import Panel from '../components/Panel.vue'
4
3
  import { usePlatformConfig } from '../composables/usePlatformConfig.js'
5
4
  import { useWallet, getSuiClient } from '../wallet.js'
6
5
  import { PACKAGE_ID } from '../config.js'
7
- import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
6
+ import {
7
+ CopyableAddress,
8
+ ExplorerLink,
9
+ suiExplorerUrl,
10
+ UiPanel,
11
+ UiStatGrid,
12
+ UiStatRow,
13
+ } from '@meddleware/ui'
8
14
  import { NETWORK } from '../config.js'
9
15
 
10
16
  const { config } = usePlatformConfig()
@@ -44,60 +50,55 @@ watch(
44
50
  </script>
45
51
 
46
52
  <template>
47
- <div style="display: flex; flex-direction: column; gap: 10px">
48
- <Panel title="Platform Parameters">
49
- <div class="dao-stat-grid" style="max-width: 480px">
50
- <span class="dao-stat-grid__label">Commission rate</span>
51
- <span class="dao-stat-grid__value dao-mono">{{ commissionPct }}</span>
52
-
53
- <span class="dao-stat-grid__label">Max commission cap</span>
54
- <span class="dao-stat-grid__value dao-mono">10.00% (1000 bps — on-chain hard cap)</span>
55
-
56
- <span class="dao-stat-grid__label">Treasury address</span>
57
- <span class="dao-stat-grid__value" style="text-align: left">
53
+ <div class="dao-stack gov">
54
+ <UiPanel title="Platform Parameters">
55
+ <UiStatGrid class="dao-stat-grid">
56
+ <UiStatRow label="Commission rate">{{ commissionPct }}</UiStatRow>
57
+ <UiStatRow label="Max commission cap">10.00% (1000 bps — on-chain hard cap)</UiStatRow>
58
+ <UiStatRow label="Treasury address" align="left">
58
59
  <CopyableAddress v-if="config?.treasury" :address="config.treasury">
59
60
  <ExplorerLink :href="suiExplorerUrl('account', config.treasury, NETWORK)" :value="config.treasury" />
60
61
  </CopyableAddress>
61
- <span v-else class="dao-mono" style="font-size: 0.7rem">—</span>
62
- </span>
63
- </div>
62
+ <span v-else class="dao-mono dao-mono--sm">—</span>
63
+ </UiStatRow>
64
+ </UiStatGrid>
64
65
 
65
- <p class="dao-muted" style="margin: 10px 0 0; font-size: 0.72rem">
66
+ <p class="dao-muted gov__footnote">
66
67
  Parameters are governed by <code>PlatformAdminCap</code> on-chain.
67
68
  Commission is charged on every NFT access purchase:
68
69
  <code>commission_bps / 10000 × price</code> routes to treasury;
69
70
  the remainder goes to the gate operator.
70
71
  </p>
71
- </Panel>
72
+ </UiPanel>
72
73
 
73
- <Panel title="Admin Actions">
74
+ <UiPanel title="Admin Actions">
74
75
  <!-- Not connected -->
75
76
  <template v-if="!account">
76
- <p class="dao-muted" style="margin: 0; font-size: 0.78rem">
77
+ <p class="dao-muted">
77
78
  Connect a wallet via the header to manage platform settings.
78
79
  </p>
79
80
  </template>
80
81
 
81
82
  <!-- Connected, checking -->
82
83
  <template v-else-if="capState === 'checking'">
83
- <p class="dao-muted" style="margin: 0; font-size: 0.78rem">
84
+ <p class="dao-muted">
84
85
  Checking capabilities for {{ account.address.slice(0, 10) }}…
85
86
  </p>
86
87
  </template>
87
88
 
88
89
  <!-- Connected, has PlatformAdminCap -->
89
90
  <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>
91
+ <p class="dao-muted">
92
+ <span class="gov__ok" aria-hidden="true">●</span>
92
93
  <code>PlatformAdminCap</code> detected. Admin controls will appear here when implemented.
93
94
  </p>
94
95
  </template>
95
96
 
96
97
  <!-- Connected, no cap -->
97
98
  <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>
99
+ <p class="dao-muted">
100
+ <span aria-hidden="true">●</span>
101
+ Connected: {{ account.address.slice(0, 10) }}…{{ account.address.slice(-6) }}<br />
101
102
  This wallet does not hold <code>PlatformAdminCap</code> or <code>DaoAdminCap</code>.
102
103
  Privileged actions are not available.
103
104
  </p>
@@ -105,10 +106,23 @@ watch(
105
106
 
106
107
  <!-- Error -->
107
108
  <template v-else-if="capState === 'error'">
108
- <p class="dao-muted" style="margin: 0; font-size: 0.78rem">
109
+ <p class="dao-muted">
109
110
  Unable to verify capabilities. Check your connection and try again.
110
111
  </p>
111
112
  </template>
112
- </Panel>
113
+ </UiPanel>
113
114
  </div>
114
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>
@@ -2,9 +2,15 @@
2
2
  // History tab: paginated on-chain access-gate event log (sold / used / burned) with
3
3
  // per-row explorer links. Data comes from useDaoEvents (pruning-tolerant, merged feed).
4
4
  import { ref, computed } from 'vue'
5
- import DataTable from '../components/DataTable.vue'
6
5
  import { useDaoEvents } from '../composables/useDaoEvents.js'
7
- import { CopyableAddress, ExplorerLink, suiExplorerUrl } from '@meddleware/ui'
6
+ import {
7
+ CopyableAddress,
8
+ ExplorerLink,
9
+ suiExplorerUrl,
10
+ UiDataTable,
11
+ UiBadge,
12
+ UiToolbarButton,
13
+ } from '@meddleware/ui'
8
14
  import { NETWORK } from '../config.js'
9
15
  import type { DaoEvent } from '../composables/useDaoEvents.js'
10
16
 
@@ -27,67 +33,78 @@ const eventLabel: Record<DaoEvent['type'], string> = {
27
33
  AccessBurned: 'Access Burned',
28
34
  }
29
35
 
36
+ function badgeVariant(type: DaoEvent['type']): 'active' | 'closed' {
37
+ return type === 'AccessMinted' ? 'active' : 'closed'
38
+ }
30
39
  </script>
31
40
 
32
41
  <template>
33
- <div>
34
- <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px">
35
- <p class="dao-section-title" style="margin: 0">On-Chain Events</p>
36
- <button class="dao-toolbar__btn" :disabled="loading" @click="reload">
37
- {{ loading ? 'Loading…' : 'Refresh' }}
38
- </button>
39
- </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>
40
48
 
41
- <p v-if="error" class="dao-muted">{{ error }}</p>
49
+ <p v-if="error" class="dao-muted">{{ error }}</p>
42
50
 
43
- <DataTable v-if="pageEvents.length || loading" :empty="'No events'">
44
- <template #head>
45
- <th>Type</th>
46
- <th>
47
- Address
48
- <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>
49
- </th>
50
- <th>
51
- Block
52
- <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>
53
- </th>
54
- <th>
55
- Transaction
56
- <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>
57
- </th>
58
- </template>
59
- <tr v-for="ev in pageEvents" :key="ev.txDigest">
60
- <td>
61
- <span
62
- class="dao-badge"
63
- :class="{
64
- 'dao-badge--active': ev.type === 'AccessMinted',
65
- 'dao-badge--closed': ev.type === 'AccessConsumed' || ev.type === 'AccessBurned',
66
- }"
67
- >{{ eventLabel[ev.type] }}</span>
68
- </td>
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>
75
- <td class="dao-mono" style="font-size: 0.72rem">
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>
82
- </td>
83
- </tr>
84
- </DataTable>
85
- <p v-else-if="!loading" class="dao-placeholder">No events found.</p>
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>
86
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>
92
- </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>
93
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>