Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions client/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ export const routes: Routes = [
redirectTo: 'pr',
pathMatch: 'full',
},
{
path: 'runs/:runId',
loadComponent: () => import('@app/pages/workflow-run-details/workflow-run-details.component').then(m => m.WorkflowRunDetailsComponent),
},
{
path: 'runs',
loadComponent: () => import('@app/pages/workflow-run-list/workflow-run-list.component').then(m => m.WorkflowRunListComponent),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,7 @@
<div class="flex-grow">
<app-deployment-stepper [deployment]="environment().latestDeployment" class="w-full" />

@if (workflowRunId()) {
<app-workflow-jobs-status [workflowRunId]="workflowRunId()!" [latestDeployment]="environment().latestDeployment" />
}
<app-workflow-jobs-status [latestDeployment]="environment().latestDeployment" />
</div>
} @else {
<app-environment-details [environment]="environment()" class="w-full" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, computed, inject, input, output } from '@angular/core';
import { Component, inject, input, output } from '@angular/core';
import { EnvironmentDeployment, EnvironmentDto } from '@app/core/modules/openapi';
import { DeploymentStepperComponent } from '../deployment-stepper/deployment-stepper.component';
import { EnvironmentActionsComponent } from '../environment-actions/environment-actions.component';
Expand Down Expand Up @@ -130,15 +130,4 @@ export class EnvironmentAccordionComponent {
window.open(url, '_blank');
}
}

workflowRunId = computed(() => {
const deployment = this.environment()?.latestDeployment;
if (!deployment?.workflowRunHtmlUrl) return undefined;

const matches = deployment.workflowRunHtmlUrl.match(/\/runs\/(\d+)$/);
if (matches && matches[1]) {
return parseInt(matches[1], 10);
}
return undefined;
});
}
Original file line number Diff line number Diff line change
@@ -1,121 +1,10 @@
<div class="workflow-jobs-status w-full mb-4">
@if (jobs().length > 0 && !deploymentSuccessful()) {
@if (jobs().length > 0 && !deploymentSuccessful()) {
<div class="workflow-jobs-status w-full mb-4">
<div class="flex justify-between items-center mb-3">
<h3 class="text-lg font-medium">Job Status</h3>
</div>

<div class="grid">
@for (job of jobs(); track job.id) {
<div
class="workflow-job mb-4 border border-gray-200 dark:border-gray-700 rounded-lg p-3 bg-gray-50 dark:bg-gray-800"
[ngClass]="{
'border-green-300 bg-green-50 dark:border-green-800 dark:bg-green-900/30': job.conclusion === 'success',
'border-red-300 bg-red-50 dark:border-red-800 dark:bg-red-900/30': job.conclusion === 'failure',
}"
>
<div class="job-header flex justify-between items-center mb-2 cursor-pointer" (click)="toggleJobExpansion(job.id!)">
<div class="flex items-center gap-2">
<i-tabler [name]="isJobExpanded(job.id!) ? 'chevron-down' : 'chevron-right'" class="!size-5 text-gray-500"></i-tabler>
<i-tabler [name]="getStatusIcon(job.status, job.conclusion)" [ngClass]="getIconColorClass(job.status, job.conclusion)" class="!size-5"></i-tabler>
<span class="font-semibold">{{ job.name }}</span>
<span class="status-badge px-2 py-1 rounded-full text-xs font-medium" [ngClass]="getStatusClass(job.status, job.conclusion)">
{{ getStatusText(job.status, job.conclusion) }}
</span>
</div>
<div class="text-sm text-gray-500 flex items-center gap-2">
@if (job.startedAt) {
<span>Started: {{ formatTime(job.startedAt) }}</span>

@if (job.completedAt) {
<span>• Completed: {{ formatTime(job.completedAt) }}</span>
<span class="px-2 py-0.5 bg-gray-100 rounded-full">
{{ getDuration(job.startedAt, job.completedAt) }}
</span>
} @else {
<span class="px-2 py-0.5 bg-blue-50 text-blue-600 rounded-full"> Running </span>
}
}
@if (!!job.htmlUrl) {
<p-button [link]="true" label="View Logs on Github" size="small" (click)="$event.stopPropagation(); openLink(job.htmlUrl)">
<i-tabler name="external-link" class="!size-4"></i-tabler>
</p-button>
}
</div>
</div>

@if (isJobExpanded(job.id!)) {
<div class="job-details mt-3">
<!-- Runner Information -->
@if (job.runnerId) {
<div class="runner-info text-sm mb-2 flex flex-wrap items-center gap-2">
<span class="text-gray-600">Runner:</span>
<span class="font-medium">{{ job.runnerName || 'Unknown' }}</span>

@if (job.labels && job.labels.length > 0) {
<div class="runner-labels flex flex-wrap gap-1 ml-2">
@for (label of job.labels; track label) {
<span class="px-2 py-0.5 rounded-full text-xs bg-gray-100 text-gray-700">
{{ label }}
</span>
}
</div>
}
</div>
}

<!-- Waiting for Runner Message -->
@if (job.status === 'queued' && !job.runnerId) {
<div class="waiting-for-runner text-sm mb-2">
<span class="text-yellow-600 flex items-center">
<i-tabler name="clock" class="mr-1"></i-tabler>
Waiting for available runner...
</span>
</div>
}

<!-- Waiting for Review Message -->
@if (job.status === 'waiting') {
<div class="waiting-for-runner text-sm mb-2">
<span class="text-yellow-600 flex items-center">
<i-tabler name="clock" class="mr-1"></i-tabler>
Waiting for approval...
</span>
</div>
}

<!-- Skipped Message -->
@if (job.conclusion === 'skipped') {
<div class="waiting-for-runner text-sm mb-2 ml-2">
<span class="text-gray-600 dark:text-gray-400 flex items-center"> This job was skipped. </span>
</div>
}

@if (job.steps?.length) {
<div class="job-steps pl-4 border-l-2 border-gray-200 mt-3">
@for (step of job.steps; track step.number) {
<div class="step py-1 flex items-center gap-2">
<span class="status-indicator size-2 rounded-full" [ngClass]="getStatusIndicatorClass(step.status, step.conclusion)"></span>
<span class="step-name text-sm flex-grow">{{ step.name }}</span>

<div class="flex items-center gap-2">
@if (step.startedAt && step.completedAt) {
<span class="text-xs text-gray-500">
{{ getDuration(step.startedAt, step.completedAt) }}
</span>
}
<span class="status-badge px-2 py-0.5 rounded-full text-xs" [ngClass]="getStatusClass(step.status, step.conclusion)">
{{ getStatusText(step.status, step.conclusion) }}
</span>
</div>
</div>
}
</div>
}
</div>
}
</div>
}
</div>
<app-workflow-job-list [jobs]="jobs()" />

@if (deploymentUnsuccessful() && !!latestDeployment()?.workflowRunHtmlUrl) {
<div class="flex-col w-full justify-items-center">
Expand All @@ -125,5 +14,5 @@ <h3 class="text-lg font-medium">Job Status</h3>
</p-button>
</div>
}
}
</div>
</div>
}
Loading
Loading