Skip to content

Commit a62c061

Browse files
committed
Create ROFL app instance details page
1 parent 79e84f1 commit a62c061

3 files changed

Lines changed: 136 additions & 1 deletion

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { useOutletContext } from 'react-router-dom'
2+
import { SearchScope } from '../../../types/searchScope'
3+
4+
export type RoflAppInstanceDetailsContext = {
5+
scope: SearchScope
6+
id: string
7+
rak: string
8+
method: string
9+
setMethod: (value: string) => void
10+
}
11+
12+
export const useRoflAppInstanceDetailsProps = () => useOutletContext<RoflAppInstanceDetailsContext>()
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { FC } from 'react'
2+
import { useHref, useParams } from 'react-router-dom'
3+
import { useTranslation } from 'react-i18next'
4+
import Typography from '@mui/material/Typography'
5+
import { Layer, RoflInstance, useGetRuntimeRoflAppsIdInstances } from '../../../oasis-nexus/api'
6+
import { SearchScope } from '../../../types/searchScope'
7+
import { AppErrors } from '../../../types/errors'
8+
import { API_MAX_TOTAL_COUNT } from '../../config'
9+
import { RoflAppInstanceDetailsContext } from './hooks'
10+
import { useRequiredScopeParam } from '../../hooks/useScopeParam'
11+
import { useScreenSize } from '../../hooks/useScreensize'
12+
import { PageLayout } from '../../components/PageLayout'
13+
import { SubPageCard } from '../../components/SubPageCard'
14+
import { TextSkeleton } from '../../components/Skeleton'
15+
import { StyledDescriptionList } from '../../components/StyledDescriptionList'
16+
import { RouterTabs } from '../../components/RouterTabs'
17+
import { RoflAppLink } from '../../components/Rofl/RoflAppLink'
18+
import { CopyToClipboard } from '../../components/CopyToClipboard'
19+
import { useTypedSearchParam } from '../../hooks/useTypedSearchParam'
20+
21+
export const RoflAppInstanceDetailsPage: FC = () => {
22+
const { t } = useTranslation()
23+
const scope = useRequiredScopeParam()
24+
const id = useParams().id!
25+
const rak = useParams().rak!
26+
const txLink = useHref('')
27+
const [method, setMethod] = useTypedSearchParam('method', 'any', {
28+
deleteParams: ['page'],
29+
})
30+
const context: RoflAppInstanceDetailsContext = { scope, id, rak, method, setMethod }
31+
const instancesQuery = useGetRuntimeRoflAppsIdInstances(scope.network, Layer.sapphire, id, {
32+
limit: API_MAX_TOTAL_COUNT,
33+
})
34+
const { isLoading, isFetched, data } = instancesQuery
35+
const instances = data?.data.instances
36+
const instance = instances?.find(inst => inst.rak === rak)
37+
if (!instance && isFetched) {
38+
throw AppErrors.NotFoundRoflAppInstance
39+
}
40+
41+
return (
42+
<PageLayout>
43+
<SubPageCard featured title={t('rofl.instanceDetails')}>
44+
<RoflAppInstanceDetailsView isLoading={isLoading} appId={id} instance={instance} scope={scope} />
45+
</SubPageCard>
46+
<RouterTabs tabs={[{ label: t('common.transactions'), to: txLink }]} context={context} />
47+
</PageLayout>
48+
)
49+
}
50+
51+
export const RoflAppInstanceDetailsView: FC<{
52+
isLoading?: boolean
53+
appId: string
54+
instance: RoflInstance | undefined
55+
scope: SearchScope
56+
}> = ({ appId, instance, isLoading, scope }) => {
57+
const { t } = useTranslation()
58+
const { isMobile } = useScreenSize()
59+
60+
if (isLoading) return <TextSkeleton numberOfRows={6} />
61+
if (!instance) return <></>
62+
63+
return (
64+
<StyledDescriptionList titleWidth={isMobile ? '100px' : '200px'}>
65+
<dt>{t('rofl.rakAbbreviation')}</dt>
66+
<dd>
67+
<Typography variant="mono">
68+
{instance.rak} <CopyToClipboard value={instance.rak} />
69+
</Typography>
70+
</dd>
71+
<dt>{t('rofl.rekAbbreviation')}</dt>
72+
<dd>
73+
<Typography variant="mono">
74+
{instance.rek} <CopyToClipboard value={instance.rek} />
75+
</Typography>
76+
</dd>
77+
<dt>{t('rofl.expirationEpoch')}</dt>
78+
<dd>{instance.expiration_epoch.toLocaleString()}</dd>
79+
<dt>{t('rofl.roflAppId')}</dt>
80+
<dd>
81+
<RoflAppLink id={appId} network={scope.network} withSourceIndicator={false} />
82+
<CopyToClipboard value={appId} />
83+
</dd>
84+
<dt>{t('rofl.endorsingNodeId')}</dt>
85+
<dd>
86+
<Typography variant="mono">
87+
{instance.endorsing_node_id} <CopyToClipboard value={instance.endorsing_node_id} />
88+
</Typography>
89+
</dd>
90+
<dt>{t('rofl.extraKeys')}</dt>
91+
<dd>
92+
{instance.extra_keys.length ? (
93+
<table>
94+
<tbody>
95+
{instance.extra_keys.map((key, index) => {
96+
const entries = Object.entries(JSON.parse(key))
97+
const [keyType, keyValue] = entries[0]
98+
99+
return (
100+
<tr key={index}>
101+
<td>
102+
<Typography variant="mono">{keyType}:</Typography>
103+
</td>
104+
<td>
105+
<Typography variant="mono">{String(keyValue)}</Typography>
106+
</td>
107+
</tr>
108+
)
109+
})}
110+
</tbody>
111+
</table>
112+
) : (
113+
t('common.missing')
114+
)}
115+
</dd>
116+
</StyledDescriptionList>
117+
)
118+
}

src/locales/en/translation.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,12 @@
718718
"secrets": "Secrets",
719719
"updates": "Updates",
720720
"secretLength": "[{{value}} bytes]",
721-
"expired": "Expired"
721+
"expired": "Expired",
722+
"instanceDetails": "Instance details",
723+
"rekAbbreviation": "REK",
724+
"roflAppId": "ROFL App ID",
725+
"endorsingNodeId": "Endorsing node ID",
726+
"extraKeys": "Extra keys"
722727
},
723728
"search": {
724729
"placeholder": "Address, Block, Contract, Transaction hash, Token name, etc.",

0 commit comments

Comments
 (0)