Files
QuantEngineByItz/src/dotnet/QuantEngine.Web/Pages/Admin/Operations/Index.cshtml
T
kjh2064 475950be36
Validators (Pushes and Pull Requests) / validate-ui-and-storage (push) Successful in 22s
Validators (Pushes and Pull Requests) / validate-core (push) Has been cancelled
Prepare Release / Build & Create Release (push) Successful in 1m9s
Prepare Release / Release Notification (push) Successful in 2s
feat(web): add manual trigger action support for Hangfire recurring jobs
2026-07-12 13:36:52 +09:00

219 lines
9.9 KiB
Plaintext

@page
@model QuantEngine.Web.Pages.Admin.Operations.IndexModel
@{
ViewData["Title"] = "작업 관리";
}
<div class="page-header d-print-none">
<div class="row align-items-center">
<div class="col">
<h2 class="page-title">작업 관리</h2>
</div>
<div class="col-auto">
<button class="btn btn-secondary" onclick="location.reload()">새로고침</button>
</div>
</div>
</div>
<div class="page-body">
@if (TempData["SuccessMessage"] != null)
{
<div class="alert alert-success" role="alert" style="background-color: rgba(76, 175, 80, 0.15); border: 1px solid rgba(76, 175, 80, 0.3); color: #81c784; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
@TempData["SuccessMessage"]
</div>
}
@if (TempData["ErrorMessage"] != null)
{
<div class="alert alert-danger" role="alert" style="background-color: rgba(244, 67, 54, 0.15); border: 1px solid rgba(244, 67, 54, 0.3); color: #ff7675; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
@TempData["ErrorMessage"]
</div>
}
<div class="row row-deck row-cards">
<!-- 예약된 작업 -->
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">예약된 작업</h3>
</div>
<div class="table-responsive">
<table class="table table-vcenter card-table">
<thead>
<tr>
<th>작업명</th>
<th>스케줄</th>
<th>다음 실행</th>
<th>상태</th>
<th>작업</th>
</tr>
</thead>
<tbody>
@if (Model.ScheduledJobs?.Any() == true)
{
@foreach (var job in Model.ScheduledJobs)
{
<tr>
<td>
<strong>@job.JobName</strong>
</td>
<td><small>@job.Schedule</small></td>
<td>@(job.NextRun?.ToString("yyyy-MM-dd HH:mm:ss") ?? "-")</td>
<td>
@if (job.IsEnabled)
{
<span class="badge bg-success">활성</span>
}
else
{
<span class="badge bg-secondary">비활성</span>
}
</td>
<td>
<form method="post" asp-page-handler="TriggerJob" class="d-inline">
@Html.AntiForgeryToken()
<input type="hidden" name="jobId" value="@job.JobId" />
<button type="submit" class="btn btn-sm btn-success text-white">즉시 실행</button>
</form>
<a href="javascript:void(0)" class="btn btn-sm btn-link ms-2">수정</a>
</td>
</tr>
}
}
else
{
<tr>
<td colspan="5" class="text-center text-muted">예약된 작업이 없습니다</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<!-- 작업 실행 통계 -->
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h3 class="card-title">작업 통계</h3>
</div>
<div class="card-body">
<div class="space-y-2">
<div class="d-flex">
<div>
<div class="text-muted">전체 작업</div>
<div class="h2">@Model.TotalJobsCount</div>
</div>
</div>
<div class="d-flex">
<div>
<div class="text-muted">활성 작업</div>
<div class="h2 text-success">@Model.ActiveJobsCount</div>
</div>
</div>
<div class="d-flex">
<div>
<div class="text-muted">비활성 작업</div>
<div class="h2 text-warning">@Model.InactiveJobsCount</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 최근 작업 실행 -->
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h3 class="card-title">최근 작업 실행 (상위 10개)</h3>
</div>
<div class="table-responsive">
<table class="table table-sm table-vcenter card-table">
<thead>
<tr>
<th>작업명</th>
<th>시작 시간</th>
<th>완료 시간</th>
<th>소요 시간</th>
<th>결과</th>
</tr>
</thead>
<tbody>
@if (Model.RecentExecutions?.Any() == true)
{
@foreach (var exec in Model.RecentExecutions)
{
var duration = exec.CompletedAt.HasValue
? (exec.CompletedAt.Value - exec.StartedAt).TotalSeconds
: 0;
<tr>
<td><small>@exec.JobName</small></td>
<td><small>@exec.StartedAt.ToString("HH:mm:ss")</small></td>
<td><small>@(exec.CompletedAt?.ToString("HH:mm:ss") ?? "-")</small></td>
<td><small>@duration.ToString("F1")s</small></td>
<td>
@if (exec.IsSuccess)
{
<span class="badge bg-success">성공</span>
}
else
{
<span class="badge bg-danger">실패</span>
}
</td>
</tr>
}
}
else
{
<tr>
<td colspan="5" class="text-center text-muted">최근 실행 기록이 없습니다</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
<!-- 시스템 상태 -->
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">시스템 상태</h3>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<div class="text-muted">작업 큐 상태</div>
<div class="h4">
@if (Model.IsJobProcessorRunning)
{
<span class="badge bg-success">처리 중</span>
}
else
{
<span class="badge bg-warning">대기 중</span>
}
</div>
</div>
<div class="col-md-3">
<div class="text-muted">대기 중인 작업</div>
<div class="h4">@Model.PendingJobsCount</div>
</div>
<div class="col-md-3">
<div class="text-muted">마지막 갱신</div>
<div class="h6">@(Model.LastRefreshTime?.ToString("HH:mm:ss") ?? "-")</div>
</div>
<div class="col-md-3">
<div class="text-muted">상태 메시지</div>
<div class="h6 text-muted">@(Model.StatusMessage ?? "정상")</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>