- Format dates as YYYY-MM-DD with full timestamp on hover - Add breadcrumb navigation with hospital icon (🏥) across all pages - Restructure main menu with grouped dropdowns: * Hardware (Assets, Components) * Health (Tickets, Failures, Analytics) * Settings (Ingest) - Make table rows clickable on Dashboard, Assets, and Components pages - Add new Customers page with list view - Improve dropdown menu stability with JS hover delay Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
133 lines
3.8 KiB
Cheetah
133 lines
3.8 KiB
Cheetah
{{define "analytics"}}
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
{{template "head" .}}
|
|
<body>
|
|
{{template "topbar" .}}
|
|
{{template "breadcrumbs" .}}
|
|
|
|
<main class="container">
|
|
<section class="card">
|
|
<h2>Query Window</h2>
|
|
<form class="form" method="get" action="/ui/analytics">
|
|
<div class="split">
|
|
<div class="field">
|
|
<label for="start">Start (RFC3339)</label>
|
|
<input class="input" id="start" name="start" value="{{.Start}}" placeholder="2025-01-01T00:00:00Z" />
|
|
</div>
|
|
<div class="field">
|
|
<label for="end">End (RFC3339)</label>
|
|
<input class="input" id="end" name="end" value="{{.End}}" placeholder="2025-02-01T00:00:00Z" />
|
|
</div>
|
|
</div>
|
|
<div class="split">
|
|
<div class="field">
|
|
<label for="horizon_days">Spare Horizon Days</label>
|
|
<input class="input" id="horizon_days" name="horizon_days" value="{{.HorizonDays}}" />
|
|
</div>
|
|
<div class="field">
|
|
<label for="multiplier">Spare Multiplier</label>
|
|
<input class="input" id="multiplier" name="multiplier" value="{{formatFloat .Multiplier 2}}" />
|
|
</div>
|
|
</div>
|
|
<button class="button" type="submit">Run Analytics</button>
|
|
</form>
|
|
{{if .Error}}
|
|
<div class="error">{{.Error}}</div>
|
|
{{end}}
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Lot Metrics</h2>
|
|
{{if .LotMetrics}}
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Lot</th>
|
|
<th>Failures</th>
|
|
<th>Components</th>
|
|
<th>Exposure Hours</th>
|
|
<th>AFR</th>
|
|
<th>MTBF (hrs)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .LotMetrics}}
|
|
<tr>
|
|
<td>{{if .LotCode}}{{.LotCode}}{{else}}Lot {{if .LotID}}{{.LotID}}{{else}}—{{end}}{{end}}</td>
|
|
<td>{{.Failures}}</td>
|
|
<td>{{.ComponentCount}}</td>
|
|
<td>{{formatFloat .ExposureHours 2}}</td>
|
|
<td>{{formatFloat .AFR 4}}</td>
|
|
<td>{{formatFloatPtr .MTBFHours 2}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<div class="meta">No lot metrics in this window.</div>
|
|
{{end}}
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Firmware Risk</h2>
|
|
{{if .FirmwareRisk}}
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Firmware</th>
|
|
<th>Failures</th>
|
|
<th>Components</th>
|
|
<th>Failure Rate</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .FirmwareRisk}}
|
|
<tr>
|
|
<td>{{.FirmwareVersion}}</td>
|
|
<td>{{.Failures}}</td>
|
|
<td>{{.ComponentCount}}</td>
|
|
<td>{{formatFloatPtr .FailureRate 4}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<div class="meta">No firmware risk data in this window.</div>
|
|
{{end}}
|
|
</section>
|
|
|
|
<section class="card">
|
|
<h2>Spare Forecast</h2>
|
|
{{if .SpareForecast}}
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Lot</th>
|
|
<th>Components</th>
|
|
<th>AFR</th>
|
|
<th>Expected Failures</th>
|
|
<th>Spares Needed</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .SpareForecast}}
|
|
<tr>
|
|
<td>{{if .LotCode}}{{.LotCode}}{{else}}Lot {{if .LotID}}{{.LotID}}{{else}}—{{end}}{{end}}</td>
|
|
<td>{{.ComponentCount}}</td>
|
|
<td>{{formatFloat .AFR 4}}</td>
|
|
<td>{{formatFloat .ExpectedFailures 2}}</td>
|
|
<td>{{.SparesNeeded}}</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
</table>
|
|
{{else}}
|
|
<div class="meta">No spare forecast data in this window.</div>
|
|
{{end}}
|
|
</section>
|
|
</main>
|
|
</body>
|
|
</html>
|
|
{{end}}
|