No phase data available
'; + + const maxDuration = Math.max(...validSpans.map((s) => s.duration || 0)); + const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#06b6d4', '#84cc16']; + + const bars = validSpans + .map((span, index) => { + const width = ((span.duration || 0) / maxDuration) * 100; + const color = colors[index % colors.length]; + return ` + + `; + }) + .join(''); + + return `No timeline data available
'; + + const sortedSpans = [...spans].sort((a, b) => a.startTime - b.startTime); + const minTime = sortedSpans[0]?.startTime || 0; + const maxTime = Math.max(...sortedSpans.map((s) => s.endTime || s.startTime)); + const totalDuration = maxTime - minTime; + + const timelineItems = sortedSpans + .map((span) => { + const start = ((span.startTime - minTime) / totalDuration) * 100; + const width = ((span.duration || 0) / totalDuration) * 100; + const statusClass = span.status === 'completed' ? 'completed' : span.status === 'error' ? 'error' : 'running'; + + return ` +No span data available
'; + + const rows = spans + .map((span) => { + const memoryBefore = span.memoryBefore ? formatBytes(span.memoryBefore.heapUsed) : '-'; + const memoryAfter = span.memoryAfter ? formatBytes(span.memoryAfter.heapUsed) : '-'; + const statusBadge = + span.status === 'completed' + ? 'Completed' + : span.status === 'error' + ? 'Error' + : 'Running'; + + return ` +${span.name}| Phase | +Duration | +Memory Before | +Memory After | +Status | +
|---|
Generated on ${new Date(report.timestamp).toLocaleString()}
+