@meddleware/treasury-ui 0.0.2 → 0.0.4
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 +5 -4
- package/src/App.vue +2 -2
- package/src/TreasuryView.vue +2 -2
- package/src/composables/chainContext.ts +24 -0
- package/src/composables/useEpoch.ts +3 -3
- package/src/composables/useGates.ts +21 -47
- package/src/composables/usePlatformConfig.ts +16 -17
- package/src/composables/useTreasuryActivity.ts +42 -47
- package/src/config.ts +20 -26
- package/src/env.d.ts +5 -6
- package/src/main.ts +6 -0
- package/src/styles/qt.css +1 -1
- package/src/tabs/AccountsTab.vue +3 -3
- package/src/tabs/ActivityTab.vue +6 -6
- package/src/tabs/OverviewTab.vue +2 -3
- package/vite.config.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/treasury-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"homepage": "https://docs.meddleware.co.uk/treasury/",
|
|
5
5
|
"description": "Meddleware treasury console — organisation-level accounting across revenue streams (Sui and beyond). Read-only, desktop-console UI built on @meddleware/ui.",
|
|
6
6
|
"author": "Meddleware <dev@meddleware.co.uk>",
|
|
@@ -44,10 +44,11 @@
|
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
+
"@meddleware/access-gate-client": "^0.0.1",
|
|
47
48
|
"@meddleware/design-tokens": "^0.1.7",
|
|
48
49
|
"@meddleware/ui": "^0.1.28",
|
|
49
|
-
"@meddleware/wallet-adapter": "^0.0.
|
|
50
|
-
"@mysten/sui": "^2.33.
|
|
50
|
+
"@meddleware/wallet-adapter": "^0.0.11",
|
|
51
|
+
"@mysten/sui": "^2.33.1",
|
|
51
52
|
"vue": "^3.5.43"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
"stylelint-config-standard": "^40.0.0",
|
|
70
71
|
"typescript": "~6.0.0",
|
|
71
72
|
"vite": "^8.3.0",
|
|
72
|
-
"vitest": "~5.0.
|
|
73
|
+
"vitest": "~5.0.2",
|
|
73
74
|
"vue-eslint-parser": "^10.4.1",
|
|
74
75
|
"vue-tsc": "~3.3.11"
|
|
75
76
|
},
|
package/src/App.vue
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Standalone shell for the Treasury SPA. The core UI lives in TreasuryView.vue (also
|
|
3
3
|
// exported for inline embedding in the dashboard).
|
|
4
4
|
import { AppHeader, AppFooter, ColorModeControl, CopyrightLine, useColorMode } from '@meddleware/ui'
|
|
5
|
-
import {
|
|
5
|
+
import { network } from './config.js'
|
|
6
6
|
import TreasuryView from './TreasuryView.vue'
|
|
7
7
|
|
|
8
8
|
const { mode, set } = useColorMode('dark')
|
|
@@ -17,7 +17,7 @@ const DEV_URL = import.meta.env.VITE_DEV_URL || 'https://dev.meddleware.co.uk/tr
|
|
|
17
17
|
<h1 class="brand-title">Meddleware Treasury</h1>
|
|
18
18
|
</template>
|
|
19
19
|
<template #actions>
|
|
20
|
-
<span class="network-badge">{{
|
|
20
|
+
<span class="network-badge">{{ network }}</span>
|
|
21
21
|
<ColorModeControl :model-value="mode" @update:model-value="set" />
|
|
22
22
|
</template>
|
|
23
23
|
</AppHeader>
|
package/src/TreasuryView.vue
CHANGED
|
@@ -17,7 +17,7 @@ import AccountsTab from './tabs/AccountsTab.vue'
|
|
|
17
17
|
import ActivityTab from './tabs/ActivityTab.vue'
|
|
18
18
|
import { useTreasuryActivity } from './composables/useTreasuryActivity.js'
|
|
19
19
|
import { useEpoch } from './composables/useEpoch.js'
|
|
20
|
-
import {
|
|
20
|
+
import { network } from './config.js'
|
|
21
21
|
|
|
22
22
|
const TABS: AppTab[] = [
|
|
23
23
|
{ id: 'overview', label: 'Overview' },
|
|
@@ -51,6 +51,6 @@ const { epoch } = useEpoch()
|
|
|
51
51
|
<component :is="activeComponent" />
|
|
52
52
|
</UiTabPanel>
|
|
53
53
|
|
|
54
|
-
<UiStatusBar :network="
|
|
54
|
+
<UiStatusBar :network="network" :healthy="!eventsError" :epoch="epoch" :last-refresh="lastRefresh" />
|
|
55
55
|
</div>
|
|
56
56
|
</template>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getCurrentInstance, onMounted, watch } from 'vue'
|
|
2
|
+
import { network } from '../config.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Load on mount and again whenever the network changes, clearing the previous network's data
|
|
6
|
+
* first. Only inside a component (unit tests call `reload()` directly).
|
|
7
|
+
*/
|
|
8
|
+
export function onChainContext(load: () => Promise<void>, reset: () => void): void {
|
|
9
|
+
if (!getCurrentInstance()) return
|
|
10
|
+
onMounted(load)
|
|
11
|
+
watch(network, () => {
|
|
12
|
+
reset()
|
|
13
|
+
void load()
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** A guard against a slower, older load overwriting a newer one. */
|
|
18
|
+
export function latest(): () => () => boolean {
|
|
19
|
+
let generation = 0
|
|
20
|
+
return () => {
|
|
21
|
+
const mine = ++generation
|
|
22
|
+
return () => mine === generation
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { ref
|
|
1
|
+
import { ref } from 'vue'
|
|
2
2
|
import { getSuiClient } from '../wallet.js'
|
|
3
|
+
import { onChainContext } from './chainContext.js'
|
|
3
4
|
|
|
4
5
|
export function useEpoch() {
|
|
5
6
|
const epoch = ref<number | null>(null)
|
|
@@ -14,8 +15,7 @@ export function useEpoch() {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
if (getCurrentInstance()) onMounted(load)
|
|
18
|
+
onChainContext(load, () => (epoch.value = null))
|
|
19
19
|
|
|
20
20
|
return { epoch, reload: load }
|
|
21
21
|
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
// Gates the platform treasury administers: its AdminCaps (resilient to event pruning), each merged
|
|
2
|
+
// with its Gate. A gate that cannot be read is left out rather than failing the list.
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { fetchAdminCaps, fetchGate, fetchPlatformConfig } from '@meddleware/access-gate-client'
|
|
2
5
|
import { getSuiClient } from '../wallet.js'
|
|
3
|
-
import {
|
|
6
|
+
import { requireDeployment } from '../config.js'
|
|
7
|
+
import { latest, onChainContext } from './chainContext.js'
|
|
4
8
|
|
|
5
9
|
export interface Gate {
|
|
6
10
|
id: string
|
|
@@ -14,64 +18,34 @@ export function useGates() {
|
|
|
14
18
|
const gates = ref<Gate[]>([])
|
|
15
19
|
const loading = ref(false)
|
|
16
20
|
const error = ref<string | null>(null)
|
|
21
|
+
const begin = latest()
|
|
17
22
|
|
|
18
23
|
async function load() {
|
|
19
|
-
|
|
24
|
+
const current = begin()
|
|
20
25
|
loading.value = true
|
|
21
26
|
error.value = null
|
|
22
27
|
try {
|
|
28
|
+
const d = requireDeployment()
|
|
23
29
|
const client = getSuiClient()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
gates.value = []
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// List AdminCap objects owned by treasury — one per gate the treasury controls.
|
|
37
|
-
const { objects } = await client.listOwnedObjects({
|
|
38
|
-
owner: treasury,
|
|
39
|
-
type: `${PACKAGE_ID}::access_gate::AdminCap`,
|
|
40
|
-
include: { json: true },
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
// Fetch each Gate shared object referenced by the AdminCap's gate_id.
|
|
44
|
-
const gateList = await Promise.all(
|
|
45
|
-
(objects ?? []).map(async (o: { json?: { gate_id?: unknown } | null; object?: { json?: { gate_id?: unknown } | null } | null }): Promise<Gate | null> => {
|
|
46
|
-
const capFields = o?.json ?? o?.object?.json
|
|
47
|
-
const gateId = String(capFields?.gate_id ?? '')
|
|
48
|
-
if (!gateId || gateId === 'undefined') return null
|
|
49
|
-
try {
|
|
50
|
-
const gateRes = await client.getObject({ objectId: gateId, include: { json: true } })
|
|
51
|
-
const f = gateRes.object?.json as Record<string, unknown> | null
|
|
52
|
-
if (!f) return null
|
|
53
|
-
return {
|
|
54
|
-
id: gateId,
|
|
55
|
-
name: String(f.nft_name ?? 'Unnamed Gate'),
|
|
56
|
-
price: BigInt(String(f.price_mist ?? '0')),
|
|
57
|
-
paused: Boolean(f.paused),
|
|
58
|
-
frozen: Boolean(f.frozen),
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
return null
|
|
62
|
-
}
|
|
63
|
-
}),
|
|
30
|
+
const { treasury } = await fetchPlatformConfig(client, d.platformConfigId, d.originalId)
|
|
31
|
+
const caps = await fetchAdminCaps(client, treasury, d.originalId)
|
|
32
|
+
const settled = await Promise.allSettled(caps.map((c) => fetchGate(client, c.gateId, d.originalId)))
|
|
33
|
+
if (!current()) return
|
|
34
|
+
gates.value = settled.flatMap((r) =>
|
|
35
|
+
r.status === 'fulfilled' && r.value
|
|
36
|
+
? [{ id: r.value.gateId, name: r.value.nftName || 'Unnamed Gate', price: r.value.priceMist, paused: r.value.paused, frozen: r.value.frozen }]
|
|
37
|
+
: [],
|
|
64
38
|
)
|
|
65
|
-
gates.value = gateList.filter((g): g is Gate => g !== null)
|
|
66
39
|
} catch (e) {
|
|
40
|
+
if (!current()) return
|
|
41
|
+
gates.value = []
|
|
67
42
|
error.value = e instanceof Error ? e.message : String(e)
|
|
68
43
|
} finally {
|
|
69
|
-
loading.value = false
|
|
44
|
+
if (current()) loading.value = false
|
|
70
45
|
}
|
|
71
46
|
}
|
|
72
47
|
|
|
73
|
-
|
|
74
|
-
if (getCurrentInstance()) onMounted(load)
|
|
48
|
+
onChainContext(load, () => (gates.value = []))
|
|
75
49
|
|
|
76
50
|
return { gates, loading, error, reload: load }
|
|
77
51
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
// Reads the PlatformConfig shared object
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
import {
|
|
1
|
+
// Reads the PlatformConfig shared object and exposes the treasury address + commission rate (bps).
|
|
2
|
+
// Source of truth for the commission model and for whose AdminCaps the gate discovery looks up.
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { fetchPlatformConfig } from '@meddleware/access-gate-client'
|
|
5
5
|
import { getSuiClient } from '../wallet.js'
|
|
6
|
-
import {
|
|
6
|
+
import { requireDeployment } from '../config.js'
|
|
7
|
+
import { latest, onChainContext } from './chainContext.js'
|
|
7
8
|
|
|
8
9
|
export interface PlatformConfig {
|
|
9
10
|
treasury: string
|
|
@@ -14,29 +15,27 @@ export function usePlatformConfig() {
|
|
|
14
15
|
const config = ref<PlatformConfig | null>(null)
|
|
15
16
|
const loading = ref(false)
|
|
16
17
|
const error = ref<string | null>(null)
|
|
18
|
+
const begin = latest()
|
|
17
19
|
|
|
18
20
|
async function load() {
|
|
19
|
-
|
|
21
|
+
const current = begin()
|
|
20
22
|
loading.value = true
|
|
21
23
|
error.value = null
|
|
22
24
|
try {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
config.value = {
|
|
28
|
-
treasury: String(fields.treasury),
|
|
29
|
-
commissionBps: Number(fields.commission_bps),
|
|
30
|
-
}
|
|
25
|
+
const d = requireDeployment()
|
|
26
|
+
const c = await fetchPlatformConfig(getSuiClient(), d.platformConfigId, d.originalId)
|
|
27
|
+
if (!current()) return
|
|
28
|
+
config.value = { treasury: c.treasury, commissionBps: Number(c.commissionBps) }
|
|
31
29
|
} catch (e) {
|
|
30
|
+
if (!current()) return
|
|
31
|
+
config.value = null
|
|
32
32
|
error.value = e instanceof Error ? e.message : String(e)
|
|
33
33
|
} finally {
|
|
34
|
-
loading.value = false
|
|
34
|
+
if (current()) loading.value = false
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
if (getCurrentInstance()) onMounted(load)
|
|
38
|
+
onChainContext(load, () => (config.value = null))
|
|
40
39
|
|
|
41
40
|
return { config, loading, error, reload: load }
|
|
42
41
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
// Recent access_gate mints and consumes, newest first, typed by @meddleware/access-gate-client.
|
|
2
|
+
// Read from the indexer when VITE_INDEXER_URL is set (display data; falls back to the full node).
|
|
3
|
+
import { ref } from 'vue'
|
|
4
|
+
import { listAccessGateEvents, type EventCursor } from '@meddleware/access-gate-client'
|
|
2
5
|
import { getSuiClient } from '../wallet.js'
|
|
3
|
-
import {
|
|
6
|
+
import { INDEXER_URL, network, requireDeployment } from '../config.js'
|
|
7
|
+
import { latest, onChainContext } from './chainContext.js'
|
|
4
8
|
|
|
5
9
|
export interface TreasuryEvent {
|
|
6
|
-
type: 'AccessMinted' | 'AccessConsumed'
|
|
10
|
+
type: 'AccessMinted' | 'AccessConsumed'
|
|
7
11
|
txDigest: string
|
|
8
12
|
checkpoint: string | null
|
|
9
|
-
|
|
13
|
+
/** The buyer (or airdrop recipient) of a mint; the NFT owner who spent a use. */
|
|
14
|
+
address: string
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
export function useTreasuryActivity(limit = 20) {
|
|
@@ -14,62 +19,52 @@ export function useTreasuryActivity(limit = 20) {
|
|
|
14
19
|
const loading = ref(false)
|
|
15
20
|
const error = ref<string | null>(null)
|
|
16
21
|
const lastRefresh = ref<Date | null>(null)
|
|
22
|
+
/** Oldest checkpoint the indexer covers, when the list came from it. */
|
|
23
|
+
const indexedFromCheckpoint = ref<string | null>(null)
|
|
24
|
+
const begin = latest()
|
|
17
25
|
|
|
18
26
|
async function load() {
|
|
19
|
-
|
|
27
|
+
const current = begin()
|
|
20
28
|
loading.value = true
|
|
21
29
|
error.value = null
|
|
22
30
|
try {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const typeName = e.eventType.split('::').pop() ?? ''
|
|
40
|
-
const label =
|
|
41
|
-
typeName === 'AccessMintedEvent' ? 'AccessMinted'
|
|
42
|
-
: typeName === 'AccessConsumedEvent' ? 'AccessConsumed'
|
|
43
|
-
: 'AccessBurned'
|
|
44
|
-
const f = (e.json ?? {}) as Record<string, unknown>
|
|
45
|
-
all.push({
|
|
46
|
-
type: label as TreasuryEvent['type'],
|
|
47
|
-
txDigest: e.transactionDigest,
|
|
48
|
-
checkpoint: e.checkpoint ?? null,
|
|
49
|
-
address: String(f.recipient ?? f.creator ?? f.sender ?? ''),
|
|
50
|
-
})
|
|
31
|
+
const d = requireDeployment()
|
|
32
|
+
const out: TreasuryEvent[] = []
|
|
33
|
+
let cursor: EventCursor | null = null
|
|
34
|
+
let from: string | null = null
|
|
35
|
+
// Bounded: each call scans at most a few full-node pages; ten calls cover any realistic limit.
|
|
36
|
+
for (let calls = 0; calls < 10; calls++) {
|
|
37
|
+
const page = await listAccessGateEvents(getSuiClient(), {
|
|
38
|
+
originalId: d.originalId,
|
|
39
|
+
kinds: ['AccessMinted', 'AccessConsumed'],
|
|
40
|
+
limit: Math.min(100, limit - out.length),
|
|
41
|
+
cursor,
|
|
42
|
+
indexer: INDEXER_URL ? { url: INDEXER_URL, network: network.value } : undefined,
|
|
43
|
+
})
|
|
44
|
+
for (const e of page.events) {
|
|
45
|
+
if (e.kind === 'AccessMinted') out.push({ type: e.kind, txDigest: e.txDigest, checkpoint: e.checkpoint, address: e.recipient })
|
|
46
|
+
else if (e.kind === 'AccessConsumed') out.push({ type: e.kind, txDigest: e.txDigest, checkpoint: e.checkpoint, address: e.consumer })
|
|
51
47
|
}
|
|
48
|
+
from = page.indexedFromCheckpoint ?? from
|
|
49
|
+
cursor = page.cursor
|
|
50
|
+
if (!cursor || out.length >= limit) break
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const ca = BigInt(a.checkpoint ?? '0')
|
|
57
|
-
const cb = BigInt(b.checkpoint ?? '0')
|
|
58
|
-
if (cb > ca) return 1
|
|
59
|
-
if (cb < ca) return -1
|
|
60
|
-
return 0
|
|
61
|
-
})
|
|
62
|
-
events.value = all.slice(0, limit)
|
|
52
|
+
if (!current()) return
|
|
53
|
+
events.value = out.slice(0, limit)
|
|
54
|
+
indexedFromCheckpoint.value = from
|
|
63
55
|
lastRefresh.value = new Date()
|
|
64
56
|
} catch (e) {
|
|
57
|
+
if (!current()) return
|
|
65
58
|
error.value = e instanceof Error ? e.message : String(e)
|
|
66
59
|
} finally {
|
|
67
|
-
loading.value = false
|
|
60
|
+
if (current()) loading.value = false
|
|
68
61
|
}
|
|
69
62
|
}
|
|
70
63
|
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
onChainContext(load, () => {
|
|
65
|
+
events.value = []
|
|
66
|
+
indexedFromCheckpoint.value = null
|
|
67
|
+
})
|
|
73
68
|
|
|
74
|
-
return { events, loading, error, lastRefresh, reload: load }
|
|
69
|
+
return { events, loading, error, lastRefresh, indexedFromCheckpoint, reload: load }
|
|
75
70
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,31 +1,25 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
1
|
+
// Chain configuration. The network is wallet-adapter's shared runtime selector — the one source for
|
|
2
|
+
// the client, the access_gate ids, explorer links and the status bar. The ids come from
|
|
3
|
+
// @meddleware/access-gate-client/deployments, generated from access-gate-sui's published records.
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
import { useNetwork } from '@meddleware/wallet-adapter'
|
|
6
|
+
import { accessGateDeployment, type AccessGateDeployment } from '@meddleware/access-gate-client/deployments'
|
|
7
|
+
import type { SuiNetwork } from '@meddleware/ui'
|
|
5
8
|
|
|
6
|
-
|
|
9
|
+
/** The active network (read-only ref; the standalone build selects `VITE_NETWORK` in main.ts). */
|
|
10
|
+
export const network = useNetwork().network
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
mainnet: import.meta.env.VITE_RPC_MAINNET || 'https://fullnode.mainnet.sui.io:443',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Published access_gate package ID. Governs which events/objects are queried. */
|
|
14
|
-
export const ACCESS_GATE_PACKAGE_ID: Record<Network, string> = {
|
|
15
|
-
testnet:
|
|
16
|
-
import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_TESTNET ||
|
|
17
|
-
'0x0bedd0b27d993d3292ca6a5315f7562de8bc0ff3752b445b4c53252c76f2d20d',
|
|
18
|
-
mainnet: import.meta.env.VITE_ACCESS_GATE_PACKAGE_ID_MAINNET || '',
|
|
19
|
-
}
|
|
12
|
+
/** SuiVision has no localnet; nothing is listed there (no deployment), so testnet links are inert. */
|
|
13
|
+
export const explorerNetwork = computed<SuiNetwork>(() => (network.value === 'mainnet' ? 'mainnet' : 'testnet'))
|
|
20
14
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
15
|
+
/**
|
|
16
|
+
* The access_gate deployment on the active network.
|
|
17
|
+
*
|
|
18
|
+
* @throws {Error} if none is recorded for it (e.g. mainnet before launch, or localnet).
|
|
19
|
+
*/
|
|
20
|
+
export function requireDeployment(): AccessGateDeployment {
|
|
21
|
+
return accessGateDeployment(network.value)
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
export const
|
|
31
|
-
export const RPC_URL = RPC_URLS[NETWORK]
|
|
24
|
+
/** Optional read-indexer (display data only; reads fall back to the full node). */
|
|
25
|
+
export const INDEXER_URL: string = import.meta.env.VITE_INDEXER_URL || ''
|
package/src/env.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
/// <reference types="vite/client" />
|
|
2
2
|
|
|
3
3
|
interface ImportMetaEnv {
|
|
4
|
+
/** Standalone build only: the network main.ts selects (embedded views follow the host's selector). */
|
|
4
5
|
readonly VITE_NETWORK?: string
|
|
5
|
-
|
|
6
|
-
readonly
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
9
|
-
readonly VITE_PLATFORM_CONFIG_ID_TESTNET?: string
|
|
10
|
-
readonly VITE_PLATFORM_CONFIG_ID_MAINNET?: string
|
|
6
|
+
/** Optional read-indexer base URL (display data; reads fall back to the full node). */
|
|
7
|
+
readonly VITE_INDEXER_URL?: string
|
|
8
|
+
readonly VITE_DOCS_URL?: string
|
|
9
|
+
readonly VITE_DEV_URL?: string
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
interface ImportMeta {
|
package/src/main.ts
CHANGED
|
@@ -7,7 +7,13 @@ import '@meddleware/ui/base.css'
|
|
|
7
7
|
import './styles/qt.css'
|
|
8
8
|
import { createApp } from 'vue'
|
|
9
9
|
import { useSeason } from '@meddleware/ui'
|
|
10
|
+
import { useNetwork } from '@meddleware/wallet-adapter'
|
|
10
11
|
import App from './App.vue'
|
|
11
12
|
|
|
13
|
+
// The standalone build targets one network; select it in the shared selector so the client, the
|
|
14
|
+
// ids, the explorer links and the status bar all agree. (Embedded, the host's selector rules.)
|
|
15
|
+
const built = import.meta.env.VITE_NETWORK || 'testnet'
|
|
16
|
+
if (built === 'testnet' || built === 'mainnet') useNetwork().setNetwork(built)
|
|
17
|
+
|
|
12
18
|
useSeason()
|
|
13
19
|
createApp(App).mount('#app')
|
package/src/styles/qt.css
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* UiActivityFeed/UiActivityItem). What remains here are small text/layout
|
|
8
8
|
* utilities that are specific to this console's tabs.
|
|
9
9
|
*
|
|
10
|
-
* Applied globally via
|
|
10
|
+
* Applied globally via TreasuryView.vue (so it works when embedded as a library).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/* ── Shell layout ────────────────────────────────────────────────────────── */
|
package/src/tabs/AccountsTab.vue
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
UiStatRow,
|
|
16
16
|
UiDataTable,
|
|
17
17
|
} from '@meddleware/ui'
|
|
18
|
-
import {
|
|
18
|
+
import { explorerNetwork } from '../config.js'
|
|
19
19
|
|
|
20
20
|
const { config, loading: cfgLoading, error: cfgErr } = usePlatformConfig()
|
|
21
21
|
const { balance } = useTreasury(() => config.value?.treasury ?? null)
|
|
@@ -42,7 +42,7 @@ function formatPrice(mist: bigint): string {
|
|
|
42
42
|
<UiStatRow label="Current balance"><AmountCell :mist="balance" /></UiStatRow>
|
|
43
43
|
<UiStatRow label="Treasury address" align="left">
|
|
44
44
|
<CopyableAddress :address="config.treasury">
|
|
45
|
-
<ExplorerLink :href="suiExplorerUrl('account', config.treasury,
|
|
45
|
+
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, explorerNetwork)" :value="config.treasury" />
|
|
46
46
|
</CopyableAddress>
|
|
47
47
|
</UiStatRow>
|
|
48
48
|
</UiStatGrid>
|
|
@@ -63,7 +63,7 @@ function formatPrice(mist: bigint): string {
|
|
|
63
63
|
<td class="dao-amount">{{ formatPrice(gate.price) }}</td>
|
|
64
64
|
<td class="dao-mono dao-mono--sm dao-nowrap">
|
|
65
65
|
<CopyableAddress :address="gate.id" label="Copy object ID">
|
|
66
|
-
<ExplorerLink :href="suiExplorerUrl('object', gate.id,
|
|
66
|
+
<ExplorerLink :href="suiExplorerUrl('object', gate.id, explorerNetwork)" :value="gate.id" />
|
|
67
67
|
</CopyableAddress>
|
|
68
68
|
</td>
|
|
69
69
|
</tr>
|
package/src/tabs/ActivityTab.vue
CHANGED
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
UiBadge,
|
|
12
12
|
UiToolbarButton,
|
|
13
13
|
} from '@meddleware/ui'
|
|
14
|
-
import {
|
|
14
|
+
import { explorerNetwork } from '../config.js'
|
|
15
15
|
import type { TreasuryEvent } from '../composables/useTreasuryActivity.js'
|
|
16
16
|
|
|
17
|
-
const { events, loading, error, reload } = useTreasuryActivity(200)
|
|
17
|
+
const { events, loading, error, reload, indexedFromCheckpoint } = useTreasuryActivity(200)
|
|
18
18
|
|
|
19
19
|
const PAGE_SIZE = 20
|
|
20
20
|
const page = ref(1)
|
|
@@ -30,7 +30,6 @@ function nextPage() { if (page.value < totalPages.value) page.value++ }
|
|
|
30
30
|
const eventLabel: Record<TreasuryEvent['type'], string> = {
|
|
31
31
|
AccessMinted: 'Access Sold',
|
|
32
32
|
AccessConsumed: 'Access Used',
|
|
33
|
-
AccessBurned: 'Access Burned',
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
function badgeVariant(type: TreasuryEvent['type']): 'active' | 'closed' {
|
|
@@ -47,13 +46,14 @@ function badgeVariant(type: TreasuryEvent['type']): 'active' | 'closed' {
|
|
|
47
46
|
</header>
|
|
48
47
|
|
|
49
48
|
<p v-if="error" class="dao-muted">{{ error }}</p>
|
|
49
|
+
<p v-if="indexedFromCheckpoint" class="dao-muted">Indexed since checkpoint {{ indexedFromCheckpoint }}.</p>
|
|
50
50
|
|
|
51
51
|
<UiDataTable v-if="pageEvents.length || loading" :empty="'No events'">
|
|
52
52
|
<template #head>
|
|
53
53
|
<th>Type</th>
|
|
54
54
|
<th>
|
|
55
55
|
Address
|
|
56
|
-
<abbr class="dao-info" title="The
|
|
56
|
+
<abbr class="dao-info" title="The buyer of a sold pass, or the holder who used one. Click to view in explorer; click the copy icon to copy the full address.">?</abbr>
|
|
57
57
|
</th>
|
|
58
58
|
<th>
|
|
59
59
|
Block
|
|
@@ -68,7 +68,7 @@ function badgeVariant(type: TreasuryEvent['type']): 'active' | 'closed' {
|
|
|
68
68
|
<td><UiBadge :variant="badgeVariant(ev.type)">{{ eventLabel[ev.type] }}</UiBadge></td>
|
|
69
69
|
<td class="dao-mono activity__cell dao-nowrap">
|
|
70
70
|
<CopyableAddress v-if="ev.address && ev.address !== 'undefined'" :address="ev.address">
|
|
71
|
-
<ExplorerLink :href="suiExplorerUrl('account', ev.address,
|
|
71
|
+
<ExplorerLink :href="suiExplorerUrl('account', ev.address, explorerNetwork)" :value="ev.address" />
|
|
72
72
|
</CopyableAddress>
|
|
73
73
|
<span v-else>—</span>
|
|
74
74
|
</td>
|
|
@@ -77,7 +77,7 @@ function badgeVariant(type: TreasuryEvent['type']): 'active' | 'closed' {
|
|
|
77
77
|
</td>
|
|
78
78
|
<td class="dao-mono activity__cell dao-nowrap">
|
|
79
79
|
<CopyableAddress :address="ev.txDigest" label="Copy transaction">
|
|
80
|
-
<ExplorerLink :href="suiExplorerUrl('txblock', ev.txDigest,
|
|
80
|
+
<ExplorerLink :href="suiExplorerUrl('txblock', ev.txDigest, explorerNetwork)" :value="ev.txDigest" />
|
|
81
81
|
</CopyableAddress>
|
|
82
82
|
</td>
|
|
83
83
|
</tr>
|
package/src/tabs/OverviewTab.vue
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
UiActivityFeed,
|
|
18
18
|
UiActivityItem,
|
|
19
19
|
} from '@meddleware/ui'
|
|
20
|
-
import {
|
|
20
|
+
import { explorerNetwork } from '../config.js'
|
|
21
21
|
import type { TreasuryEvent } from '../composables/useTreasuryActivity.js'
|
|
22
22
|
|
|
23
23
|
const { config } = usePlatformConfig()
|
|
@@ -28,7 +28,6 @@ const { gates, loading: gatesLoading } = useGates()
|
|
|
28
28
|
const eventLabel: Record<TreasuryEvent['type'], string> = {
|
|
29
29
|
AccessMinted: 'Access sold',
|
|
30
30
|
AccessConsumed: 'Access used',
|
|
31
|
-
AccessBurned: 'Access burned',
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
const commissionPct = computed(() =>
|
|
@@ -49,7 +48,7 @@ const accessesConsumed = computed(() => events.value.filter((e) => e.type === 'A
|
|
|
49
48
|
<UiStatRow label="Commission rate">{{ commissionPct }}</UiStatRow>
|
|
50
49
|
<UiStatRow label="Treasury address" align="left">
|
|
51
50
|
<CopyableAddress v-if="config?.treasury" :address="config.treasury">
|
|
52
|
-
<ExplorerLink :href="suiExplorerUrl('account', config.treasury,
|
|
51
|
+
<ExplorerLink :href="suiExplorerUrl('account', config.treasury, explorerNetwork)" :value="config.treasury" />
|
|
53
52
|
</CopyableAddress>
|
|
54
53
|
<span v-else class="dao-mono dao-mono--sm">—</span>
|
|
55
54
|
</UiStatRow>
|
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
|
})
|