Files
ShooterHub/frontend/chrono.html
2026-04-02 11:24:30 +02:00

211 lines
9.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Chronograph Analyser ShooterHub</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="/css/app.css">
<style>
.session-item { cursor: pointer; border-radius: 6px; padding: .5rem .75rem; }
.session-item:hover { background: #f0f4ff; }
.session-item.active { background: #0d6efd; color: #fff; }
.session-item.active .text-muted { color: rgba(255,255,255,.7) !important; }
#rightPanel { min-height: 60vh; }
.stat-table td:first-child { color: #6c757d; width: 160px; }
.stat-table td:last-child { font-weight: 500; }
.group-chart { max-width: 100%; border-radius: 6px; }
.upload-area {
border: 2px dashed #ced4da; border-radius: 8px;
padding: 2rem; text-align: center; color: #6c757d;
cursor: pointer; transition: border-color .2s;
}
.upload-area:hover, .upload-area.drag-over { border-color: #0d6efd; color: #0d6efd; }
.photo-thumb { width: 120px; height: 90px; object-fit: cover; border-radius: 6px; cursor: pointer; }
.group-size-badge { font-size: .8rem; }
</style>
</head>
<body>
<div id="navbar"></div>
<div class="container-fluid py-4">
<div class="container mb-4 d-flex align-items-center justify-content-between flex-wrap gap-2">
<div>
<h2 class="fw-bold mb-0"><i class="bi bi-speedometer2 me-2"></i>Chronograph Analyser</h2>
<p class="text-muted small mb-0">Upload a CSV from your chronograph and analyse your shot data.</p>
</div>
<button class="btn btn-primary" id="uploadBtn">
<i class="bi bi-upload me-1"></i>Upload CSV
</button>
</div>
<!-- Upload modal -->
<div class="modal fade" id="uploadModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-upload me-2"></i>Upload chronograph CSV</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Session name</label>
<input type="text" class="form-control" id="uploadName" placeholder="e.g. 308 match range day">
</div>
<div class="mb-3">
<label class="form-label">Date (optional)</label>
<input type="date" class="form-control" id="uploadDate">
</div>
<div class="mb-3">
<label class="form-label">Chronograph model</label>
<select class="form-select" id="uploadChronoType">
<option value="garmin_xero_c1_pro">Garmin Xero C1 Pro</option>
</select>
</div>
<div class="mb-3">
<label class="form-label">Velocity unit in CSV</label>
<div class="btn-group w-100" role="group">
<input type="radio" class="btn-check" name="uploadVelUnit" id="uploadUnitFps" value="fps" checked>
<label class="btn btn-outline-secondary" for="uploadUnitFps">fps (feet/s)</label>
<input type="radio" class="btn-check" name="uploadVelUnit" id="uploadUnitMps" value="mps">
<label class="btn btn-outline-secondary" for="uploadUnitMps">m/s (metres/s)</label>
</div>
</div>
<div class="mb-3">
<label class="form-label">CSV file</label>
<div class="upload-area" id="dropArea">
<i class="bi bi-file-earmark-spreadsheet fs-2 d-block mb-2"></i>
Drop file here or click to browse
<input type="file" id="csvFileInput" accept=".csv,.txt" class="d-none">
</div>
<div id="selectedFileName" class="small text-success mt-1 d-none"></div>
</div>
<div class="alert alert-secondary small">
<strong>Expected CSV format:</strong><br>
<code>idx, speed, std_dev, energy, power_factor, time</code><br>
Time column: <code>HH:MM:SS</code> or <code>HH:MM:SS.mmm</code>
</div>
<div id="uploadAlert" class="alert alert-danger d-none small"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="uploadSubmitBtn" disabled>
<span id="uploadSpinnerInline" class="spinner-border spinner-border-sm me-1 d-none"></span>
Analyse
</button>
</div>
</div>
</div>
</div>
<div class="row g-0" style="min-height: 70vh;">
<!-- ── Left: sessions list ───────────────────────────────────────────── -->
<div class="col-md-3 border-end px-3">
<h6 class="fw-semibold text-muted small text-uppercase mb-3">Sessions</h6>
<div id="sessionsSpinner" class="text-center py-3">
<div class="spinner-border spinner-border-sm text-primary"></div>
</div>
<div id="sessionsEmpty" class="text-muted small d-none">No sessions yet.</div>
<div id="sessionsList"></div>
</div>
<!-- ── Right: analysis results ───────────────────────────────────────── -->
<div class="col-md-9 px-4" id="rightPanel">
<div class="text-center text-muted py-5" id="noSessionMsg">
<i class="bi bi-arrow-left fs-3 d-block mb-2"></i>
Select a session to view the analysis
</div>
<div id="analysisContent" class="d-none">
<!-- Header -->
<div class="d-flex justify-content-between align-items-start mb-4 flex-wrap gap-2">
<div>
<h4 class="fw-bold mb-0" id="sessionTitle"></h4>
<span class="text-muted small" id="sessionDate"></span>
</div>
<div class="d-flex gap-2 flex-wrap">
<button class="btn btn-sm btn-outline-secondary" id="togglePublicBtn" title="Make public/private">
<i class="bi bi-lock me-1" id="togglePublicIcon"></i><span id="togglePublicLabel">Private</span>
</button>
<button class="btn btn-sm btn-outline-danger" id="deleteSessionBtn">
<i class="bi bi-trash me-1"></i>Delete
</button>
<button class="btn btn-sm btn-outline-secondary" id="downloadPdfBtn">
<i class="bi bi-file-earmark-pdf me-1"></i>Download PDF
</button>
</div>
</div>
<!-- Loading charts -->
<div id="chartsSpinner" class="text-center py-5">
<div class="spinner-border text-primary"></div>
<p class="text-muted small mt-2">Generating charts…</p>
</div>
<!-- Charts + stats -->
<div id="chartsContent" class="d-none">
<!-- Overview chart -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body">
<h5 class="fw-semibold mb-3"><i class="bi bi-bar-chart-line me-2"></i>Session overview</h5>
<div id="overallStatsWrap" class="mb-3"></div>
<img id="overviewChart" class="group-chart w-100" alt="Overview chart">
</div>
</div>
<!-- Per-group sections -->
<div id="groupSections"></div>
</div><!-- /chartsContent -->
</div><!-- /analysisContent -->
</div>
</div>
</div>
<!-- ── Photo upload modal (per group) ──────────────────────────────────────── -->
<div class="modal fade" id="photoModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="bi bi-image me-2"></i>Add photo to group</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Image file</label>
<input type="file" class="form-control" id="photoFileInput" accept="image/jpeg,image/png,image/webp">
</div>
<div class="mb-3">
<label class="form-label">Caption (optional)</label>
<input type="text" class="form-control" id="photoCaption" placeholder="e.g. 100m cold bore">
</div>
<div id="photoAlert" class="alert alert-danger d-none small"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="photoSubmitBtn">
<span id="photoSpinner" class="spinner-border spinner-border-sm me-1 d-none"></span>
Upload
</button>
</div>
</div>
</div>
</div>
<div id="toastContainer"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="/js/api.js"></script>
<script src="/js/utils.js"></script>
<script src="/js/i18n.js"></script>
<script src="/js/nav.js"></script>
<script src="/js/chrono.js"></script>
</body>
</html>