11'use client'
22
3- import { lazy , memo , Suspense , useEffect , useMemo , useState } from 'react'
3+ import { lazy , memo , Suspense , useEffect , useMemo } from 'react'
44import { createLogger } from '@sim/logger'
5- import { formatDuration } from '@sim/utils/formatting'
65import { Square } from 'lucide-react'
76import { useRouter } from 'next/navigation'
8- import { Button , Eye , PlayOutline , Skeleton , Tooltip } from '@/components/emcn'
7+ import { Button , PlayOutline , Skeleton , Tooltip } from '@/components/emcn'
98import {
109 Download ,
1110 FileX ,
@@ -14,15 +13,12 @@ import {
1413 SquareArrowUpRight ,
1514 WorkflowX ,
1615} from '@/components/emcn/icons'
17- import { BASE_EXECUTION_CHARGE } from '@/lib/billing/constants'
1816import type { FilePreviewSession } from '@/lib/copilot/request/session'
1917import {
2018 cancelRunToolExecution ,
2119 markRunToolManuallyStopped ,
2220 reportManualRunToolStop ,
2321} from '@/lib/copilot/tools/client/run-tool-execution'
24- import { cn } from '@/lib/core/utils/cn'
25- import { filterHiddenOutputKeys } from '@/lib/logs/execution/trace-spans/trace-spans'
2622import {
2723 downloadWorkspaceFile ,
2824 getFileExtension ,
@@ -43,18 +39,7 @@ import type {
4339 MothershipResource ,
4440} from '@/app/workspace/[workspaceId]/home/types'
4541import { KnowledgeBase } from '@/app/workspace/[workspaceId]/knowledge/[id]/base'
46- import {
47- ExecutionSnapshot ,
48- FileCards ,
49- TraceSpans ,
50- WorkflowOutputSection ,
51- } from '@/app/workspace/[workspaceId]/logs/components'
52- import {
53- formatDate ,
54- getDisplayStatus ,
55- StatusBadge ,
56- TriggerBadge ,
57- } from '@/app/workspace/[workspaceId]/logs/utils'
42+ import { LogDetailsContent } from '@/app/workspace/[workspaceId]/logs/components'
5843import {
5944 useUserPermissionsContext ,
6045 useWorkspacePermissionsContext ,
@@ -68,7 +53,6 @@ import { downloadTableExport } from '@/hooks/queries/tables'
6853import { useWorkflows } from '@/hooks/queries/workflows'
6954import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
7055import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
71- import { formatCost } from '@/providers/utils'
7256import { useExecutionStore } from '@/stores/execution/store'
7357import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
7458
@@ -639,26 +623,6 @@ interface EmbeddedLogProps {
639623
640624function EmbeddedLog ( { logId } : EmbeddedLogProps ) {
641625 const { data : log , isLoading } = useLogDetail ( logId )
642- const [ isSnapshotOpen , setIsSnapshotOpen ] = useState ( false )
643-
644- const logStatus = getDisplayStatus ( log ?. status )
645-
646- const workflowOutput = useMemo ( ( ) => {
647- const executionData = log ?. executionData as
648- | { finalOutput ?: Record < string , unknown > }
649- | undefined
650- if ( ! executionData ?. finalOutput ) return null
651- return filterHiddenOutputKeys ( executionData . finalOutput ) as Record < string , unknown >
652- } , [ log ?. executionData ] )
653-
654- const isWorkflowExecutionLog =
655- ! ! log &&
656- ( ( log . trigger === 'manual' && ! ! log . duration ) ||
657- ! ! ( log . executionData ?. enhanced && log . executionData ?. traceSpans ) )
658-
659- const hasCostInfo = isWorkflowExecutionLog && log ?. cost
660-
661- const formattedTimestamp = log ? formatDate ( log . createdAt ) : null
662626
663627 if ( isLoading ) return LOADING_SKELETON
664628
@@ -676,232 +640,9 @@ function EmbeddedLog({ logId }: EmbeddedLogProps) {
676640 )
677641 }
678642
679- const workflowColor =
680- log . trigger === 'mothership'
681- ? '#ec4899'
682- : log . workflow ?. color || ( ! log . workflowId ? 'var(--text-tertiary)' : undefined )
683-
684- const totalToolCost = ( ( ) => {
685- const models = ( log . cost as Record < string , unknown > ) ?. models as
686- | Record < string , { toolCost ?: number } >
687- | undefined
688- return models ? Object . values ( models ) . reduce ( ( sum , m ) => sum + ( m ?. toolCost || 0 ) , 0 ) : 0
689- } ) ( )
690-
691643 return (
692- < div className = 'flex h-full flex-col overflow-y-auto' >
693- < div className = 'flex flex-col gap-2.5 p-4 pb-6' >
694- { /* Timestamp & Workflow Row */ }
695- < div className = 'flex min-w-0 items-center gap-4 px-[1px]' >
696- < div className = 'flex w-[140px] flex-shrink-0 flex-col gap-2' >
697- < div className = 'font-medium text-[var(--text-tertiary)] text-caption' > Timestamp</ div >
698- < div className = 'flex items-center gap-1.5' >
699- < span className = 'font-medium text-[var(--text-secondary)] text-sm' >
700- { formattedTimestamp ?. compactDate || 'N/A' }
701- </ span >
702- < span className = 'font-medium text-[var(--text-secondary)] text-sm' >
703- { formattedTimestamp ?. compactTime || 'N/A' }
704- </ span >
705- </ div >
706- </ div >
707- < div className = 'flex w-0 min-w-0 flex-1 flex-col gap-2' >
708- < div className = 'font-medium text-[var(--text-tertiary)] text-caption' >
709- { log . trigger === 'mothership' ? 'Job' : 'Workflow' }
710- </ div >
711- < div className = 'flex min-w-0 items-center gap-2' >
712- < div
713- className = 'h-[10px] w-[10px] flex-shrink-0 rounded-[3px] border-[1.5px]'
714- style = { {
715- backgroundColor : workflowColor ,
716- borderColor : workflowColor ? workflowBorderColor ( workflowColor ) : undefined ,
717- backgroundClip : 'padding-box' ,
718- } }
719- />
720- < span className = 'min-w-0 flex-1 truncate font-medium text-[var(--text-secondary)] text-sm' >
721- { log . trigger === 'mothership'
722- ? log . jobTitle || 'Untitled Job'
723- : log . workflow ?. name || ( ! log . workflowId ? 'Deleted Workflow' : 'Unknown' ) }
724- </ span >
725- </ div >
726- </ div >
727- </ div >
728-
729- { /* Run ID */ }
730- { log . executionId && (
731- < div className = 'flex flex-col gap-1.5 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-2.5 py-2' >
732- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' > Run ID</ span >
733- < span className = 'truncate font-medium text-[var(--text-secondary)] text-sm' >
734- { log . executionId }
735- </ span >
736- </ div >
737- ) }
738-
739- { /* Details Section */ }
740- < div className = '-my-1 flex min-w-0 flex-col overflow-hidden' >
741- < div className = 'flex h-[48px] items-center justify-between border-[var(--border)] border-b p-2' >
742- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' > Level</ span >
743- < StatusBadge status = { logStatus } />
744- </ div >
745- < div className = 'flex h-[48px] items-center justify-between border-[var(--border)] border-b p-2' >
746- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' > Trigger</ span >
747- { log . trigger ? (
748- < TriggerBadge trigger = { log . trigger } />
749- ) : (
750- < span className = 'font-medium text-[var(--text-secondary)] text-caption' > —</ span >
751- ) }
752- </ div >
753- < div
754- className = { cn (
755- 'flex h-[48px] items-center justify-between border-b p-2' ,
756- log . deploymentVersion ? 'border-[var(--border)]' : 'border-transparent'
757- ) }
758- >
759- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' > Duration</ span >
760- < span className = 'font-medium text-[var(--text-secondary)] text-small' >
761- { formatDuration ( log . duration , { precision : 2 } ) || '—' }
762- </ span >
763- </ div >
764- { log . deploymentVersion && (
765- < div className = 'flex h-[48px] items-center gap-2 p-2' >
766- < span className = 'flex-shrink-0 font-medium text-[var(--text-tertiary)] text-caption' >
767- Version
768- </ span >
769- < div className = 'flex w-0 flex-1 justify-end' >
770- < span className = 'max-w-full truncate rounded-md bg-[var(--badge-success-bg)] px-[9px] py-0.5 font-medium text-[var(--badge-success-text)] text-caption' >
771- { log . deploymentVersionName || `v${ log . deploymentVersion } ` }
772- </ span >
773- </ div >
774- </ div >
775- ) }
776- </ div >
777-
778- { /* Workflow State Snapshot */ }
779- { isWorkflowExecutionLog && log . executionId && log . trigger !== 'mothership' && (
780- < div className = '-mt-2 flex flex-col gap-1.5 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-2.5 py-2' >
781- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
782- Workflow State
783- </ span >
784- < Button
785- variant = 'active'
786- onClick = { ( ) => setIsSnapshotOpen ( true ) }
787- className = 'flex w-full items-center justify-between px-2.5 py-1.5'
788- >
789- < span className = 'font-medium text-caption' > View Snapshot</ span >
790- < Eye className = 'h-[14px] w-[14px]' />
791- </ Button >
792- </ div >
793- ) }
794-
795- { /* Workflow Output */ }
796- { isWorkflowExecutionLog && workflowOutput && (
797- < div className = 'mt-1 flex flex-col gap-1.5 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-2.5 py-2 dark:bg-transparent' >
798- < span
799- className = { cn (
800- 'font-medium text-caption' ,
801- workflowOutput . error ? 'text-[var(--text-error)]' : 'text-[var(--text-tertiary)]'
802- ) }
803- >
804- Workflow Output
805- </ span >
806- < WorkflowOutputSection output = { workflowOutput } />
807- </ div >
808- ) }
809-
810- { /* Trace Spans */ }
811- { isWorkflowExecutionLog && log . executionData ?. traceSpans && (
812- < div className = 'mt-1 flex flex-col gap-1.5 rounded-md border border-[var(--border)] bg-[var(--surface-2)] px-2.5 py-2 dark:bg-transparent' >
813- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' > Trace Span</ span >
814- < TraceSpans traceSpans = { log . executionData . traceSpans } />
815- </ div >
816- ) }
817-
818- { /* Files */ }
819- { log . files && log . files . length > 0 && < FileCards files = { log . files } isExecutionFile /> }
820-
821- { /* Cost Breakdown */ }
822- { hasCostInfo && (
823- < div className = 'flex flex-col gap-2' >
824- < span className = 'px-[1px] font-medium text-[var(--text-tertiary)] text-caption' >
825- Cost Breakdown
826- </ span >
827- < div className = 'flex flex-col gap-1 rounded-md border border-[var(--border)]' >
828- < div className = 'flex flex-col gap-2.5 rounded-md p-2.5' >
829- < div className = 'flex items-center justify-between' >
830- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
831- Base Execution:
832- </ span >
833- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
834- { formatCost ( BASE_EXECUTION_CHARGE ) }
835- </ span >
836- </ div >
837- < div className = 'flex items-center justify-between' >
838- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
839- Model Input:
840- </ span >
841- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
842- { formatCost ( log . cost ?. input || 0 ) }
843- </ span >
844- </ div >
845- < div className = 'flex items-center justify-between' >
846- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
847- Model Output:
848- </ span >
849- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
850- { formatCost ( log . cost ?. output || 0 ) }
851- </ span >
852- </ div >
853- { totalToolCost > 0 && (
854- < div className = 'flex items-center justify-between' >
855- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
856- Tool Usage:
857- </ span >
858- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
859- { formatCost ( totalToolCost ) }
860- </ span >
861- </ div >
862- ) }
863- </ div >
864- < div className = 'border-[var(--border)] border-t' />
865- < div className = 'flex flex-col gap-2.5 rounded-md p-2.5' >
866- < div className = 'flex items-center justify-between' >
867- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
868- Total:
869- </ span >
870- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
871- { formatCost ( log . cost ?. total || 0 ) }
872- </ span >
873- </ div >
874- < div className = 'flex items-center justify-between' >
875- < span className = 'font-medium text-[var(--text-tertiary)] text-caption' >
876- Tokens:
877- </ span >
878- < span className = 'font-medium text-[var(--text-secondary)] text-caption' >
879- { log . cost ?. tokens ?. input || log . cost ?. tokens ?. prompt || 0 } in /{ ' ' }
880- { log . cost ?. tokens ?. output || log . cost ?. tokens ?. completion || 0 } out
881- </ span >
882- </ div >
883- </ div >
884- </ div >
885- < div className = 'flex items-center justify-center rounded-md bg-[var(--surface-2)] p-2 text-center' >
886- < p className = 'font-medium text-[var(--text-subtle)] text-xs' >
887- Total cost includes a base execution charge of { formatCost ( BASE_EXECUTION_CHARGE ) } { ' ' }
888- plus any model and tool usage costs.
889- </ p >
890- </ div >
891- </ div >
892- ) }
893- </ div >
894-
895- { /* Frozen Canvas Modal */ }
896- { log . executionId && (
897- < ExecutionSnapshot
898- executionId = { log . executionId }
899- traceSpans = { log . executionData ?. traceSpans }
900- isModal
901- isOpen = { isSnapshotOpen }
902- onClose = { ( ) => setIsSnapshotOpen ( false ) }
903- />
904- ) }
644+ < div className = 'flex h-full flex-col overflow-hidden px-3.5 pt-3' >
645+ < LogDetailsContent log = { log } />
905646 </ div >
906647 )
907648}
0 commit comments