Files
ShooterHub/frontend/chrono.html

211 lines
9.3 KiB
HTML
Raw Normal View History

<!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>