diff --git a/dashboard/src/components/EventCard.tsx b/dashboard/src/components/EventCard.tsx index f7cf08e..e6dcd96 100644 --- a/dashboard/src/components/EventCard.tsx +++ b/dashboard/src/components/EventCard.tsx @@ -17,21 +17,7 @@ function shortenAddress(address: string): string { return `${address.slice(0, 6)}...${address.slice(-4)}`; } -const EVENT_TYPE_COLORS: Record = { - TaskCreated: 'event-card__badge--green', - WorkSubmitted: 'event-card__badge--blue', - SubmissionApproved: 'event-card__badge--green', - SubmissionRejected: 'event-card__badge--red', - TaskCancelled: 'event-card__badge--red', - DisputeRaised: 'event-card__badge--yellow', - AutoshareCreated: 'event-card__badge--purple', - Withdrawal: 'event-card__badge--orange', -}; - -function getEventBadgeClass(name: string | null): string { - if (!name) return 'event-card__badge--default'; - return EVENT_TYPE_COLORS[name] ?? 'event-card__badge--default'; -} +import { getEventBadgeClass } from '../utils/eventTypeMapping'; function SkeletonLine({ width = '100%', height = '14px' }: { width?: string; height?: string }) { return ( diff --git a/dashboard/src/components/EventExplorerCard.tsx b/dashboard/src/components/EventExplorerCard.tsx index 930b841..772c2e6 100644 --- a/dashboard/src/components/EventExplorerCard.tsx +++ b/dashboard/src/components/EventExplorerCard.tsx @@ -3,17 +3,7 @@ import type { ContractStatus } from '../services/eventsApi'; import { formatTimestamp } from '../utils/formatTime'; import { CopyButton } from './CopyButton'; -const EVENT_KIND_STYLES: Record = { - contract: 'event-explorer__badge--blue', - system: 'event-explorer__badge--purple', - debug: 'event-explorer__badge--default', -}; - -const EVENT_KIND_LABELS: Record = { - contract: 'Contract', - system: 'System', - debug: 'Debug', -}; +import { getEventKindClass, getEventKindLabel } from '../utils/eventTypeMapping'; function shortenAddress(address: string) { if (address.length <= 14) { @@ -23,14 +13,6 @@ function shortenAddress(address: string) { return `${address.slice(0, 6)}...${address.slice(-4)}`; } -function getEventKindClass(type: string) { - return EVENT_KIND_STYLES[type.toLowerCase()] ?? EVENT_KIND_STYLES.debug; -} - -function getEventKindLabel(type: string) { - return EVENT_KIND_LABELS[type.toLowerCase()] ?? 'Unknown'; -} - interface EventExplorerCardProps { event: BlockchainEvent; onCopyContract: (contractAddress: string) => void; diff --git a/dashboard/src/components/NotificationDetailsDrawer.tsx b/dashboard/src/components/NotificationDetailsDrawer.tsx index eaa7fb2..b721bf0 100644 --- a/dashboard/src/components/NotificationDetailsDrawer.tsx +++ b/dashboard/src/components/NotificationDetailsDrawer.tsx @@ -2,6 +2,8 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import type { BlockchainEvent } from '../types/event'; import { formatTimestamp } from '../utils/formatTime'; import { copyTextToClipboard } from '../utils/clipboard'; +import { getEventTypePresentation } from '../utils/eventTypeMapping'; +import { formatRawPayload, copyPayloadToClipboard } from '../utils/payloadViewer'; type FetchState = | { status: 'idle' } @@ -71,6 +73,7 @@ export function NotificationDetailsDrawer({ status: 'idle', }); const [copyMessage, setCopyMessage] = useState(null); + const [isRawView, setIsRawView] = useState(false); const resolvedFetcher = useMemo( () => fetchMetadata ?? (async (e: BlockchainEvent) => defaultMetadata(e)), @@ -80,11 +83,10 @@ export function NotificationDetailsDrawer({ useEffect(() => { if (!isOpen || !notification) { setFetchState({ status: 'idle' }); + setIsRawView(false); return; } - // Fast path: no async metadata provider, keep the drawer snappy and avoid - // unnecessary loading states. if (!fetchMetadata) { setFetchState({ status: 'success', data: defaultMetadata(notification) }); return; @@ -121,7 +123,7 @@ export function NotificationDetailsDrawer({ useEffect(() => { if (!copyMessage) return; - const id = window.setTimeout(() => setCopyMessage(null), 1500); + const id = window.setTimeout(() => setCopyMessage(null), 1800); return () => window.clearTimeout(id); }, [copyMessage]); @@ -130,10 +132,23 @@ export function NotificationDetailsDrawer({ setCopyMessage(ok ? `${label} copied` : `Copy failed`); }, []); + const handleCopyPayload = useCallback(async () => { + if (!notification) return; + const res = await copyPayloadToClipboard(notification.value); + if (res.success) { + setCopyMessage(res.isJson ? 'Payload copied as valid JSON' : 'Payload copied'); + } else { + setCopyMessage('Copy failed'); + } + }, [notification]); + if (!isOpen || !notification) { return null; } + const presentation = getEventTypePresentation(notification.eventName ?? notification.type); + const formattedPayload = formatRawPayload(notification.value); + const sender = fetchState.status === 'success' ? fetchState.data.sender @@ -153,7 +168,10 @@ export function NotificationDetailsDrawer({