First commit of claude's rework in django + vanillajs fronted

This commit is contained in:
Gérald Colangelo
2026-04-02 11:24:30 +02:00
parent 7710a876df
commit fde92f92db
163 changed files with 84852 additions and 15 deletions

200
frontend/group-size.html Normal file
View File

@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Group Size Calculator 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>
.upload-area {
border: 2px dashed #ced4da; border-radius: 10px;
padding: 3.5rem 2rem; text-align: center; color: #6c757d;
cursor: pointer; transition: border-color .2s, color .2s;
}
.upload-area:hover, .upload-area.drag-over { border-color: #0d6efd; color: #0d6efd; }
#annotCanvas { display: block; width: 100%; border-radius: 6px; border: 1px solid #dee2e6; }
.input-section { background: #f8f9fa; border-radius: 8px; padding: .85rem 1rem; margin-bottom: .6rem; }
.input-section h6 { margin-bottom: .6rem; font-size: .85rem; }
.legend-dot { display: inline-block; width: 11px; height: 11px; border-radius: 50%; vertical-align: middle; }
.result-row td:first-child { color: #6c757d; font-size: .85rem; width: 170px; }
.result-row td:last-child { font-weight: 500; font-size: .9rem; }
</style>
</head>
<body>
<div id="navbar"></div>
<div class="container-fluid py-3">
<div class="row g-3">
<!-- ══ Left: controls ═══════════════════════════════════════════════════ -->
<div class="col-md-4 col-xl-3">
<div id="backBtnWrap" class="d-none mb-2">
<a id="backBtn" href="/chrono.html" class="btn btn-sm btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>Back to analysis
</a>
</div>
<h5 class="fw-bold mb-1">
<i class="bi bi-crosshair2 me-2 text-primary"></i>Group Size Calculator
</h5>
<p class="text-muted small mb-3">Annotate a target photo to measure group size, mean radius, and correction.</p>
<!-- Unit switch -->
<div class="input-section">
<h6 class="fw-semibold mb-2"><i class="bi bi-rulers me-1"></i>Display units</h6>
<div class="btn-group btn-group-sm w-100" id="gsDistUnitBtns">
<button class="btn btn-outline-secondary" data-dist-unit="mm">mm</button>
<button class="btn btn-outline-secondary" data-dist-unit="moa">MOA</button>
<button class="btn btn-outline-secondary" data-dist-unit="mrad">MRAD</button>
</div>
</div>
<!-- Setup -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-sliders me-1"></i>Setup</h6>
<div class="mb-2">
<label class="form-label form-label-sm mb-1">Distance (m)</label>
<input type="number" class="form-control form-control-sm" id="distanceM"
placeholder="e.g. 100" min="1" step="1">
<div class="form-text">Required for MOA values</div>
</div>
<div class="mb-2">
<label class="form-label form-label-sm mb-1">
Reference length (mm) <span class="text-danger">*</span>
</label>
<input type="number" class="form-control form-control-sm" id="refLength"
placeholder="e.g. 100" min="1" step="0.5">
<div class="form-text">Real-world length of the line you'll draw</div>
</div>
<div>
<label class="form-label form-label-sm mb-1">Bullet ⌀ (mm)</label>
<input type="number" class="form-control form-control-sm" id="bulletDia"
placeholder="e.g. 7.62" min="0" step="0.1">
<div class="form-text">Draws bullet holes at correct scale</div>
</div>
</div>
<!-- Annotation buttons -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-cursor me-1"></i>Annotation</h6>
<div class="d-grid gap-2 mb-2">
<button class="btn btn-sm btn-primary w-100" id="btnRef" onclick="btnRef()">
<i class="bi bi-rulers me-1"></i>① Draw reference line
</button>
<button class="btn btn-sm btn-outline-secondary w-100" id="btnPoa" onclick="btnPoa()">
<i class="bi bi-crosshair me-1"></i>② Set POA
</button>
<button class="btn btn-sm btn-outline-secondary w-100" id="btnPoi" onclick="btnPoi()">
<i class="bi bi-plus-circle me-1"></i>③ Add POI (bullet holes)
</button>
</div>
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-secondary flex-fill" onclick="btnUndo()">
<i class="bi bi-arrow-counterclockwise"></i> Undo
</button>
<button class="btn btn-sm btn-outline-danger flex-fill" onclick="btnReset()">
<i class="bi bi-trash"></i> Reset
</button>
</div>
</div>
<!-- Legend -->
<div class="input-section">
<h6 class="fw-semibold"><i class="bi bi-palette me-1"></i>Legend</h6>
<div class="small vstack gap-1">
<div><span class="legend-dot me-2" style="background:#3b82f6"></span>Reference line</div>
<div><span class="legend-dot me-2" style="background:#22c55e"></span>POA — Point of Aim</div>
<div><span class="legend-dot me-2" style="background:#ef4444"></span>POI — Bullet holes</div>
<div><span class="legend-dot me-2" style="background:#f97316"></span>Group centroid</div>
</div>
</div>
<button class="btn btn-sm btn-outline-secondary w-100" onclick="loadNewPhoto()">
<i class="bi bi-image me-1"></i>Load different photo
</button>
</div>
<!-- ══ Right: canvas + results ══════════════════════════════════════════ -->
<div class="col-md-8 col-xl-9">
<!-- Upload section -->
<div id="uploadSection">
<div class="upload-area" id="dropArea">
<i class="bi bi-image fs-1 d-block mb-3 opacity-40"></i>
<p class="fw-semibold mb-1">Drop your target photo here</p>
<p class="small mb-0 text-muted">or click to browse — JPEG, PNG, WebP</p>
<input type="file" id="fileInput" accept="image/jpeg,image/png,image/webp" class="d-none">
</div>
<div id="uploadError" class="alert alert-danger mt-2 d-none small"></div>
</div>
<!-- Annotation section -->
<div id="annotSection" class="d-none">
<!-- Status + linked badge -->
<div class="d-flex align-items-center gap-2 mb-2 flex-wrap">
<span id="statusMsg" class="text-primary small fw-semibold"></span>
<span id="linkedBadge" class="badge bg-success d-none"></span>
</div>
<!-- Canvas -->
<div id="canvasWrap" class="mb-3">
<canvas id="annotCanvas"></canvas>
</div>
<!-- Results -->
<div class="card border-0 shadow-sm">
<div class="card-body">
<h6 class="fw-semibold mb-2"><i class="bi bi-bar-chart-line me-1 text-primary"></i>Results</h6>
<p id="resultsHint" class="text-muted small mb-0">Draw a reference line on the image.</p>
<div id="resultsSection" class="d-none">
<table class="table table-sm mb-2">
<tbody>
<tr class="result-row">
<td>Shots (POIs)</td>
<td id="resPOICount"></td>
</tr>
<tr class="result-row">
<td>Group size (ES)</td>
<td id="resGroupSize"></td>
</tr>
<tr class="result-row">
<td>Mean radius</td>
<td id="resMeanRadius"></td>
</tr>
<tr class="result-row">
<td><i class="bi bi-dot text-warning"></i> Group offset from POA</td>
<td id="resOffset"></td>
</tr>
<tr class="result-row">
<td><i class="bi bi-arrow-right text-danger"></i> Correction needed</td>
<td id="resCorrection"></td>
</tr>
</tbody>
</table>
<p id="resMoaNote" class="text-muted small d-none mb-2">
<i class="bi bi-info-circle me-1"></i>Enter the distance above to see MOA values.
</p>
<button id="saveBtn" class="btn btn-sm btn-outline-primary d-none" onclick="saveToApi()">
<i class="bi bi-floppy me-1"></i>Save POIs & compute server-side
</button>
</div>
</div>
</div>
</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/group-size.js"></script>
</body>
</html>