diff --git a/inc/Core/Admin/Pages/Jobs/assets/css/jobs-page.css b/inc/Core/Admin/Pages/Jobs/assets/css/jobs-page.css index b5d4c41fc..eb72d41da 100644 --- a/inc/Core/Admin/Pages/Jobs/assets/css/jobs-page.css +++ b/inc/Core/Admin/Pages/Jobs/assets/css/jobs-page.css @@ -83,16 +83,29 @@ /* Table Column Widths */ .datamachine-col-job-id { - width: 80px; + width: 70px; +} + +.datamachine-col-pipeline, +.datamachine-col-flow { + width: 140px; +} + +.datamachine-col-label { + /* Flexible — takes remaining space */ +} + +.datamachine-col-label-cell { + word-break: break-word; } .datamachine-col-status { - width: 100px; + width: 180px; } .datamachine-col-created, .datamachine-col-completed { - width: 140px; + width: 130px; } /* Job Status Colors - now using shared classes from root.css: @@ -102,6 +115,24 @@ * .datamachine-status--neutral */ +/* Status detail (compound status reason) */ +.datamachine-status-detail { + display: block; + font-size: 11px; + color: #646970; + margin-top: 2px; + line-height: 1.3; + word-break: break-word; +} + +/* Pipeline/Flow cell styling */ +.datamachine-col-pipeline, +.datamachine-col-flow { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + /* Modal Styles */ @@ -273,6 +304,8 @@ gap: 15px; } + .datamachine-col-label, + .datamachine-col-label-cell, .datamachine-col-created, .datamachine-col-completed { display: none; diff --git a/inc/Core/Admin/Pages/Jobs/assets/react/components/JobsTable.jsx b/inc/Core/Admin/Pages/Jobs/assets/react/components/JobsTable.jsx index c5811c32d..4f95f397c 100644 --- a/inc/Core/Admin/Pages/Jobs/assets/react/components/JobsTable.jsx +++ b/inc/Core/Admin/Pages/Jobs/assets/react/components/JobsTable.jsx @@ -38,10 +38,31 @@ const formatStatus = ( status ) => { if ( ! status ) { return __( 'Unknown', 'data-machine' ); } - return ( + // Show base status as the label, full detail on hover via title attr. + const formatted = status.charAt( 0 ).toUpperCase() + - status.slice( 1 ).replace( /_/g, ' ' ) - ); + status.slice( 1 ).replace( /_/g, ' ' ); + return formatted; +}; + +/** + * Extract the base status (before " - " detail separator). + */ +const getBaseStatus = ( status ) => { + if ( ! status ) { + return ''; + } + return status.split( ' - ' )[ 0 ]; +}; + +/** + * Get the detail portion of a compound status (after " - "). + */ +const getStatusDetail = ( status ) => { + if ( ! status || ! status.includes( ' - ' ) ) { + return null; + } + return status.split( ' - ' ).slice( 1 ).join( ' - ' ); }; /** @@ -163,7 +184,7 @@ const ChildRows = ( { parentJobId } ) => { if ( isLoading ) { return ( - +
@@ -175,11 +196,24 @@ const ChildRows = ( { parentJobId } ) => { ); } - if ( isError || ! children || children.length === 0 ) { + if ( isError ) { + return ( + + + { __( 'Failed to load child jobs.', 'data-machine' ) } + + + ); + } + + if ( ! children || children.length === 0 ) { return ( - - { __( 'No child jobs found.', 'data-machine' ) } + + { __( + 'Child jobs were scheduled but have not been recorded yet.', + 'data-machine' + ) } ); @@ -194,14 +228,26 @@ const ChildRows = ( { parentJobId } ) => { { child.job_id } - { child.display_label || - child.label || - __( 'Child job', 'data-machine' ) } + { child.pipeline_name || '\u2014' } - - { formatStatus( child.status ) } + { child.flow_name || '\u2014' } + + + { child.label || '\u2014' } + + + + { formatStatus( getBaseStatus( child.status ) || child.status ) } + { getStatusDetail( child.status ) && ( + + { getStatusDetail( child.status ) } + + ) } { child.created_at_display || '' } { child.completed_at_display || '' } @@ -212,6 +258,34 @@ const ChildRows = ( { parentJobId } ) => { /** * Single job row — handles expand/collapse for batch parents. */ +/** + * Format pipeline display value. + * Shows name for DB pipelines, "Direct" for direct execution, em dash for null. + */ +const formatPipeline = ( job ) => { + if ( job.pipeline_name ) { + return job.pipeline_name; + } + if ( job.pipeline_id === 'direct' ) { + return __( 'Direct', 'data-machine' ); + } + return '\u2014'; +}; + +/** + * Format flow display value. + * Shows name for DB flows, "Direct" for direct execution, em dash for null. + */ +const formatFlow = ( job ) => { + if ( job.flow_name ) { + return job.flow_name; + } + if ( job.flow_id === 'direct' ) { + return __( 'Direct', 'data-machine' ); + } + return '\u2014'; +}; + const JobRow = ( { job, isExpanded, onToggle } ) => { const isBatch = hasChildren( job ); @@ -238,17 +312,27 @@ const JobRow = ( { job, isExpanded, onToggle } ) => { - { job.display_label || - job.label || - ( job.pipeline_name && job.flow_name - ? `${ job.pipeline_name } \u2192 ${ job.flow_name }` - : __( 'Unknown', 'data-machine' ) ) } + { formatPipeline( job ) } + + + { formatFlow( job ) } + + + { job.label || '\u2014' } { isBatch && } - - { formatStatus( job.status ) } + + { formatStatus( getBaseStatus( job.status ) || job.status ) } + { getStatusDetail( job.status ) && ( + + { getStatusDetail( job.status ) } + + ) } { job.created_at_display || '' } { job.completed_at_display || '' } @@ -307,15 +391,23 @@ const JobsTable = ( { jobs, isLoading, isError, error } ) => { { __( 'Job ID', 'data-machine' ) } - { __( 'Source', 'data-machine' ) } + + { __( 'Pipeline', 'data-machine' ) } + + + { __( 'Flow', 'data-machine' ) } + + + { __( 'Label', 'data-machine' ) } + { __( 'Status', 'data-machine' ) } - { __( 'Created At', 'data-machine' ) } + { __( 'Created', 'data-machine' ) } - { __( 'Completed At', 'data-machine' ) } + { __( 'Completed', 'data-machine' ) }