Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import {
useResolveOnChainAuditTrail,
} from '~/hooks/useResolveAuditTrail';
import { TransactionsView } from '../common/TransactionsView';
import { AuditTrailSummaryView } from './views/AuditTrailSummaryView';
import { MetadataView } from './views/MetadataView';
import { TagsView } from './views/TagsView';
import { RecordsView } from './views/RecordsView';
import { SideBySidePanels } from '~/components/ui/SideBySidePanels';

interface AuditTrailContentProps {
objectId: string;
Expand Down Expand Up @@ -108,6 +113,20 @@ export function AuditTrailContent({ objectId }: AuditTrailContentProps) {
.addItem(getAuditTrailRecordsSize(auditTrailObject))
.build()}
/>
<AuditTrailSummaryView
auditTrailObject={auditTrailObject}
objectData={objectResult.data!}
/>
<SideBySidePanels
firstPanel={<p>Replace with LockLifecycleView</p>}
secondPanel={<MetadataView auditTrail={auditTrailObject} />}
/>
<RecordsView objectId={objectId} auditTrail={auditTrailHandle} />
<SideBySidePanels
ratio="66-34"
firstPanel={<p>Replace with RolesView</p>}
secondPanel={<TagsView tags={auditTrailObject.tags} />}
/>
<TransactionsView objectId={objectId} />
</div>
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { DisplayStats, TooltipPosition } from '@iota/apps-ui-kit';
import { formatDate, useFormatCoin } from '@iota/core';
import { type IotaObjectData } from '@iota/iota-sdk/client';
import { CoinFormat, formatDigest } from '@iota/iota-sdk/utils';
import clsx from 'clsx';
import { ObjectLink, TransactionLink } from '~/components/ui';
import { onCopySuccess } from '~/lib/utils';
import { ErrorBoundary } from '~/components';
import { type OnChainAuditTrail } from '@iota/audit-trails/web';

interface AuditTrailSummaryViewProps {
auditTrailObject: OnChainAuditTrail;
objectData: IotaObjectData;
}

export function AuditTrailSummaryView({
auditTrailObject,
objectData,
}: AuditTrailSummaryViewProps): JSX.Element {
const objectId = objectData?.objectId;
const storageRebate = objectData?.storageRebate;
const version = `v${auditTrailObject.version}`;
const sequenceNumber = `${auditTrailObject.sequenceNumber}`;

const dateFormat = (timestamp: bigint): string =>
// Convert seconds to milliseconds for Date constructor
formatDate(new Date(Number(timestamp)), ['year', 'month', 'day', 'hour', 'minute']);
const createdAt = dateFormat(auditTrailObject.createdAt);
const lastTransactionBlockDigest = objectData?.previousTransaction;

return (
<ErrorBoundary>
<div className="flex flex-col gap-md">
<div className={clsx('address-grid-container-top', 'no-image', 'no-description')}>
{objectId && (
<div>
<ObjectIdCard objectId={objectId} />
</div>
)}

{version && (
<div>
<DisplayStats
label="Version"
value={version}
tooltipPosition={TooltipPosition.Left}
tooltipText="Version of object in a progressive sequence."
/>
</div>
)}

{storageRebate && (
<div>
<StorageRebateCard storageRebate={storageRebate} />
</div>
)}

{createdAt && (
<div>
<DisplayStats
label="Created at"
value={createdAt}
tooltipPosition={TooltipPosition.Left}
tooltipText="Timestamp of the transaction that first published this audit trail onchain."
/>
</div>
)}

{sequenceNumber && (
<div>
<DisplayStats
label="Sequence"
value={sequenceNumber}
tooltipPosition={TooltipPosition.Left}
tooltipText="Version of state change in a progressive sequence."
/>
</div>
)}

{lastTransactionBlockDigest && (
<div>
<LastTxBlockCard digest={lastTransactionBlockDigest} />
</div>
)}
</div>
</div>
</ErrorBoundary>
);
}

interface ObjectIdCardProps {
objectId: string;
}

function ObjectIdCard({ objectId }: ObjectIdCardProps): JSX.Element {
return (
<DisplayStats
label="Object ID"
value={
<div className="flex flex-col gap-xs">
<ObjectLink objectId={objectId} copyText={objectId} />
</div>
}
tooltipPosition={TooltipPosition.Left}
tooltipText="The unique onchain identifier of the Move object storing this audit trail's state."
/>
);
}

interface LastTxBlockCardProps {
digest: string;
}

function LastTxBlockCard({ digest }: LastTxBlockCardProps): JSX.Element {
return (
<DisplayStats
label="Last Transaction Block Digest"
value={<TransactionLink digest={digest}>{formatDigest(digest)}</TransactionLink>}
copyText={digest}
onCopySuccess={onCopySuccess}
tooltipPosition={TooltipPosition.Left}
tooltipText="Hash of the most recent transaction that modified this audit trail. Use it to inspect transaction details on the explorer."
/>
);
}

interface StorageRebateCardProps {
storageRebate: string;
}

function StorageRebateCard({ storageRebate }: StorageRebateCardProps): JSX.Element | null {
const [storageRebateFormatted, symbol] = useFormatCoin({
balance: storageRebate,
format: CoinFormat.Full,
});

return (
<DisplayStats
label="Storage Rebate"
value={`-${storageRebateFormatted}`}
supportingLabel={symbol}
tooltipPosition={TooltipPosition.Left}
tooltipText="IOTA tokens locked as a storage deposit for this object. Partially refundable when the object is deleted or reduced in size."
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) 2026 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { Title, TooltipPosition, Panel } from '@iota/apps-ui-kit';
import { type ImmutableMetadata, type OnChainAuditTrail } from '@iota/audit-trails/web';
import { ErrorBoundary, SyntaxHighlighter } from '~/components';

interface MetadataViewProps {
auditTrail: OnChainAuditTrail;
}

interface LabelledSyntaxHighlighterProps {
label: string;
code: string;
language: string;
}

function LabelledSyntaxHighlighter({ label, code, language }: LabelledSyntaxHighlighterProps) {
return (
<div className="flex flex-col">
<SyntaxHighlighter code={code} language={language} />
<span className="mt-1 text-body-sm text-gray-500 dark:text-gray-400">{label}</span>
</div>
);
}

export function MetadataView({ auditTrail }: MetadataViewProps) {
const immutableMetadata = auditTrail.immutableMetadata;
const updatableMetadata = auditTrail.updatableMetadata;

return (
<ErrorBoundary>
<div className="panel-bg flex w-full flex-col gap-sm--rs rounded-xl border border-transparent">
<UpdatableMetadataPanel metadata={updatableMetadata} />
<ImmutableMetadataPanel metadata={immutableMetadata} />
</div>
</ErrorBoundary>
);
}

function ImmutableMetadataPanel({ metadata }: { metadata?: ImmutableMetadata }) {
return (
<Panel>
<div className="flex w-full flex-col gap-sm">
<Title
title="Immutable Metadata"
tooltipPosition={TooltipPosition.Left}
tooltipText="The immutable metadata of this Audit Trail. This data cannot be changed."
/>
<div className="flex flex-col gap-y-md">
<LabelledSyntaxHighlighter
label="Name (Text)"
code={metadata?.name ?? ''}
language="text"
/>
<LabelledSyntaxHighlighter
label="Description (Text)"
code={metadata?.description ?? ''}
language="text"
/>
</div>
</div>
</Panel>
);
}

function UpdatableMetadataPanel({ metadata }: { metadata?: string }) {
return (
<Panel>
<div className="flex w-full flex-col gap-sm">
<Title
title="Updatable Metadata"
tooltipPosition={TooltipPosition.Left}
tooltipText="The updatable metadata of this Audit Trail. This data can be changed by authorized actors."
/>
<LabelledSyntaxHighlighter
label="Metadata (Text)"
code={metadata ?? ''}
language="text"
/>
</div>
</Panel>
);
}
Loading
Loading