1+ /**
2+ * ExecutionLog — pure rendering component for the execution timeline.
3+ *
4+ * All log state management, entry merge semantics, display projection,
5+ * and item helper logic live in {@link ../../ExecutionLogModel.js}.
6+ * This file is exclusively concerned with rendering.
7+ */
8+
19import React , { useState } from 'react' ;
210
311import {
@@ -12,206 +20,17 @@ import { Link } from '@carbon/react';
1220
1321import OutputEditor from './OutputEditor' ;
1422
15- const STATUS_LABELS = {
16- deploying : 'Process deployed' ,
17- 'starting-instance' : 'Process instance created' ,
18- executing : null ,
19- completed : 'Process instance completed' ,
20- incident : 'Incident' ,
21- canceled : 'Test canceled'
22- } ;
23-
24- const WAITING_ITEM_LABELS = {
25- 'job-active' : 'Job is active' ,
26- 'job-completed' : 'Job completed' ,
27- 'job-inactive' : 'Job waiting to be activated' ,
28- 'user-task-form' : 'User task waiting for completion' ,
29- 'message-subscription' : 'Waiting for message' ,
30- 'boundary-event' : 'Boundary event triggered' ,
31- 'event-sub-process' : 'Event sub-process triggered'
32- } ;
33-
34- const WAITING_ITEM_FINISHED_LABELS = {
35- 'user-task-form' : 'User task completed'
36- } ;
37-
38- /**
39- * Get display label for a waiting item.
40- *
41- * @param {import('../../types').WaitingItem } item
42- * @param {boolean } [isFinished]
43- * @returns {string }
44- */
45- function getWaitingItemLabel ( item , isFinished ) {
46- if ( isFinished && WAITING_ITEM_FINISHED_LABELS [ item . type ] ) {
47- return WAITING_ITEM_FINISHED_LABELS [ item . type ] ;
48- }
49-
50- return WAITING_ITEM_LABELS [ item . type ] || item . type ;
51- }
52-
53- /**
54- * Get details for a waiting item to display in expanded view.
55- *
56- * @param {import('../../types').WaitingItem } item
57- * @returns {{ label: string; value: string }[] }
58- */
59- function getWaitingItemDetails ( item ) {
60- if ( ! item . data ) {
61- return [ ] ;
62- }
63-
64- // Handle boundary events and event sub-processes
65- if ( item . type === 'boundary-event' || item . type === 'event-sub-process' ) {
66- const details = [ ] ;
67-
68- if ( item . data . elementId ) {
69- details . push ( { label : 'Element' , value : item . data . elementId } ) ;
70- }
71-
72- if ( item . data . elementName ) {
73- details . push ( { label : 'Name' , value : item . data . elementName } ) ;
74- }
75-
76- if ( item . data . state ) {
77- details . push ( { label : 'State' , value : item . data . state } ) ;
78- }
79-
80- if ( item . data . startDate ) {
81- details . push ( { label : 'Started' , value : new Date ( item . data . startDate ) . toLocaleTimeString ( ) } ) ;
82- }
83-
84- if ( item . data . elementInstanceKey ) {
85- details . push ( { label : 'Instance Key' , value : item . data . elementInstanceKey } ) ;
86- }
87-
88- return details ;
89- }
90-
91- // Handle job items
92- if ( ! isJobItem ( item ) ) {
93- return [ ] ;
94- }
95-
96- const details = [ ] ;
97-
98- if ( item . data . type ) {
99- details . push ( { label : 'Type' , value : item . data . type } ) ;
100- }
101-
102- if ( item . data . elementId ) {
103- details . push ( { label : 'Element' , value : item . data . elementId } ) ;
104- }
105-
106- const listenerLabel = getExecutionListenerLabel ( item . data ) ;
107- if ( listenerLabel ) {
108- details . push ( { label : 'Kind' , value : listenerLabel } ) ;
109- }
110-
111- if ( item . data . state ) {
112- details . push ( { label : 'State' , value : item . data . state } ) ;
113- }
114-
115- if ( item . data . jobKey ) {
116- details . push ( { label : 'Job Key' , value : item . data . jobKey } ) ;
117- }
118-
119- return details ;
120- }
121-
122- /**
123- * Get execution listener label if the job is an execution listener.
124- *
125- * @param {Object } jobData
126- * @returns {string|null }
127- */
128- function getExecutionListenerLabel ( jobData ) {
129- if ( ! jobData || jobData . kind !== 'EXECUTION_LISTENER' ) {
130- return null ;
131- }
132-
133- const eventType = jobData . listenerEventType ;
134-
135- if ( eventType === 'START' ) {
136- return 'start execution listener' ;
137- }
138-
139- if ( eventType === 'END' ) {
140- return 'end execution listener' ;
141- }
142-
143- return 'execution listener' ;
144- }
145-
146- /**
147- * @param {import('../../types').WaitingItem } item
148- * @returns {boolean }
149- */
150- function isJobItem ( item ) {
151- return item . type === 'job-active'
152- || item . type === 'job-completed'
153- || item . type === 'job-inactive' ;
154- }
155-
156- /**
157- * Determine if a waiting item is still pending (should show blue dot).
158- * Completed/triggered items show green.
159- *
160- * @param {import('../../types').WaitingItem } item
161- * @returns {boolean }
162- */
163- function isPendingItem ( item ) {
164- switch ( item . type ) {
165- case 'job-active' :
166- case 'job-inactive' :
167- case 'user-task-form' :
168- case 'message-subscription' :
169- return true ;
170- default :
171- return false ;
172- }
173- }
174-
175- /**
176- * Get badge texts for waiting items.
177- *
178- * @param {import('../../types').WaitingItem } item
179- * @returns {{ text: string, variant?: string }[] }
180- */
181- function getWaitingItemBadges ( item ) {
182- const badges = [ ] ;
183-
184- // Execution listener start/end badge
185- if ( isJobItem ( item ) && item . data ?. kind === 'EXECUTION_LISTENER' && item . data . listenerEventType ) {
186- badges . push ( {
187- text : item . data . listenerEventType === 'START' ? 'start' : 'end' ,
188- variant : item . data . listenerEventType === 'START' ? 'start' : 'end'
189- } ) ;
190- }
191-
192- // Job type badge
193- if ( isJobItem ( item ) && item . data ?. type ) {
194- badges . push ( { text : item . data . type } ) ;
195- }
196-
197- // Boundary event or event sub-process badge - show element name or ID
198- if ( ( item . type === 'boundary-event' || item . type === 'event-sub-process' ) && item . data ) {
199- const label = item . data . elementName || item . data . elementId ;
200- if ( label ) badges . push ( { text : label } ) ;
201- }
202-
203- return badges ;
204- }
205-
206- /**
207- * Format a timestamp as a human-readable time string.
208- *
209- * @param {number } timestamp
210- * @returns {string }
211- */
212- function formatTime ( timestamp ) {
213- return new Date ( timestamp ) . toLocaleTimeString ( ) ;
214- }
23+ import {
24+ STATUS_LABELS ,
25+ getDisplayEntries ,
26+ getWaitingItemLabel ,
27+ getWaitingItemDetails ,
28+ getWaitingItemBadges ,
29+ isPendingItem ,
30+ formatTime ,
31+ formatElementType ,
32+ isFinished as checkIsFinished
33+ } from '../../ExecutionLogModel' ;
21534
21635/**
21736 * Renders an execution log that builds up during task execution.
@@ -232,20 +51,8 @@ export function ExecutionLog({ entries, tasklistBaseUrl, currentOperateUrl, onSe
23251 }
23352
23453 const lastEntry = entries [ entries . length - 1 ] ;
235-
236- // Check if execution has finished (completed, incident, or canceled)
237- const isFinished = entries . some (
238- e => e . type === 'status' && ( e . status === 'completed' || e . status === 'incident' || e . status === 'canceled' )
239- ) ;
240-
241- // Filter out 'executing' status once finished - it's just a temporary placeholder
242- // During execution, move 'executing' to the end so it's always last
243- const displayEntries = isFinished
244- ? entries . filter ( e => ! ( e . type === 'status' && e . status === 'executing' ) )
245- : [
246- ...entries . filter ( e => ! ( e . type === 'status' && e . status === 'executing' ) ) ,
247- ...entries . filter ( e => e . type === 'status' && e . status === 'executing' )
248- ] ;
54+ const isFinished = checkIsFinished ( entries ) ;
55+ const displayEntries = getDisplayEntries ( entries ) ;
24956
25057 return (
25158 < div className = "execution-log" >
@@ -355,19 +162,6 @@ function ChildElement({ child }) {
355162 ) ;
356163}
357164
358- /**
359- * Format an element type for display.
360- *
361- * @param {string } type
362- * @returns {string }
363- */
364- function formatElementType ( type ) {
365- return ( type || '' )
366- . toLowerCase ( )
367- . replace ( / _ / g, ' ' )
368- . replace ( / \b \w / g, c => c . toUpperCase ( ) ) ;
369- }
370-
371165function WaitingEntry ( { entry, isLast, isFinished, onSelectElement } ) {
372166 if ( ! entry . items ?. length ) {
373167 return null ;
0 commit comments