Files
Maintainarr/internal/views/pages/updates.gohtml

265 lines
12 KiB
Plaintext

{{define "updatesNodeRows"}}
{{range .}}
<tr>
<td>
<div class="fw-semibold text-body-emphasis">{{.Name}}</div>
<div class="small text-body-secondary">{{packageManagerLabel .PackageManager}}{{if .UpdatesLastError}} · {{.UpdatesLastError}}{{end}}</div>
</td>
<td class="text-nowrap">
<div class="d-flex align-items-center gap-2">
<span class="badge {{if gt .UpdatesAvailable 0}}text-bg-warning{{else}}text-bg-secondary{{end}}">{{.UpdatesAvailable}}</span>
{{if and (gt .UpdatesAvailable 0) .UpdatesDetails}}
<button
class="btn btn-link btn-sm updates-details-trigger"
type="button"
data-bs-toggle="modal"
data-bs-target="#updatePackagesModal"
data-node-name="{{.Name}}"
data-package-count="{{.UpdatesAvailable}}"
data-packages="{{.UpdatesDetails}}"
>
View packages
</button>
{{end}}
</div>
</td>
<td class="text-nowrap">{{if .UpdatesLastChecked}}{{.UpdatesLastChecked.Format "2006-01-02 15:04:05"}}{{else}}Never{{end}}</td>
<td>
<form id="node-policy-{{.ID}}" method="post" action="/updates/nodes/{{.ID}}/policy"></form>
<input class="form-check-input mt-0" type="checkbox" name="updates_scan_enabled" form="node-policy-{{.ID}}" {{if .UpdatesScanEnabled}}checked{{end}}>
</td>
<td>
<input class="form-check-input mt-0" type="checkbox" name="auto_updates_enabled" form="node-policy-{{.ID}}" {{if .AutoUpdatesEnabled}}checked{{end}}>
</td>
<td>
<div class="d-flex flex-wrap gap-2">
<button class="btn btn-outline-secondary btn-sm" type="submit" form="node-policy-{{.ID}}"><i class="ti ti-device-floppy"></i></button>
<form method="post" action="/updates/nodes/{{.ID}}/scan">
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-refresh"></i></button>
</form>
<form method="post" action="/updates/nodes/{{.ID}}/apply">
<button class="btn btn-primary btn-sm" type="submit"><i class="ti ti-package-import"></i></button>
</form>
</div>
</td>
</tr>
{{end}}
{{end}}
{{define "content"}}
{{$data := .Content}}
<section class="row g-3 mb-4">
<div class="col-12 col-md-6 col-xxl-3">
<article class="card border-0 shadow-sm uptime-summary-card h-100">
<div class="card-body">
<div class="uptime-summary-label"><i class="ti ti-package me-2"></i>Pending packages</div>
<div class="uptime-summary-value">{{$data.TotalUpdates}}</div>
</div>
</article>
</div>
<div class="col-12 col-md-6 col-xxl-3">
<article class="card border-0 shadow-sm uptime-summary-card h-100">
<div class="card-body">
<div class="uptime-summary-label"><i class="ti ti-server-2 me-2"></i>Nodes with updates</div>
<div class="uptime-summary-value">{{$data.NodesWithUpdates}}</div>
</div>
</article>
</div>
<div class="col-12 col-md-6 col-xxl-3">
<article class="card border-0 shadow-sm uptime-summary-card h-100">
<div class="card-body">
<div class="uptime-summary-label"><i class="ti ti-radar-2 me-2"></i>Scanned nodes</div>
<div class="uptime-summary-value">{{$data.ScannedNodes}}</div>
</div>
</article>
</div>
<div class="col-12 col-md-6 col-xxl-3">
<article class="card border-0 shadow-sm uptime-summary-card h-100">
<div class="card-body">
<div class="uptime-summary-label"><i class="ti ti-bolt me-2"></i>Auto update</div>
<div class="uptime-summary-value">{{$data.AutoUpdateNodes}}</div>
</div>
</article>
</div>
</section>
<section class="card border-0 shadow-sm mb-4">
<div class="card-body p-4 d-flex flex-column flex-xl-row align-items-xl-center justify-content-between gap-4">
<div class="min-w-0">
<div class="uptime-summary-label mb-2"><i class="ti ti-clock-cog me-2"></i>Global Auto Update Window</div>
<div class="h4 mb-1">{{updateWindowSummary $data.GlobalWindowStart $data.GlobalWindowEnd $data.GlobalUpdateDays}}</div>
<div class="text-body-secondary small">Automatic package upgrades respect this org-wide schedule so updates stay out of peak hours.</div>
</div>
<div class="d-flex flex-wrap gap-2">
<button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#globalUpdateWindowModal">
<i class="ti ti-settings me-2"></i>Edit Window
</button>
<form method="post" action="/updates/scan">
<button class="btn btn-outline-primary" type="submit"><i class="ti ti-refresh me-2"></i>Scan all nodes</button>
</form>
<form method="post" action="/updates/apply">
<button class="btn btn-primary" type="submit"><i class="ti ti-package-import me-2"></i>Update all</button>
</form>
</div>
</div>
</section>
{{range $data.Groups}}
<section class="card border-0 shadow-sm mb-4">
<div class="card-body p-4">
<div class="d-flex flex-column flex-xl-row align-items-xl-center justify-content-between gap-3 mb-4">
<div>
<h2 class="h4 mb-1">{{.Name}}</h2>
<div class="text-body-secondary small">{{.UpdatesAvailable}} updates on {{.NodesWithUpdates}} nodes</div>
</div>
<div class="d-flex flex-wrap gap-2">
<form method="post" action="/updates/groups/{{.ID}}/scan">
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-refresh me-1"></i>Scan Group</button>
</form>
<form method="post" action="/updates/groups/{{.ID}}/apply">
<button class="btn btn-primary btn-sm" type="submit"><i class="ti ti-package-import me-1"></i>Update Group</button>
</form>
</div>
</div>
<form method="post" action="/updates/groups/{{.ID}}/policy" class="updates-policy-row mb-4">
<label class="option-check">
<input class="form-check-input" type="checkbox" name="updates_scan_enabled" {{if .ScanEnabled}}checked{{end}}>
<span>Scan this group</span>
</label>
<label class="option-check">
<input class="form-check-input" type="checkbox" name="auto_updates_enabled" {{if .AutoUpdate}}checked{{end}}>
<span>Auto update this group</span>
</label>
<button class="btn btn-outline-secondary btn-sm" type="submit"><i class="ti ti-device-floppy me-1"></i>Save</button>
</form>
<div class="table-responsive">
<table class="table align-middle mb-0 uptime-table uptime-table-nodes">
<thead>
<tr>
<th>Node</th>
<th>Updates</th>
<th>Last Scan</th>
<th>Scan</th>
<th>Auto</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{template "updatesNodeRows" .Nodes}}
</tbody>
</table>
</div>
</div>
</section>
{{end}}
{{if $data.Ungrouped}}
<section class="card border-0 shadow-sm">
<div class="card-body p-4">
<h2 class="h4 mb-4">Ungrouped</h2>
<div class="table-responsive">
<table class="table align-middle mb-0 uptime-table uptime-table-nodes">
<thead>
<tr>
<th>Node</th>
<th>Updates</th>
<th>Last Scan</th>
<th>Scan</th>
<th>Auto</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{{template "updatesNodeRows" $data.Ungrouped}}
</tbody>
</table>
</div>
</div>
</section>
{{end}}
<div class="modal fade" id="globalUpdateWindowModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content border-0 shadow-lg">
<form method="post" action="/updates/settings/window">
<div class="modal-header">
<div class="min-w-0">
<h2 class="modal-title fs-5"><i class="ti ti-clock-cog me-2"></i>Global Auto Update Window</h2>
<div class="small text-body-secondary">Choose when automatic updates are allowed to run across the whole organization.</div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 p-lg-5">
<div class="updates-window-editor">
<section class="add-vm-panel">
<div class="add-vm-panel-title"><i class="ti ti-calendar-time me-2"></i>Allowed Days</div>
<div class="updates-weekdays updates-weekdays-lg">
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="mon" {{if hasUpdateDay $data.GlobalUpdateDays "mon"}}checked{{end}}><span>Monday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="tue" {{if hasUpdateDay $data.GlobalUpdateDays "tue"}}checked{{end}}><span>Tuesday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="wed" {{if hasUpdateDay $data.GlobalUpdateDays "wed"}}checked{{end}}><span>Wednesday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="thu" {{if hasUpdateDay $data.GlobalUpdateDays "thu"}}checked{{end}}><span>Thursday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="fri" {{if hasUpdateDay $data.GlobalUpdateDays "fri"}}checked{{end}}><span>Friday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="sat" {{if hasUpdateDay $data.GlobalUpdateDays "sat"}}checked{{end}}><span>Saturday</span></label>
<label class="updates-day-chip updates-day-chip-lg"><input type="checkbox" name="auto_update_days" value="sun" {{if hasUpdateDay $data.GlobalUpdateDays "sun"}}checked{{end}}><span>Sunday</span></label>
</div>
</section>
<section class="add-vm-panel">
<div class="add-vm-panel-title"><i class="ti ti-clock-hour-9 me-2"></i>Allowed Time Range</div>
<div class="row g-3">
<div class="col-12 col-md-6">
<label class="form-label">Start</label>
<input class="form-control" type="time" name="auto_update_window_start" value="{{$data.GlobalWindowStart}}">
</div>
<div class="col-12 col-md-6">
<label class="form-label">End</label>
<input class="form-control" type="time" name="auto_update_window_end" value="{{$data.GlobalWindowEnd}}">
</div>
</div>
<div class="small text-body-secondary mt-3">Leave start/end empty to allow automatic updates at any time on the selected days.</div>
</section>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal"><i class="ti ti-x me-1"></i>Cancel</button>
<button type="submit" class="btn btn-primary"><i class="ti ti-device-floppy me-1"></i>Save Window</button>
</div>
</form>
</div>
</div>
</div>
<div class="modal fade" id="updatePackagesModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content border-0 shadow-lg">
<div class="modal-header">
<div class="min-w-0">
<h2 class="modal-title fs-5"><i class="ti ti-package me-2"></i>Pending Packages</h2>
<div class="small text-body-secondary" data-update-packages-node></div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-0">
<div class="table-responsive">
<table class="table align-middle mb-0 updates-packages-table">
<thead>
<tr>
<th>Package</th>
<th>Current</th>
<th>Available</th>
<th>Arch</th>
</tr>
</thead>
<tbody data-update-packages-body>
<tr>
<td colspan="4" class="text-body-secondary text-center py-4">No pending packages.</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
{{end}}