2025-05-06 02:47:26 +00:00
|
|
|
<x-app-layout>
|
2025-05-06 03:25:09 +00:00
|
|
|
@if (request()->routeIs('submissions.showAll'))
|
|
|
|
|
<x-slot name="header">
|
|
|
|
|
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
|
|
|
|
{{ __('All submissions for project: ') . $project->title }}
|
|
|
|
|
</h2>
|
|
|
|
|
</x-slot>
|
2025-05-09 14:57:14 +00:00
|
|
|
<div class="py-4">
|
|
|
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
|
|
|
<!-- Tips Alert for Dashboard -->
|
|
|
|
|
<x-alert.tips-alert pageId="submission-show" title="Tips untuk Anda">
|
|
|
|
|
<p>Selamat datang di Submission! Berikut adalah beberapa tips untuk membantu Anda:</p>
|
|
|
|
|
<ul class="list-disc pl-5 mt-2">
|
|
|
|
|
<li>Klik "View" untuk melihat feedback atau error log dari sistem</li>
|
|
|
|
|
<li>Klik "Download Results" untuk mengunduh laporan berupa json</li>
|
|
|
|
|
</ul>
|
|
|
|
|
</x-alert.tips-alert>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-05-06 03:25:09 +00:00
|
|
|
<div class="py-5">
|
|
|
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
|
|
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
|
|
|
|
@php
|
|
|
|
|
$no = 0;
|
|
|
|
|
@endphp
|
|
|
|
|
@forelse ($submissions as $submission)
|
|
|
|
|
@php
|
|
|
|
|
$no += 1;
|
|
|
|
|
@endphp
|
|
|
|
|
<div class="relative overflow-x-auto">
|
|
|
|
|
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
|
|
|
|
|
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
|
|
|
|
<caption
|
|
|
|
|
class="p-5 text-lg font-semibold text-left text-gray-900 bg-white dark:text-white dark:bg-gray-800">
|
|
|
|
|
{{ $submission->project->title }} - submission number #{{ $no }}
|
|
|
|
|
<p class="mt-1 text-sm font-normal text-gray-500 dark:text-gray-400">Here is the
|
|
|
|
|
the
|
|
|
|
|
list of attempts for this submission</p>
|
|
|
|
|
<p
|
|
|
|
|
class="mt-1 text-sm font-normal text-gray-500 dark:text-gray-400 float-right">
|
|
|
|
|
Total
|
|
|
|
|
Attempts: {{ $submission->getTotalAttemptsCount() }}</p>
|
|
|
|
|
</caption>
|
|
|
|
|
<thead
|
|
|
|
|
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
|
|
|
<tr>
|
|
|
|
|
<th scope="col" class="px-6 py-3">Attempt NO#</th>
|
|
|
|
|
<th scope="col" class="px-6 py-3">Status</th>
|
|
|
|
|
<th scope="col" class="px-6 py-3">Time Spent</th>
|
|
|
|
|
<th scope="col" class="px-6 py-3">Description</th>
|
|
|
|
|
<th scope="col" class="px-6 py-3"><span class="sr-only">Edit</span></th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white text-center">
|
|
|
|
|
<span
|
|
|
|
|
class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-secondary-100 text-secondary-800">
|
|
|
|
|
{{ $submission->attempts }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
@php
|
|
|
|
|
$statusClass = '';
|
|
|
|
|
if ($submission->status === 'completed') {
|
|
|
|
|
$statusClass = 'bg-green-100 text-green-800';
|
|
|
|
|
} elseif ($submission->status === 'failed') {
|
|
|
|
|
$statusClass = 'bg-red-100 text-red-800';
|
|
|
|
|
} elseif ($submission->status === 'processing') {
|
|
|
|
|
$statusClass = 'bg-secondary-100 text-secondary-800';
|
|
|
|
|
} elseif ($submission->status === 'pending') {
|
|
|
|
|
$statusClass = 'bg-blue-100 text-blue-800';
|
|
|
|
|
}
|
|
|
|
|
@endphp
|
|
|
|
|
<span
|
|
|
|
|
class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none {{ $statusClass }}">
|
|
|
|
|
{{ ucfirst($submission->status) }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
{{-- get the difference between start and end time from submission using carbon and if end is null then use current time --}}
|
|
|
|
|
@php
|
|
|
|
|
$start = Carbon\Carbon::parse($submission->start);
|
|
|
|
|
$end = Carbon\Carbon::parse($submission->end ?? now());
|
|
|
|
|
$time = $end->diff($start)->format('%H:%I:%S');
|
|
|
|
|
@endphp
|
|
|
|
|
{{ $time }}
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
<span
|
|
|
|
|
class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none bg-secondary-100 text-secondary-800">
|
|
|
|
|
Current Attempt
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-6 py-4 text-right">
|
|
|
|
|
<a href="/nodejs/submissions/submission/{{ $submission->id }}"
|
|
|
|
|
class="font-medium text-blue-600 dark:text-blue-500 hover:underline">View</a>
|
|
|
|
|
@if ($submission->status === 'completed' || $submission->status === 'failed')
|
|
|
|
|
|
|
|
|
|
|
<a href="/nodejs/submissions/submission/{{ $submission->id }}/download?type=current"
|
|
|
|
|
class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Download
|
|
|
|
|
Results</a>
|
|
|
|
|
@endif
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
Past Attempts
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
@forelse ($submission->history->sortByDesc('attempts') as $history)
|
|
|
|
|
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white text-center">
|
|
|
|
|
{{ $history->attempts }}
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
@php
|
|
|
|
|
$statusClass = '';
|
|
|
|
|
if ($history->status === 'completed') {
|
|
|
|
|
$statusClass = 'bg-green-100 text-green-800';
|
|
|
|
|
} elseif ($history->status === 'failed') {
|
|
|
|
|
$statusClass = 'bg-red-100 text-red-800';
|
|
|
|
|
} elseif ($history->status === 'processing') {
|
|
|
|
|
$statusClass = 'bg-secondary-100 text-secondary-800';
|
|
|
|
|
} elseif ($history->status === 'pending') {
|
|
|
|
|
$statusClass = 'bg-blue-100 text-blue-800';
|
|
|
|
|
}
|
|
|
|
|
@endphp
|
|
|
|
|
<span
|
|
|
|
|
class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-xs font-bold leading-none {{ $statusClass }}">
|
|
|
|
|
{{ ucfirst($history->status) }}
|
|
|
|
|
</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
@php
|
|
|
|
|
$history_start = Carbon\Carbon::parse($history->start);
|
|
|
|
|
$history_end = Carbon\Carbon::parse($history->end ?? now());
|
|
|
|
|
$history_time = $history_end
|
|
|
|
|
->diff($history_start)
|
|
|
|
|
->format('%H:%I:%S');
|
|
|
|
|
@endphp
|
|
|
|
|
{{ $history_time }}
|
|
|
|
|
</td>
|
|
|
|
|
<td scope="row"
|
|
|
|
|
class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
|
|
|
|
{{ $history->description }}
|
|
|
|
|
</td>
|
|
|
|
|
<td class="px-6 py-4 text-right">
|
|
|
|
|
<a href="/submissions/submission/history/{{ $history->id }}"
|
|
|
|
|
class="font-medium text-blue-600 dark:text-blue-500 hover:underline">View</a>
|
|
|
|
|
|
|
|
|
|
|
<a href="/submissions/submission/{{ $history->id }}/download?type=history"
|
|
|
|
|
class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Download
|
|
|
|
|
Results</a>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
@empty
|
|
|
|
|
<div class="p-5">
|
|
|
|
|
<x-not-found message="No Past Attempts Found" />
|
|
|
|
|
</div>
|
|
|
|
|
@endforelse
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
@empty
|
|
|
|
|
<div class="p-5">
|
|
|
|
|
<x-not-found message="No Submissions Found" />
|
|
|
|
|
</div>
|
2025-05-06 02:47:26 +00:00
|
|
|
@endforelse
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-05-06 03:25:09 +00:00
|
|
|
</div>
|
2025-05-06 02:47:26 +00:00
|
|
|
@elseif(request()->routeIs('submissions.show') || request()->routeIs('submissions.history'))
|
2025-05-06 03:25:09 +00:00
|
|
|
<x-slot name="header">
|
|
|
|
|
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
|
|
|
|
{{ __('Submission NO#') . $submission->id . __(' of Project: ') . $submission->project->title . __(' attempt NO#: ') . $submission->attempts }}
|
|
|
|
|
</h2>
|
|
|
|
|
</x-slot>
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
<div class="py-5">
|
|
|
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-3 md:gap-8">
|
|
|
|
|
<div
|
|
|
|
|
class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg md:col-span-1 md:row-span-1 md:rounded-md md:shadow-md md:py-1 md:px-3 lg:px-5 xl:px-7">
|
|
|
|
|
<!-- content for the smaller column -->
|
|
|
|
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
|
|
|
|
<ol class="list-disc list-inside">
|
|
|
|
|
<li class="list-none">
|
|
|
|
|
<div class="flex justify-start gap-2">
|
|
|
|
|
<x-pending_icon class="w-[20px] h-[20px] pt-[0.10rem]" id="start_pending_icon"
|
|
|
|
|
svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-success_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="start_success_icon" svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-failed_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="start_failed_icon" svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<span class="text-gray-400 font-semiblid stepNames">Start</span>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
@forelse ($steps as $step)
|
|
|
|
|
<li class="list-none">
|
|
|
|
|
<div class="flex justify-start gap-2">
|
|
|
|
|
<x-pending_icon class="w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_pending_icon" svgWidth='20px'
|
|
|
|
|
svgHeight='20px' />
|
|
|
|
|
<x-success_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_success_icon" svgWidth='20px'
|
|
|
|
|
svgHeight='20px' />
|
|
|
|
|
<x-failed_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_failed_icon" svgWidth='20px'
|
|
|
|
|
svgHeight='20px' />
|
|
|
|
|
<span
|
|
|
|
|
class="text-gray-400 font-semiblid stepNames">{{ $step->executionStep->name }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
@if ($step->executionStep->name == 'NPM Run Tests')
|
|
|
|
|
@forelse ($step->variables as $testCommandValue)
|
|
|
|
|
@php
|
|
|
|
|
$command = implode(' ', $step->executionStep->commands);
|
|
|
|
|
$key = explode('=', $testCommandValue)[0];
|
|
|
|
|
$value = explode('=', $testCommandValue)[1];
|
|
|
|
|
$testStep = str_replace($key, $value, $command);
|
|
|
|
|
$iconID = str_replace(' ', '_', $testStep);
|
|
|
|
|
$iconID = str_replace('.', '_', $iconID);
|
|
|
|
|
@endphp
|
|
|
|
|
<li class="list-none pl-5">
|
|
|
|
|
<div class="flex justify-start gap-2">
|
|
|
|
|
<x-pending_icon class="w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_pending_icon_{{ $iconID }}"
|
|
|
|
|
svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-success_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_success_icon_{{ $iconID }}"
|
|
|
|
|
svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-failed_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="{{ $step->id }}_failed_icon_{{ $iconID }}"
|
|
|
|
|
svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<span
|
|
|
|
|
class="text-gray-400 font-semiblid stepTestNames">{{ $testStep }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
@empty
|
|
|
|
|
<x-not-found message="No Tests Found" />
|
|
|
|
|
@endforelse
|
|
|
|
|
@endif
|
|
|
|
|
@empty
|
|
|
|
|
<x-not-found message="No Steps Found" />
|
|
|
|
|
@endforelse
|
|
|
|
|
<li class="list-none">
|
|
|
|
|
<div class="flex justify-start gap-2">
|
|
|
|
|
<x-pending_icon class="w-[20px] h-[20px] pt-[0.10rem]" id="done_pending_icon"
|
|
|
|
|
svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-success_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="done_success_icon" svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<x-failed_icon class="hidden w-[20px] h-[20px] pt-[0.10rem]"
|
|
|
|
|
id="done_failed_icon" svgWidth='20px' svgHeight='20px' />
|
|
|
|
|
<span class="text-gray-400 font-semiblid stepNames">Done</span>
|
|
|
|
|
</div>
|
|
|
|
|
</li>
|
|
|
|
|
</ol>
|
|
|
|
|
</div>
|
2025-05-06 02:47:26 +00:00
|
|
|
</div>
|
2025-05-06 03:25:09 +00:00
|
|
|
<div
|
|
|
|
|
class="bg-gray-200 dark:bg-gray-900 border-secondary border-2 overflow-hidden shadow-sm sm:rounded-lg md:col-span-2 md:row-span-1 md:rounded-md md:shadow-md md:py-6 md:px-8 lg:px-12 xl:px-16">
|
|
|
|
|
<!-- content for the bigger column -->
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
<div class="m-5 w-100 bg-gray-600 sm:rounded-lg md:rounded-md" id="progress">
|
|
|
|
|
<div class="text-center w-[1%] h-5 bg-secondary text-white sm:rounded-lg md:rounded-md text-sm"
|
|
|
|
|
id="bar">0%</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div id="loader"
|
|
|
|
|
class="mx-5 mt-1 float-right hidden h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] text-secondary motion-reduce:animate-[spin_1.5s_linear_infinite]"
|
|
|
|
|
role="status">
|
|
|
|
|
<span
|
|
|
|
|
class="!absolute !-m-px !h-px !w-px !overflow-hidden !whitespace-nowrap !border-0 !p-0 ![clip:rect(0,0,0,0)]">Loading...</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div id="refresh"
|
|
|
|
|
class="hidden float-right cursor-pointer mx-5 mt-1 motion-reduce:animate-[spin_1.5s_linear_infinite]">
|
|
|
|
|
<x-refresh_icon class="h-8 w-8" svgWidth='2rem' svgHeight='2rem' />
|
|
|
|
|
</div>
|
|
|
|
|
<div class="mx-5">
|
|
|
|
|
<p class="text-white text-xl" id="submission_message"></p>
|
|
|
|
|
<p class="text-white text-xl mt-5" id="submission_status"></p>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
|
|
|
|
<div class="text-white mt-5" id="submission_results">
|
|
|
|
|
Waitting for the submission to progress...
|
|
|
|
|
</div>
|
2025-05-06 02:47:26 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
@section('scripts')
|
|
|
|
|
<script>
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
|
// Left side steps
|
|
|
|
|
const startElement = $(`.stepNames:contains(Start)`).closest('li');
|
|
|
|
|
const stepNames = $('.stepNames');
|
|
|
|
|
const stepTestNames = $('.stepTestNames');
|
|
|
|
|
const doneElement = $(`.stepNames:contains(Done)`).closest('li');
|
|
|
|
|
// Right side submission results
|
|
|
|
|
const submission_message = $('#submission_message');
|
|
|
|
|
const submission_status = $('#submission_status');
|
|
|
|
|
const submission_results = $('#submission_results');
|
|
|
|
|
// Right side progress indicators
|
|
|
|
|
const barElement = $('#bar');
|
|
|
|
|
const loaderElement = $('#loader');
|
|
|
|
|
const refreshElement = $('#refresh');
|
|
|
|
|
// varibale for faild submission, to be used in the refresh button
|
|
|
|
|
let allowedToRefresh = true;
|
|
|
|
|
// variable for the progress bar
|
|
|
|
|
let completion_percentage = 0;
|
|
|
|
|
// variable for the response
|
|
|
|
|
let old_response = {
|
|
|
|
|
status: 'pending',
|
|
|
|
|
message: 'Submission is pending',
|
|
|
|
|
results: {}
|
2025-05-06 02:47:26 +00:00
|
|
|
};
|
2025-05-06 03:25:09 +00:00
|
|
|
let error_server = false;
|
|
|
|
|
// the history page
|
|
|
|
|
let isNotHistory = `{{ !request()->routeIs('submissions.history') }}`;
|
|
|
|
|
isNotHistory = isNotHistory == 1 ? true : false;
|
|
|
|
|
// variable for checking if all the steps are completed
|
|
|
|
|
let checkResponse = false;
|
|
|
|
|
// retry mechanism variables
|
|
|
|
|
let maxRetries = 5;
|
|
|
|
|
let currentRetryCount = 0;
|
|
|
|
|
let retryDelay = 500;
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
// animate the progess bar
|
|
|
|
|
function move(start = 0, end = 100) {
|
|
|
|
|
var width = start;
|
|
|
|
|
barElement.width(width + '%').html(width + '%');
|
|
|
|
|
var duration = 500; // duration of the animation in ms
|
|
|
|
|
barElement.stop().animate({
|
|
|
|
|
width: end + '%'
|
|
|
|
|
}, {
|
|
|
|
|
duration: duration * (end - start) / 100,
|
|
|
|
|
step: function(now, fx) {
|
|
|
|
|
var val = Math.round(now);
|
|
|
|
|
barElement.html(val + '%');
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
2025-05-06 03:25:09 +00:00
|
|
|
});
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
2025-05-06 03:25:09 +00:00
|
|
|
|
|
|
|
|
async function getSubmissionStatus() {
|
|
|
|
|
try {
|
|
|
|
|
await $.ajax({
|
|
|
|
|
url: `/nodejs/submissions/status/submission/{{ $submission->id }}`,
|
|
|
|
|
method: 'GET',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
|
|
|
},
|
|
|
|
|
data: {
|
|
|
|
|
isNotHistory: isNotHistory,
|
|
|
|
|
},
|
|
|
|
|
success: function(response) {
|
|
|
|
|
// Reset retry count on successful request
|
|
|
|
|
currentRetryCount = 0;
|
|
|
|
|
retryDelay = 2000;
|
|
|
|
|
|
|
|
|
|
if (response.status === 'processing' || response.status === 'pending') {
|
|
|
|
|
try {
|
|
|
|
|
checkSubmissionProgress();
|
|
|
|
|
if (response.step !== null && error_server == false) {
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
window.requestAnimationFrame(
|
|
|
|
|
getSubmissionStatus);
|
|
|
|
|
}, 2000);
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
retrySubmissionStatus();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (response.status === 'failed') {
|
|
|
|
|
// failed status
|
|
|
|
|
updateUIFailedStatus(response);
|
|
|
|
|
} else if (response.status === 'completed') {
|
|
|
|
|
// completed status
|
|
|
|
|
loaderElement.addClass('hidden');
|
|
|
|
|
updateUICompletedStatus(response);
|
|
|
|
|
}
|
|
|
|
|
updateUI(response);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
error: function(xhr, status, error) {
|
|
|
|
|
console.error('Status request failed:', error);
|
|
|
|
|
retrySubmissionStatus();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
retrySubmissionStatus();
|
|
|
|
|
}
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
2025-05-06 03:25:09 +00:00
|
|
|
|
|
|
|
|
function retrySubmissionStatus() {
|
|
|
|
|
if (currentRetryCount < maxRetries) {
|
|
|
|
|
currentRetryCount++;
|
|
|
|
|
console.log(
|
|
|
|
|
`Retrying status request (${currentRetryCount}/${maxRetries}) in ${retryDelay/1000}s...`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
submission_message.text("Submssion Message: Network error. Retrying connection...");
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
retryDelay = Math.min(retryDelay * 1.5, 10000);
|
|
|
|
|
window.requestAnimationFrame(getSubmissionStatus);
|
|
|
|
|
}, retryDelay);
|
2025-05-06 02:47:26 +00:00
|
|
|
} else {
|
2025-05-06 03:25:09 +00:00
|
|
|
const error_response = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: 'Network connection failed after multiple attempts. Please refresh the page and try again.'
|
|
|
|
|
};
|
|
|
|
|
error_server = true;
|
|
|
|
|
updateUIFailedStatus(error_response);
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
// send request to the server to check the submission progress
|
|
|
|
|
async function checkSubmissionProgress() {
|
|
|
|
|
try {
|
|
|
|
|
// get the response from the server
|
|
|
|
|
const response = await $.ajax({
|
|
|
|
|
url: `/nodejs/submissions/process/submission`,
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
|
|
|
},
|
|
|
|
|
data: {
|
|
|
|
|
isNotHistory: isNotHistory,
|
|
|
|
|
submission_id: '{{ $submission->id }}',
|
|
|
|
|
_token: '{{ csrf_token() }}',
|
|
|
|
|
},
|
|
|
|
|
error: function(xhr, status, error) {
|
|
|
|
|
if (xhr.status === 504) {
|
|
|
|
|
location.reload();
|
|
|
|
|
}
|
|
|
|
|
console.error('Process request failed:', error);
|
|
|
|
|
throw new Error('Process request failed');
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
// Reset retry count on successful request
|
|
|
|
|
currentRetryCount = 0;
|
|
|
|
|
retryDelay = 2000;
|
|
|
|
|
|
|
|
|
|
checkResponse = JSON.stringify(old_response) === JSON.stringify(response);
|
|
|
|
|
if (!checkResponse) {
|
|
|
|
|
updateUI(response);
|
|
|
|
|
old_response = response;
|
|
|
|
|
}
|
|
|
|
|
if (response.message === 'Submission is processing meanwhile there is no step to execute') {
|
|
|
|
|
// run through all the results if there is on of them failed the checkResponse will be false
|
|
|
|
|
for (const [stepName, stepData] of Object.entries(response.results)) {
|
|
|
|
|
if (stepData.status === 'failed') {
|
|
|
|
|
checkResponse = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (checkResponse) {
|
|
|
|
|
// if all the steps are completed
|
|
|
|
|
updateUICompletedStatus(response);
|
|
|
|
|
} else {
|
|
|
|
|
// if one of the steps failed
|
|
|
|
|
updateUIFailedStatus(response);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// update the UI with the response
|
|
|
|
|
// move the progress bar if the completion percentage has changed
|
|
|
|
|
if (completion_percentage !== response.completion_percentage) {
|
|
|
|
|
completion_percentage = response.completion_percentage;
|
|
|
|
|
move(0, response.completion_percentage);
|
|
|
|
|
}
|
|
|
|
|
// check the status of the submission
|
|
|
|
|
// processing status
|
|
|
|
|
if (response.status === 'processing') {
|
|
|
|
|
if (response.next_step?.id !== undefined && isNotHistory) {
|
|
|
|
|
// the submission is still processing
|
|
|
|
|
loaderElement.removeClass('hidden');
|
|
|
|
|
} else {
|
|
|
|
|
// the end of the submission steps
|
|
|
|
|
loaderElement.addClass('hidden');
|
|
|
|
|
}
|
|
|
|
|
} else if (response.status === 'failed') {
|
|
|
|
|
// failed status
|
|
|
|
|
updateUIFailedStatus(response);
|
|
|
|
|
} else if (response.status === 'completed') {
|
|
|
|
|
// completed status
|
|
|
|
|
loaderElement.addClass('hidden');
|
|
|
|
|
updateUICompletedStatus(response);
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// if the request failed
|
|
|
|
|
const error_response = {
|
|
|
|
|
status: 'failed',
|
|
|
|
|
message: 'Something went wrong'
|
|
|
|
|
};
|
|
|
|
|
updateUIFailedStatus(error_response);
|
|
|
|
|
error_server = true;
|
|
|
|
|
throw error;
|
|
|
|
|
// console.log(error);
|
|
|
|
|
}
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
getSubmissionStatus();
|
|
|
|
|
|
|
|
|
|
function updateUIFailedStatus(response) {
|
|
|
|
|
// hide the loader
|
|
|
|
|
loaderElement.addClass('hidden');
|
|
|
|
|
// change the done icon to failed
|
|
|
|
|
doneElement.find('#done_pending_icon').addClass('hidden');
|
|
|
|
|
doneElement.find('#done_failed_icon').removeClass('hidden');
|
|
|
|
|
doneElement.find('span').addClass('text-red-400');
|
|
|
|
|
// show error message
|
|
|
|
|
updateSubmissionHeader(response.status, response.message);
|
|
|
|
|
// move the progress bar to 100% and red color
|
|
|
|
|
move(0, 100);
|
|
|
|
|
barElement.removeClass('bg-secondary');
|
|
|
|
|
barElement.addClass('bg-red-400');
|
|
|
|
|
// show the refresh button
|
|
|
|
|
if (isNotHistory) {
|
|
|
|
|
refreshElement.removeClass('hidden');
|
|
|
|
|
if (allowedToRefresh) {
|
|
|
|
|
refreshElement.click(function() {
|
|
|
|
|
// update the submission results and progress bar
|
|
|
|
|
move(0, 0);
|
|
|
|
|
updateSubmissionHeader('Wait', "Restarting...");
|
|
|
|
|
// request the server to refresh the submission
|
|
|
|
|
requestRefresh();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// update the submission results last element
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
async function requestRefresh() {
|
|
|
|
|
// start the refresh loader
|
|
|
|
|
refreshElement.addClass('animate-spin');
|
|
|
|
|
refreshElement.removeClass('cursor-pointer');
|
|
|
|
|
try {
|
|
|
|
|
if (allowedToRefresh) {
|
|
|
|
|
// prevent the user from clicking the button multiple times
|
|
|
|
|
allowedToRefresh = false;
|
|
|
|
|
const response = await $.ajax({
|
|
|
|
|
url: '/nodejs/submissions/refresh/submission',
|
|
|
|
|
method: 'POST',
|
|
|
|
|
headers: {
|
|
|
|
|
'X-CSRF-TOKEN': '{{ csrf_token() }}'
|
|
|
|
|
},
|
|
|
|
|
data: {
|
|
|
|
|
submission_id: '{{ $submission->id }}',
|
|
|
|
|
_token: '{{ csrf_token() }}',
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
// update the UI with the response
|
|
|
|
|
// refresh the page after 5 seconds
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
refreshElement.removeClass('animate-spin');
|
|
|
|
|
refreshElement.addClass('hidden');
|
|
|
|
|
barElement.removeClass('bg-red-400');
|
|
|
|
|
barElement.addClass('bg-secondary');
|
|
|
|
|
window.location.reload();
|
|
|
|
|
}, 5000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.log(error);
|
|
|
|
|
const error_response = {
|
|
|
|
|
status: "failed",
|
|
|
|
|
message: "Something went wrong",
|
|
|
|
|
};
|
|
|
|
|
updateUIFailedStatus(error_response);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
function updateUICompletedStatus(response) {
|
|
|
|
|
// hide the loader
|
|
|
|
|
loaderElement.addClass('hidden');
|
|
|
|
|
// change the done icon to success
|
|
|
|
|
doneElement.find('#done_pending_icon').addClass('hidden');
|
|
|
|
|
doneElement.find('#done_success_icon').removeClass('hidden');
|
|
|
|
|
doneElement.find('span').addClass('text-secondary');
|
|
|
|
|
// show the submission results
|
|
|
|
|
updateSubmissionHeader(response.status, response.message);
|
|
|
|
|
// move the progress bar to 100% and green color
|
|
|
|
|
move(0, 100);
|
|
|
|
|
barElement.removeClass('bg-secondary');
|
|
|
|
|
barElement.addClass('bg-green-400');
|
|
|
|
|
// update the submission results last element
|
|
|
|
|
const submission_results_done = $('#submission_results_done');
|
|
|
|
|
submission_results_done.children('h2').text("Status: " + response.status);
|
|
|
|
|
submission_results_done.children('p').removeClass("text-red-400");
|
|
|
|
|
submission_results_done.children('p').removeClass("text-gray-400");
|
|
|
|
|
submission_results_done.children('p').addClass("text-secondary");
|
|
|
|
|
submission_results_done.children('p').next().text("Message: " + response.message);
|
|
|
|
|
submission_results_done.children('p').next().removeClass("text-secondary");
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
console.log($('#submission_results_done').children());
|
|
|
|
|
}
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
function updateUI(response) {
|
|
|
|
|
if (response.status && response.message && response.results) {
|
|
|
|
|
// number of steps
|
|
|
|
|
number = 1;
|
|
|
|
|
// clean the submission results
|
|
|
|
|
submission_results.empty();
|
|
|
|
|
// update the submission results
|
|
|
|
|
updateSubmissionHeader(response.status, response.message);
|
|
|
|
|
// submission_status.text("Submssion Status: " + response.status);
|
|
|
|
|
// submission_message.text("Submssion Message: " + response.message);
|
|
|
|
|
// order the steps by order
|
|
|
|
|
let arr = Object.entries(response.results);
|
|
|
|
|
arr.sort((a, b) => parseInt(a[1].order) - parseInt(b[1].order));
|
|
|
|
|
results = Object.fromEntries(arr);
|
|
|
|
|
// update the submission results first step start
|
|
|
|
|
startElement.find('#start_pending_icon').addClass('hidden');
|
|
|
|
|
startElement.find('span').addClass('text-secondary');
|
|
|
|
|
startElement.find('#start_success_icon').removeClass('hidden');
|
|
|
|
|
submission_results.append('<p class="text-center m-0 p-0">Results Summary</p>');
|
|
|
|
|
submission_results.append(
|
|
|
|
|
`<div class="text-lg text-white mt-5 border p-5 rounded-md" id="submission_results_start">
|
2025-05-06 02:47:26 +00:00
|
|
|
|
|
|
|
|
<h1 class="text-md font-bold">${number}- Start</h1>
|
|
|
|
|
<h2 class="text-xs font-semibold text-secondary-400">Status: Done</h2>
|
2025-05-06 03:25:09 +00:00
|
|
|
<p class="text-xs font-semibold">Output: Submission has been initialized and ready to process</div>`
|
|
|
|
|
);
|
|
|
|
|
// check all the steps status and update the UI
|
|
|
|
|
for (const [stepName, stepData] of Object.entries(results)) {
|
|
|
|
|
const stepElement = $(`.stepNames:contains(${stepName})`).closest('li');
|
|
|
|
|
if (stepData.status === 'completed') {
|
|
|
|
|
stausClass = 'text-secondary';
|
|
|
|
|
outputClass = 'break-words';
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_pending_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_success_icon`).removeClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_failed_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find('span').removeClass('text-gray-400');
|
|
|
|
|
stepElement.find('span').removeClass('text-red-400');
|
|
|
|
|
stepElement.find('span').addClass('text-secondary');
|
|
|
|
|
checkResponse = true;
|
|
|
|
|
} else if (stepData.status === 'failed') {
|
|
|
|
|
stausClass = 'text-red-400';
|
|
|
|
|
outputClass = 'break-words';
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_pending_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_success_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_failed_icon`).removeClass('hidden');
|
|
|
|
|
stepElement.find('span').removeClass('text-secondary');
|
|
|
|
|
stepElement.find('span').removeClass('text-gray-400');
|
|
|
|
|
stepElement.find('span').addClass('text-red-400');
|
2025-05-06 02:47:26 +00:00
|
|
|
} else {
|
2025-05-06 03:25:09 +00:00
|
|
|
stausClass = 'text-gray-400';
|
|
|
|
|
outputClass = 'text-gray-400 break-words';
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_pending_icon`).removeClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_success_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find(`#${stepData.stepID}_failed_icon`).addClass('hidden');
|
|
|
|
|
stepElement.find('span').removeClass('text-red-400');
|
|
|
|
|
stepElement.find('span').removeClass('text-secondary');
|
|
|
|
|
stepElement.find('span').addClass('text-gray-400');
|
2025-05-06 02:47:26 +00:00
|
|
|
}
|
2025-05-06 03:25:09 +00:00
|
|
|
number += 1;
|
|
|
|
|
if (stepName == 'NPM Run Tests') {
|
|
|
|
|
testResultsDiv = ``;
|
|
|
|
|
let totalPassedTests = 0;
|
|
|
|
|
let totalTestsCount = 0;
|
|
|
|
|
let hasTestMetrics = false;
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
stepTestNames.each(function(index) {
|
|
|
|
|
const stepTestName = $(`.stepTestNames:contains(${$(this).text()})`)
|
|
|
|
|
.closest('li');
|
|
|
|
|
iconID = $(this).text().replaceAll(" ", "_").replaceAll(".", "_")
|
|
|
|
|
.replaceAll("/", "_");
|
|
|
|
|
|
|
|
|
|
if (stepData.testResults[$(this).text()].status === 'completed') {
|
|
|
|
|
teststausClass = 'text-secondary';
|
|
|
|
|
testOutputClass = 'break-words';
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_pending_icon_${iconID}`)
|
|
|
|
|
.addClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_success_icon_${iconID}`)
|
|
|
|
|
.removeClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_failed_icon_${iconID}`).addClass(
|
|
|
|
|
'hidden');
|
|
|
|
|
stepTestName.find('span').removeClass('text-gray-400');
|
|
|
|
|
stepTestName.find('span').removeClass('text-red-400');
|
|
|
|
|
stepTestName.find('span').addClass('text-secondary');
|
|
|
|
|
} else if (stepData.testResults[$(this).text()].status === 'failed') {
|
|
|
|
|
teststausClass = 'text-red-400';
|
|
|
|
|
testOutputClass = 'break-words';
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_pending_icon_${iconID}`)
|
|
|
|
|
.addClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_success_icon_${iconID}`)
|
|
|
|
|
.addClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_failed_icon_${iconID}`)
|
|
|
|
|
.removeClass('hidden');
|
|
|
|
|
stepTestName.find('span').removeClass('text-secondary');
|
|
|
|
|
stepTestName.find('span').removeClass('text-gray-400');
|
|
|
|
|
stepTestName.find('span').addClass('text-red-400');
|
|
|
|
|
} else {
|
|
|
|
|
teststausClass = 'text-gray-400';
|
|
|
|
|
testOutputClass = 'text-gray-400 break-words';
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_pending_icon_${iconID}`)
|
|
|
|
|
.removeClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_success_icon_${iconID}`)
|
|
|
|
|
.addClass('hidden');
|
|
|
|
|
stepTestName.find(`#${stepData.stepID}_failed_icon_${iconID}`).addClass(
|
|
|
|
|
'hidden');
|
|
|
|
|
stepTestName.find('span').removeClass('text-red-400');
|
|
|
|
|
stepTestName.find('span').removeClass('text-secondary');
|
|
|
|
|
stepTestName.find('span').addClass('text-gray-400');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (stepData.testResults[$(this).text()].passedTests !== undefined) {
|
|
|
|
|
totalPassedTests += stepData.testResults[$(this).text()].passedTests;
|
|
|
|
|
totalTestsCount += stepData.testResults[$(this).text()].totalTests;
|
|
|
|
|
hasTestMetrics = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
testResultsDiv += `
|
|
|
|
|
<div class="text-xs mt-5 border p-5 break-words rounded-md" id="submission_results_${stepData.stepID}_${$(this).text()}">
|
|
|
|
|
<h1 class="text-xs font-bold">${$(this).text()}</h1>
|
|
|
|
|
<h2 class="text-xs font-semibold ${teststausClass}">Status: ${stepData.testResults[$(this).text()].status}</h2>
|
|
|
|
|
<p class="text-xs font-semibold ${testOutputClass}">Output: ${stepData.testResults[$(this).text()].output.replaceAll("\n", "<br>").replaceAll(" ", " ")}</p>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let testMetricsSummary = '';
|
|
|
|
|
if (hasTestMetrics) {
|
|
|
|
|
const metricsColor = (totalPassedTests === totalTestsCount) ? 'text-green-500' :
|
|
|
|
|
'text-yellow-500';
|
|
|
|
|
const progressPercent = totalTestsCount > 0 ? Math.round((totalPassedTests /
|
|
|
|
|
totalTestsCount) * 100) : 0;
|
|
|
|
|
const progressBarColor = progressPercent === 100 ? 'bg-green-500' : (
|
|
|
|
|
progressPercent >= 80 ? 'bg-yellow-500' : 'bg-red-500');
|
|
|
|
|
|
|
|
|
|
testMetricsSummary = `
|
|
|
|
|
<div class="text-lg text-white mt-6 p-4 border border-gray-700 rounded-md shadow-lg">
|
|
|
|
|
<div class="flex items-center justify-between mb-2">
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
${progressPercent === 100 ?
|
|
|
|
|
'<svg class="w-5 h-5 mr-2 text-green-500" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>' :
|
|
|
|
|
'<svg class="w-5 h-5 mr-2 text-yellow-500" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>'}
|
|
|
|
|
<h3 class="text-md font-bold ${metricsColor}">Test Summary</h3>
|
|
|
|
|
</div>
|
|
|
|
|
<span class="text-sm font-bold ${metricsColor}">${progressPercent}%</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="w-full bg-gray-700 rounded-full h-2.5 mb-4">
|
|
|
|
|
<div class="h-2.5 rounded-full ${progressBarColor} transition-all duration-500" style="width: ${progressPercent}%"></div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="grid grid-cols-3 gap-2 text-sm font-medium">
|
|
|
|
|
<div class="flex flex-col items-center p-2 bg-gray-800 rounded-md">
|
|
|
|
|
<span class="text-green-500 font-bold">${totalPassedTests}</span>
|
|
|
|
|
<span class="text-xs text-gray-400">Passed</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col items-center p-2 bg-gray-800 rounded-md">
|
|
|
|
|
<span class="text-red-500 font-bold">${totalTestsCount - totalPassedTests}</span>
|
|
|
|
|
<span class="text-xs text-gray-400">Failed</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flex flex-col items-center p-2 bg-gray-800 rounded-md">
|
|
|
|
|
<span class="text-blue-500 font-bold">${totalTestsCount}</span>
|
|
|
|
|
<span class="text-xs text-gray-400">Total</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
submission_results.append(`<div class="text-lg text-white mt-5 border p-5 rounded-md" id="submission_results_${stepData.stepID}">
|
2025-05-06 02:47:26 +00:00
|
|
|
<h1 class="text-md font-bold">${number}- ${stepName}</h1>
|
|
|
|
|
<h2 class="text-xs font-semibold ${stausClass}">Status: ${stepData.status}</h2>
|
|
|
|
|
<p class="text-xs font-semibold ${outputClass}">Output: ${stepData.output}</p>
|
|
|
|
|
<p class="text-xs font-semibold">Test Results:</p>
|
|
|
|
|
${testResultsDiv}
|
2025-05-06 03:25:09 +00:00
|
|
|
${testMetricsSummary}
|
2025-05-06 02:47:26 +00:00
|
|
|
</div>`);
|
2025-05-06 03:25:09 +00:00
|
|
|
} else {
|
|
|
|
|
submission_results.append(`<div class="text-lg text-white mt-5 border p-5 rounded-md" id="submission_results_${stepData.stepID}">
|
|
|
|
|
<h1 class="text-md font-bold">${number}- ${stepName}</h2>
|
2025-05-06 02:47:26 +00:00
|
|
|
<h2 class="text-xs font-semibold ${stausClass}">Status: ${stepData.status}</h2>
|
|
|
|
|
<p class="text-xs font-semibold ${outputClass}">Output: ${stepData.output}</p>
|
|
|
|
|
</div>`);
|
2025-05-06 03:25:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// add the last step done
|
|
|
|
|
statusClasses = '';
|
|
|
|
|
if (response.status === 'completed') {
|
|
|
|
|
statusClasses = 'text-secondary';
|
|
|
|
|
} else if (response.status === 'failed') {
|
|
|
|
|
statusClasses = 'text-red-400';
|
|
|
|
|
} else if (response.status === 'processing') {
|
|
|
|
|
statusClasses = 'text-gray-400';
|
|
|
|
|
} else if (response.status === 'pending') {
|
|
|
|
|
statusClasses = 'text-gray-400';
|
|
|
|
|
}
|
|
|
|
|
doneStep = ` <div class="text-lg text-white mt-5 border p-5 rounded-md" id="submission_results_done">
|
2025-05-06 02:47:26 +00:00
|
|
|
<h1 class="text-md font-bold">${number}- Done</h1>
|
|
|
|
|
<h2 class="text-xs font-semibold ${statusClasses}">Status: ${response.status}</h2>
|
|
|
|
|
<p class="text-xs font-semibold ${statusClasses}">Message: ${response.message}</p>
|
|
|
|
|
</div>`;
|
2025-05-06 03:25:09 +00:00
|
|
|
submission_results.append(doneStep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-06 02:47:26 +00:00
|
|
|
|
2025-05-06 03:25:09 +00:00
|
|
|
function updateSubmissionHeader(status, message) {
|
|
|
|
|
submission_status.text("Submssion Status: ");
|
|
|
|
|
submission_message.text("Submssion Message: " + message);
|
|
|
|
|
let statusClass = '';
|
|
|
|
|
if (status === 'completed') {
|
|
|
|
|
statusClass = 'bg-green-100 text-green-800';
|
|
|
|
|
} else if (status === 'failed') {
|
|
|
|
|
statusClass = 'bg-red-100 text-red-800';
|
|
|
|
|
} else if (status === 'processing') {
|
|
|
|
|
statusClass = 'bg-secondary-100 text-secondary-800';
|
|
|
|
|
} else if (status === 'pending') {
|
|
|
|
|
statusClass = 'bg-blue-100 text-blue-800';
|
|
|
|
|
} else if (status === 'Wait') {
|
|
|
|
|
statusClass = 'bg-blue-100 text-blue-800';
|
|
|
|
|
}
|
|
|
|
|
status = status.charAt(0).toUpperCase() + status.slice(1);
|
|
|
|
|
submission_status.append(`<span class="inline-flex items-center justify-center px-2 py-1 rounded-lg text-md font-bold leading-none ${statusClass}">
|
2025-05-06 02:47:26 +00:00
|
|
|
${status}
|
|
|
|
|
</span>`);
|
2025-05-06 03:25:09 +00:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
@endsection
|
2025-05-06 02:47:26 +00:00
|
|
|
@endif
|
2025-05-06 03:25:09 +00:00
|
|
|
</x-app-layout>
|