From 06267b368500df9b5cd2dda6953e65d170585602 Mon Sep 17 00:00:00 2001 From: Anuj Boddu Date: Sat, 7 Mar 2026 19:43:48 -0600 Subject: [PATCH] fix(web-ui): replace vcStatus any casts with shared types --- .../WorkflowDAG/HoverDetailPanel.tsx | 4 +- .../execution/CompactExecutionHeader.tsx | 11 +--- .../execution/EnhancedExecutionHeader.tsx | 11 +--- .../components/execution/EnhancedModal.tsx | 10 ++- .../components/execution/ExecutionHeader.tsx | 11 +--- .../components/execution/ExecutionHero.tsx | 22 ++----- .../execution/ExecutionIdentityPanel.tsx | 11 +--- .../src/components/layout/ResponsiveGrid.tsx | 4 +- .../reasoners/EnhancedJsonViewer.tsx | 64 +++++++++++-------- .../components/reasoners/ExecutionQueue.tsx | 28 ++++++-- .../vc/VerifiableCredentialBadge.tsx | 3 +- .../web/client/src/hooks/useVCVerification.ts | 9 +-- .../src/pages/EnhancedExecutionDetailPage.tsx | 12 +--- .../client/src/pages/ExecutionDetailPage.tsx | 9 +-- .../pages/RedesignedExecutionDetailPage.tsx | 9 +-- control-plane/web/client/src/types/vc.ts | 11 ++++ .../web/client/src/types/workflows.ts | 2 + 17 files changed, 113 insertions(+), 118 deletions(-) create mode 100644 control-plane/web/client/src/types/vc.ts diff --git a/control-plane/web/client/src/components/WorkflowDAG/HoverDetailPanel.tsx b/control-plane/web/client/src/components/WorkflowDAG/HoverDetailPanel.tsx index fbbc2c4b..febbd558 100644 --- a/control-plane/web/client/src/components/WorkflowDAG/HoverDetailPanel.tsx +++ b/control-plane/web/client/src/components/WorkflowDAG/HoverDetailPanel.tsx @@ -142,8 +142,8 @@ export const HoverDetailPanel = memo(({ node, position, visible }: HoverDetailPa const tone = statusTone[toneKey]; // Handle optional fields that may not exist on WorkflowDAGLightweightNode - const agentNameField = (node as any).agent_name; - const taskNameField = (node as any).task_name; + const agentNameField = node.agent_name; + const taskNameField = node.task_name; const agentColor = agentColorManager.getAgentColor( agentNameField || node.agent_node_id, diff --git a/control-plane/web/client/src/components/execution/CompactExecutionHeader.tsx b/control-plane/web/client/src/components/execution/CompactExecutionHeader.tsx index 5b3efbed..3d44f547 100644 --- a/control-plane/web/client/src/components/execution/CompactExecutionHeader.tsx +++ b/control-plane/web/client/src/components/execution/CompactExecutionHeader.tsx @@ -8,6 +8,7 @@ import { import { ArrowDown, ArrowUp } from "@/components/ui/icon-bridge"; import { useNavigate } from "react-router-dom"; import type { WorkflowExecution } from "../../types/executions"; +import type { VCStatusData } from "../../types/vc"; import { DIDDisplay } from "../did/DIDDisplay"; import { Button } from "../ui/button"; import { CopyButton } from "../ui/copy-button"; @@ -18,13 +19,7 @@ import { normalizeExecutionStatus } from "../../utils/status"; interface CompactExecutionHeaderProps { execution: WorkflowExecution; - vcStatus?: { - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - } | null; + vcStatus?: VCStatusData | null; vcLoading?: boolean; onClose?: () => void; } @@ -245,7 +240,7 @@ export function CompactExecutionHeader({ void; } @@ -326,7 +321,7 @@ export function EnhancedExecutionHeader({ ("formatted"); + const isViewMode = (value: string): value is "formatted" | "raw" | "markdown" => { + return value === "formatted" || value === "raw" || value === "markdown"; + }; + const jsonString = React.useMemo(() => { try { return JSON.stringify(data, null, 2); @@ -149,7 +153,11 @@ export function DataModal({ isOpen, onClose, title, icon, data }: DataModalProps
setViewMode(value as any)} + onValueChange={(value) => { + if (isViewMode(value)) { + setViewMode(value); + } + }} className="w-full" > diff --git a/control-plane/web/client/src/components/execution/ExecutionHeader.tsx b/control-plane/web/client/src/components/execution/ExecutionHeader.tsx index 64aa0fe4..2b014e99 100644 --- a/control-plane/web/client/src/components/execution/ExecutionHeader.tsx +++ b/control-plane/web/client/src/components/execution/ExecutionHeader.tsx @@ -8,6 +8,7 @@ import { } from "@/components/ui/icon-bridge"; import { useNavigate } from "react-router-dom"; import type { WorkflowExecution } from "../../types/executions"; +import type { VCStatusData } from "../../types/vc"; import type { CanonicalStatus } from "../../utils/status"; import { DIDDisplay } from "../did/DIDDisplay"; import StatusIndicator from "../ui/status-indicator"; @@ -25,13 +26,7 @@ import { interface ExecutionHeaderProps { execution: WorkflowExecution; - vcStatus?: { - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - } | null; + vcStatus?: VCStatusData | null; vcLoading?: boolean; onNavigateBack?: () => void; } @@ -271,7 +266,7 @@ export function ExecutionHeader({ diff --git a/control-plane/web/client/src/components/execution/ExecutionIdentityPanel.tsx b/control-plane/web/client/src/components/execution/ExecutionIdentityPanel.tsx index 02c3b888..cdc0cfeb 100644 --- a/control-plane/web/client/src/components/execution/ExecutionIdentityPanel.tsx +++ b/control-plane/web/client/src/components/execution/ExecutionIdentityPanel.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { Shield, ExternalLink, AlertCircle, CheckCircle, Eye, Download, Loader2 } from "@/components/ui/icon-bridge"; import { ResponsiveGrid } from "@/components/layout/ResponsiveGrid"; import type { WorkflowExecution } from "../../types/executions"; +import type { VCStatusData } from "../../types/vc"; import { DIDDisplay } from "../did/DIDDisplay"; import { Button } from "../ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; @@ -13,13 +14,7 @@ import { CopyButton } from "../ui/copy-button"; interface ExecutionIdentityPanelProps { execution: WorkflowExecution; - vcStatus?: { - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - } | null; + vcStatus?: VCStatusData | null; vcLoading?: boolean; } @@ -166,7 +161,7 @@ export function ExecutionIdentityPanel({ ( return ( } className={cn(spanClasses, className)} {...props} > diff --git a/control-plane/web/client/src/components/reasoners/EnhancedJsonViewer.tsx b/control-plane/web/client/src/components/reasoners/EnhancedJsonViewer.tsx index f637512d..d99a6cbc 100644 --- a/control-plane/web/client/src/components/reasoners/EnhancedJsonViewer.tsx +++ b/control-plane/web/client/src/components/reasoners/EnhancedJsonViewer.tsx @@ -14,7 +14,7 @@ import { SmartStringRenderer } from './SmartStringRenderer'; import { JsonModal } from './JsonModal'; interface EnhancedJsonViewerProps { - data: any; + data: unknown; title?: string; className?: string; maxInlineHeight?: number; @@ -22,7 +22,7 @@ interface EnhancedJsonViewerProps { interface JsonItem { key: string; - value: any; + value: unknown; type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'null'; path: string[]; isExpandable: boolean; @@ -36,7 +36,7 @@ export function EnhancedJsonViewer({ const [expandedItems, setExpandedItems] = useState>(new Set()); const [modalState, setModalState] = useState<{ isOpen: boolean; - content: any; + content: unknown | null; path: string[]; title: string; }>({ @@ -56,7 +56,7 @@ export function EnhancedJsonViewer({ setExpandedItems(newExpanded); }; - const openModal = (content: any, path: string[], itemTitle: string) => { + const openModal = (content: unknown, path: string[], itemTitle: string) => { setModalState({ isOpen: true, content, @@ -74,12 +74,12 @@ export function EnhancedJsonViewer({ }); }; - const copyToClipboard = (content: any) => { + const copyToClipboard = (content: unknown) => { const text = typeof content === 'string' ? content : JSON.stringify(content, null, 2); navigator.clipboard.writeText(text); }; - const processJsonData = (obj: any, parentPath: string[] = []): JsonItem[] => { + const processJsonData = (obj: unknown, parentPath: string[] = []): JsonItem[] => { if (obj === null || obj === undefined) { return []; } @@ -88,7 +88,7 @@ export function EnhancedJsonViewer({ return [{ key: 'value', value: obj, - type: typeof obj as any, + type: typeof obj as JsonItem["type"], path: parentPath, isExpandable: false }]; @@ -113,7 +113,7 @@ export function EnhancedJsonViewer({ return { key, value, - type: type as any, + type: type as JsonItem["type"], path, isExpandable: type === 'object' || type === 'array' }; @@ -133,50 +133,56 @@ export function EnhancedJsonViewer({ const isExpanded = expandedItems.has(itemKey); switch (item.type) { - case 'string': + case 'string': { + const stringValue = typeof item.value === 'string' ? item.value : String(item.value); return ( openModal(item.value, item.path, formatLabel(item.key))} + onOpenModal={() => openModal(stringValue, item.path, formatLabel(item.key))} maxInlineHeight={maxInlineHeight} /> ); + } - case 'number': + case 'number': { + const numberValue = typeof item.value === 'number' ? item.value : Number(item.value ?? 0); return (
- {item.value.toLocaleString()} + {numberValue.toLocaleString()}
); + } - case 'boolean': + case 'boolean': { + const boolValue = Boolean(item.value); return (
- - {item.value ? 'true' : 'false'} + + {boolValue ? 'true' : 'false'}
); + } case 'null': return ( @@ -193,7 +199,8 @@ export function EnhancedJsonViewer({
); - case 'array': + case 'array': { + const arrayValue = Array.isArray(item.value) ? item.value : []; return (
toggleExpanded(itemKey)}> @@ -218,7 +225,7 @@ export function EnhancedJsonViewer({ Array - {item.value.length} items + {arrayValue.length} items
@@ -244,7 +251,7 @@ export function EnhancedJsonViewer({
- {item.value.slice(0, 10).map((arrayItem: any, index: number) => ( + {arrayValue.slice(0, 10).map((arrayItem: unknown, index: number) => (
[{index}] @@ -272,9 +279,9 @@ export function EnhancedJsonViewer({
))} - {item.value.length > 10 && ( + {arrayValue.length > 10 && (
- ... and {item.value.length - 10} more items + ... and {arrayValue.length - 10} more items
)} @@ -282,8 +289,12 @@ export function EnhancedJsonViewer({ ); + } - case 'object': + case 'object': { + const objectValue = item.value && typeof item.value === 'object' && !Array.isArray(item.value) + ? (item.value as Record) + : {}; return (
toggleExpanded(itemKey)}> @@ -308,7 +319,7 @@ export function EnhancedJsonViewer({ Object - {Object.keys(item.value).length} keys + {Object.keys(objectValue).length} keys
@@ -335,7 +346,7 @@ export function EnhancedJsonViewer({
@@ -343,6 +354,7 @@ export function EnhancedJsonViewer({ ); + } default: return ( diff --git a/control-plane/web/client/src/components/reasoners/ExecutionQueue.tsx b/control-plane/web/client/src/components/reasoners/ExecutionQueue.tsx index 95ca4b2a..e3e91e82 100644 --- a/control-plane/web/client/src/components/reasoners/ExecutionQueue.tsx +++ b/control-plane/web/client/src/components/reasoners/ExecutionQueue.tsx @@ -1,4 +1,4 @@ -import { useState, forwardRef, useImperativeHandle } from 'react'; +import { useState, forwardRef, useImperativeHandle, type KeyboardEvent, type MouseEvent } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from '../ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; @@ -362,7 +362,7 @@ export const ExecutionQueue = forwardRef } }; - const handleExecutionCardClick = (execution: QueuedExecution, event: React.MouseEvent) => { + const handleExecutionCardClick = (execution: QueuedExecution, event: MouseEvent) => { // Prevent navigation if clicking on action buttons if ((event.target as HTMLElement).closest('button')) { return; @@ -383,7 +383,7 @@ export const ExecutionQueue = forwardRef } }; - const handleViewDetailsClick = (event: React.MouseEvent, execution: QueuedExecution) => { + const handleViewDetailsClick = (event: MouseEvent, execution: QueuedExecution) => { event.stopPropagation(); if (execution.execution_id && execution.page_available) { handleNavigateToExecution(execution); @@ -449,10 +449,17 @@ export const ExecutionQueue = forwardRef role={execution.execution_id ? "button" : undefined} tabIndex={execution.execution_id ? 0 : undefined} aria-label={execution.execution_id ? `Navigate to execution ${execution.execution_id}` : undefined} - onKeyDown={execution.execution_id ? (e) => { + onKeyDown={execution.execution_id ? (e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); - handleExecutionCardClick(execution, e as any); + if (execution.execution_id && execution.page_available) { + handleNavigateToExecution(execution); + } else { + const isCurrentlySelected = selectedExecution === execution.id; + const newSelection = isCurrentlySelected ? null : execution.id; + setSelectedExecution(newSelection); + onExecutionSelect?.(isCurrentlySelected ? null : execution); + } } } : undefined} > @@ -562,10 +569,17 @@ export const ExecutionQueue = forwardRef ? `Execution ${execution.execution_id} page not ready yet` : `View execution details` } - onKeyDown={(e) => { + onKeyDown={(e: KeyboardEvent) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); - handleExecutionCardClick(execution, e as any); + if (execution.execution_id && execution.page_available) { + handleNavigateToExecution(execution); + } else { + const isCurrentlySelected = selectedExecution === execution.id; + const newSelection = isCurrentlySelected ? null : execution.id; + setSelectedExecution(newSelection); + onExecutionSelect?.(isCurrentlySelected ? null : execution); + } } }} > diff --git a/control-plane/web/client/src/components/vc/VerifiableCredentialBadge.tsx b/control-plane/web/client/src/components/vc/VerifiableCredentialBadge.tsx index b582ec02..c10ee080 100644 --- a/control-plane/web/client/src/components/vc/VerifiableCredentialBadge.tsx +++ b/control-plane/web/client/src/components/vc/VerifiableCredentialBadge.tsx @@ -21,11 +21,12 @@ import type { WorkflowVC, ComprehensiveVCVerificationResult } from "../../types/did"; +import type { VCStatusData } from "../../types/vc"; interface VerifiableCredentialBadgeProps { hasVC: boolean; status: string; - vcData?: ExecutionVC | WorkflowVC; + vcData?: ExecutionVC | WorkflowVC | VCStatusData; workflowId?: string; // For workflow-level VCs executionId?: string; // For execution-level VCs showCopyButton?: boolean; // Show copy button for detail pages diff --git a/control-plane/web/client/src/hooks/useVCVerification.ts b/control-plane/web/client/src/hooks/useVCVerification.ts index 613250ec..b1486fb1 100644 --- a/control-plane/web/client/src/hooks/useVCVerification.ts +++ b/control-plane/web/client/src/hooks/useVCVerification.ts @@ -13,6 +13,7 @@ import { getExecutionVCStatus, getWorkflowVCStatuses } from '../services/vcApi'; +import type { VCStatusData } from '../types/vc'; /** * Hook for managing VC verification operations @@ -169,13 +170,7 @@ export function useAuditTrail(workflowId: string) { * Hook for managing execution VC status */ export function useExecutionVCStatus(executionId: string) { - const [vcStatus, setVCStatus] = useState<{ - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; // Include vc_document for download functionality - } | null>(null); + const [vcStatus, setVCStatus] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); diff --git a/control-plane/web/client/src/pages/EnhancedExecutionDetailPage.tsx b/control-plane/web/client/src/pages/EnhancedExecutionDetailPage.tsx index 4dd188ba..ced74e9b 100644 --- a/control-plane/web/client/src/pages/EnhancedExecutionDetailPage.tsx +++ b/control-plane/web/client/src/pages/EnhancedExecutionDetailPage.tsx @@ -13,6 +13,7 @@ import { ErrorBoundary } from "../components/ErrorBoundary"; import { getExecutionDetails, retryExecutionWebhook } from "../services/executionsApi"; import { getExecutionVCStatus } from "../services/vcApi"; import type { WorkflowExecution } from "../types/executions"; +import type { VCStatusData } from "../types/vc"; import { Database, Bug, Shield, Wrench, FileText, RadioTower, Cog, PauseCircle } from "../components/ui/icon-bridge"; import { Badge } from "../components/ui/badge"; import { ExecutionWebhookActivity } from "../components/execution/ExecutionWebhookActivity"; @@ -43,16 +44,7 @@ export function EnhancedExecutionDetailPage() { // Core data state const [execution, setExecution] = useState(null); - const [vcStatus, setVcStatus] = useState<{ - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - storage_uri?: string; - document_size_bytes?: number; - original_status?: string; - } | null>(null); + const [vcStatus, setVcStatus] = useState(null); const [loading, setLoading] = useState(true); const [vcLoading, setVcLoading] = useState(true); const [error, setError] = useState(null); diff --git a/control-plane/web/client/src/pages/ExecutionDetailPage.tsx b/control-plane/web/client/src/pages/ExecutionDetailPage.tsx index c38829ca..ba6d7cba 100644 --- a/control-plane/web/client/src/pages/ExecutionDetailPage.tsx +++ b/control-plane/web/client/src/pages/ExecutionDetailPage.tsx @@ -17,6 +17,7 @@ import { Card, CardContent } from "../components/ui/card"; import { getExecutionDetails } from "../services/executionsApi"; import { getExecutionVCStatus } from "../services/vcApi"; import type { WorkflowExecution } from "../types/executions"; +import type { VCStatusData } from "../types/vc"; function InlineCopyButton({ value }: { value: string }) { const [copied, setCopied] = useState(false); @@ -75,13 +76,7 @@ export function ExecutionDetailPage() { const { executionId } = useParams<{ executionId: string }>(); const navigate = useNavigate(); const [execution, setExecution] = useState(null); - const [vcStatus, setVcStatus] = useState<{ - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - } | null>(null); + const [vcStatus, setVcStatus] = useState(null); const [loading, setLoading] = useState(true); const [vcLoading, setVcLoading] = useState(true); const [error, setError] = useState(null); diff --git a/control-plane/web/client/src/pages/RedesignedExecutionDetailPage.tsx b/control-plane/web/client/src/pages/RedesignedExecutionDetailPage.tsx index 34f8c90e..18057301 100644 --- a/control-plane/web/client/src/pages/RedesignedExecutionDetailPage.tsx +++ b/control-plane/web/client/src/pages/RedesignedExecutionDetailPage.tsx @@ -10,18 +10,13 @@ import { CollapsibleSection } from "../components/execution/CollapsibleSection"; import { getExecutionDetails } from "../services/executionsApi"; import { getExecutionVCStatus } from "../services/vcApi"; import type { WorkflowExecution } from "../types/executions"; +import type { VCStatusData } from "../types/vc"; import { Settings } from "@/components/ui/icon-bridge"; export function RedesignedExecutionDetailPage() { const { executionId } = useParams<{ executionId: string }>(); const [execution, setExecution] = useState(null); - const [vcStatus, setVcStatus] = useState<{ - has_vc: boolean; - vc_id?: string; - status: string; - created_at?: string; - vc_document?: any; - } | null>(null); + const [vcStatus, setVcStatus] = useState(null); const [loading, setLoading] = useState(true); const [vcLoading, setVcLoading] = useState(true); const [error, setError] = useState(null); diff --git a/control-plane/web/client/src/types/vc.ts b/control-plane/web/client/src/types/vc.ts new file mode 100644 index 00000000..86e67d1e --- /dev/null +++ b/control-plane/web/client/src/types/vc.ts @@ -0,0 +1,11 @@ +import type { ExecutionVC } from "./did"; + +export interface VCStatusData + extends Partial> { + has_vc: boolean; + vc_id?: string; + status: string; + created_at?: string; + vc_document?: Record; + original_status?: string; +} diff --git a/control-plane/web/client/src/types/workflows.ts b/control-plane/web/client/src/types/workflows.ts index e53a1879..cb28dcbe 100644 --- a/control-plane/web/client/src/types/workflows.ts +++ b/control-plane/web/client/src/types/workflows.ts @@ -126,6 +126,8 @@ export interface WorkflowDAGLightweightNode { completed_at?: string; duration_ms?: number; workflow_depth: number; + agent_name?: string; + task_name?: string; } export interface WorkflowDAGLightweightResponse {