mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Upgraded `golang.org/x/crypto` to v0.36.0 and other indirect dependencies to their latest versions. - Added functionality to assign admin roles to users upon creation in the main application logic. - Introduced new templates for admin role management, including forms for creating and editing roles. - Removed deprecated admin tools template to streamline the admin interface. - Added audit logging for role changes to improve tracking and accountability.
202 lines
9.0 KiB
Templ
202 lines
9.0 KiB
Templ
package components
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"github.com/starfleetcptn/gomft/internal/db"
|
|
"time"
|
|
)
|
|
|
|
type JobRunDetailsData struct {
|
|
JobHistory db.JobHistory
|
|
Job db.Job
|
|
Config db.TransferConfig
|
|
}
|
|
|
|
templ JobRunDetails(ctx context.Context, data JobRunDetailsData) {
|
|
@LayoutWithContext("Job Run Details", ctx) {
|
|
@JobRunDetailsContent(ctx, data)
|
|
}
|
|
}
|
|
|
|
// JobRunDetailsContent is the same as JobRunDetails but without the layout wrapper
|
|
// This is used for testing
|
|
templ JobRunDetailsContent(ctx context.Context, data JobRunDetailsData) {
|
|
<div class="py-6 px-4 mx-auto max-w-7xl lg:px-8">
|
|
<div class="mb-6">
|
|
<a href="/dashboard" class="text-blue-600 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300 inline-flex items-center">
|
|
<i class="fas fa-arrow-left mr-2"></i> Back to Dashboard
|
|
</a>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between mb-8">
|
|
<h1 class="text-3xl font-bold text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-file-alt mr-3 text-blue-600 dark:text-blue-400"></i>
|
|
Job Run Details
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Job Run Information Card -->
|
|
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm mb-8">
|
|
<div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
|
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white">{ data.Job.Name }</h3>
|
|
if data.JobHistory.Status == "completed" {
|
|
<span class="px-3 py-1 text-sm font-medium rounded-full bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-300 inline-flex items-center">
|
|
<i class="fas fa-check mr-2"></i> Completed
|
|
</span>
|
|
} else if data.JobHistory.Status == "failed" {
|
|
<span class="px-3 py-1 text-sm font-medium rounded-full bg-red-100 dark:bg-red-900 text-red-800 dark:text-red-300 inline-flex items-center">
|
|
<i class="fas fa-times mr-2"></i> Failed
|
|
</span>
|
|
} else {
|
|
<span class="px-3 py-1 text-sm font-medium rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-300 inline-flex items-center">
|
|
<i class="fas fa-spinner fa-spin mr-2"></i> Running
|
|
</span>
|
|
}
|
|
</div>
|
|
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">Config: { data.Config.Name }</p>
|
|
</div>
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<dl class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-calendar-alt mr-2 text-gray-400 dark:text-gray-500"></i> Start Time
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
{ data.JobHistory.StartTime.Format("Jan 02, 2006 15:04:05") }
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-calendar-check mr-2 text-gray-400 dark:text-gray-500"></i> End Time
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
if data.JobHistory.EndTime != nil {
|
|
{ data.JobHistory.EndTime.Format("Jan 02, 2006 15:04:05") }
|
|
} else {
|
|
<span class="italic text-gray-500 dark:text-gray-400">In progress</span>
|
|
}
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-clock mr-2 text-gray-400 dark:text-gray-500"></i> Duration
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
if data.JobHistory.EndTime != nil {
|
|
{ data.JobHistory.EndTime.Sub(data.JobHistory.StartTime).String() }
|
|
} else {
|
|
<span class="italic text-gray-500 dark:text-gray-400">In progress</span>
|
|
}
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-upload mr-2 text-gray-400 dark:text-gray-500"></i> Data Transferred
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
{ formatBytes(data.JobHistory.BytesTransferred) }
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-file mr-2 text-gray-400 dark:text-gray-500"></i> Files Transferred
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
{ fmt.Sprintf("%d files", data.JobHistory.FilesTransferred) }
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 flex items-center mb-1">
|
|
<i class="fas fa-calendar-day mr-2 text-gray-400 dark:text-gray-500"></i> Job Schedule
|
|
</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
{ data.Job.Schedule }
|
|
</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Transfer Configuration Details -->
|
|
<div class="bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm mb-8">
|
|
<div class="px-4 py-5 sm:px-6 border-b border-gray-200 dark:border-gray-700">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-white flex items-center">
|
|
<i class="fas fa-cog mr-3 text-blue-600 dark:text-blue-400"></i>
|
|
Transfer Configuration
|
|
</h3>
|
|
</div>
|
|
<div class="px-4 py-5 sm:p-6">
|
|
<dl class="grid grid-cols-1 gap-x-6 gap-y-6 sm:grid-cols-2">
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">Source Type</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
<span class="px-2.5 py-0.5 text-xs font-medium rounded bg-gray-100 dark:bg-gray-700">{ data.Config.SourceType }</span>
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">Destination Type</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white">
|
|
<span class="px-2.5 py-0.5 text-xs font-medium rounded bg-gray-100 dark:bg-gray-700">{ data.Config.DestinationType }</span>
|
|
</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">Source Path</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white font-mono bg-gray-50 dark:bg-gray-900 p-2 rounded-lg border border-gray-200 dark:border-gray-700">{ data.Config.SourcePath }</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">Destination Path</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white font-mono bg-gray-50 dark:bg-gray-900 p-2 rounded-lg border border-gray-200 dark:border-gray-700">{ data.Config.DestinationPath }</dd>
|
|
</div>
|
|
<div class="sm:col-span-1">
|
|
<dt class="text-sm font-medium text-gray-500 dark:text-gray-400 mb-1">File Pattern</dt>
|
|
<dd class="text-sm text-gray-900 dark:text-white font-mono">{ data.Config.FilePattern }</dd>
|
|
</div>
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Error Information (if any) -->
|
|
if data.JobHistory.Status == "failed" && data.JobHistory.ErrorMessage != "" {
|
|
<div class="p-4 mb-8 text-red-800 border-l-4 border-red-300 bg-red-50 dark:bg-red-900/20 dark:text-red-400 dark:border-red-800 rounded-lg">
|
|
<div class="flex items-center mb-2">
|
|
<i class="fas fa-exclamation-triangle flex-shrink-0 mr-2 text-red-600 dark:text-red-500"></i>
|
|
<h3 class="text-lg font-medium">Error Information</h3>
|
|
</div>
|
|
<div class="mt-2">
|
|
<pre class="text-sm whitespace-pre-wrap font-mono p-3 bg-white dark:bg-gray-900 rounded-lg border border-red-200 dark:border-red-800">{ data.JobHistory.ErrorMessage }</pre>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="flex flex-col sm:flex-row gap-4 mt-8">
|
|
<a href="/jobs" class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700 inline-flex items-center justify-center">
|
|
<i class="fas fa-list-ul mr-2"></i> View All Jobs
|
|
</a>
|
|
<a href={ templ.SafeURL(fmt.Sprintf("/jobs/%d", data.Job.ID)) } class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800 inline-flex items-center justify-center">
|
|
<i class="fas fa-edit mr-2"></i> Edit Job
|
|
</a>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
// formatDuration formats a duration in a human-readable way
|
|
func formatDuration(d time.Duration) string {
|
|
d = d.Round(time.Second)
|
|
h := d / time.Hour
|
|
d -= h * time.Hour
|
|
m := d / time.Minute
|
|
d -= m * time.Minute
|
|
s := d / time.Second
|
|
|
|
if h > 0 {
|
|
return fmt.Sprintf("%dh %dm %ds", h, m, s)
|
|
}
|
|
if m > 0 {
|
|
return fmt.Sprintf("%dm %ds", m, s)
|
|
}
|
|
return fmt.Sprintf("%ds", s)
|
|
}
|