2026-03-17 17:20:54 +01:00
{% extends "base.html" %}
{% set editing = session is not none %}
2026-03-19 16:42:37 +01:00
{# Effective type: prefill form > existing session > URL param #}
{% set eff_type = (prefill.session_type if prefill else None) or (session.session_type if session else None) or selected_type or '' %}
{# Find display name for this type #}
{% set type_name = '' %}
{% for slug, name, _ in (session_types or []) %}{% if slug == eff_type %}{% set type_name = name %}{% endif %}{% endfor %}
{% block title %}{{ _('Edit session') if editing else _('New session') }} — The Shooter's Network{% endblock %}
2026-03-17 17:20:54 +01:00
{% block content %}
2026-03-19 16:42:37 +01:00
< div style = "display:flex;align-items:center;gap:1rem;margin-bottom:.4rem;" >
{% if not editing %}
< a href = "{{ url_for('sessions.new') }}" style = "font-size:0.85rem;color:#888;text-decoration:none;" > {{ _('← Change type') }}< / a >
{% endif %}
< / div >
< h1 style = "margin-bottom:1.5rem;" >
{{ _('Edit session') if editing else _('New session') }}
{% if type_name %}
< span style = "font-size:0.95rem;font-weight:400;color:#666;margin-left:.6rem;" > — {{ type_name }}< / span >
{% endif %}
< / h1 >
2026-03-17 17:20:54 +01:00
{% set f = prefill or session %}
< form method = "post"
action="{{ url_for('sessions.edit', session_id=session.id) if editing else url_for('sessions.new') }}"
2026-03-19 16:42:37 +01:00
style="max-width:600px;">
< input type = "hidden" name = "session_type" id = "session_type_hidden" value = "{{ eff_type }}" >
2026-03-17 17:20:54 +01:00
2026-03-19 16:42:37 +01:00
{# In edit mode: allow changing type via a small selector #}
{% if editing %}
< div style = "margin-bottom:1.5rem;padding:.75rem 1rem;background:#f8f9fb;border-radius:6px;display:flex;align-items:center;gap:1rem;" >
< label style = "font-size:.85rem;font-weight:600;color:#444;white-space:nowrap;" > {{ _('Session type:') }}< / label >
< select id = "type_sel" onchange = "document.getElementById('session_type_hidden').value=this.value;applyType(this.value);"
style="padding:.4rem .7rem;border:1px solid #ccc;border-radius:4px;font-size:0.9rem;background:#fff;">
{% for slug, name, _ in (session_types or []) %}
< option value = "{{ slug }}" { % if slug = = eff_type % } selected { % endif % } > {{ name }}< / option >
{% endfor %}
< / select >
< / div >
{% endif %}
{# ── Section: basic information ── #}
< h2 > {{ _('Basic information') }}< / h2 >
2026-03-17 17:20:54 +01:00
< div style = "display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;" >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Date *') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "date" name = "session_date" required
value="{{ (f.session_date.isoformat() if f.session_date else '') if f else (today or '') }}"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
2026-03-19 16:42:37 +01:00
{# Distance: shown for long_range and pistol_25m (hidden for prs — per stage) #}
< div id = "field_distance_wrap" >
< label class = "fl" > {{ _('Distance (m)') }}< / label >
< input type = "number" name = "distance_m" min = "1" max = "5000" id = "field_distance"
value="{{ f.distance_m if f and f.distance_m else (prefill_distance or '') }}"
2026-03-17 17:20:54 +01:00
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< / div >
< div style = "margin-bottom:1rem;" >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Location') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "text" name = "location_name" value = "{{ f.location_name if f else '' }}"
2026-03-19 16:42:37 +01:00
placeholder="ex : Nom du stand, commune"
2026-03-17 17:20:54 +01:00
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
2026-03-19 16:42:37 +01:00
{# ── Section: shooting position (long_range and pistol_25m) ── #}
< div id = "section_position" >
< div style = "margin-bottom:1rem;" >
< label class = "fl" > {{ _('Shooting position') }}< / label >
< select name = "shooting_position" id = "field_position"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.93rem;background:#fff;">
{# Options injected by JS based on type #}
< / select >
< / div >
< / div >
2026-03-17 17:20:54 +01:00
2026-03-19 16:42:37 +01:00
{# ── Section: weather ── #}
< h2 > {{ _('Weather') }}< / h2 >
2026-03-17 17:20:54 +01:00
< div style = "display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem;" >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Conditions') }}< / label >
2026-03-17 17:20:54 +01:00
< select name = "weather_cond"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.93rem;background:#fff;">
{% for val, label in weather_conditions %}
< option value = "{{ val }}" { % if f and f . weather_cond = = val % } selected { % endif % } > {{ label }}< / option >
{% endfor %}
< / select >
< / div >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Temp. (°C)') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "number" name = "weather_temp_c" step = "0.1"
value="{{ f.weather_temp_c if f and f.weather_temp_c is not none else '' }}"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Wind (km/h)') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "number" name = "weather_wind_kph" step = "0.1" min = "0"
value="{{ f.weather_wind_kph if f and f.weather_wind_kph is not none else '' }}"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< / div >
2026-03-19 16:42:37 +01:00
{# ── Section: equipment & ammunition ── #}
< h2 > {{ _('Equipment & Ammunition') }}< / h2 >
2026-03-17 17:20:54 +01:00
< div style = "display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;" >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Rifle / Handgun') }}< / label >
2026-03-17 17:20:54 +01:00
< select name = "rifle_id"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.93rem;background:#fff;">
2026-03-19 16:42:37 +01:00
< option value = "" > {{ _('— none —') }}< / option >
2026-03-17 17:20:54 +01:00
{% for r in rifles %}
< option value = "{{ r.id }}" { % if f and f . rifle_id = = r . id % } selected { % endif % } >
{{ r.name }}{% if r.caliber %} ({{ r.caliber }}){% endif %}
< / option >
{% endfor %}
< / select >
{% if not rifles %}
< div style = "font-size:0.78rem;color:#aaa;margin-top:.25rem;" >
2026-03-19 16:42:37 +01:00
< a href = "{{ url_for('equipment.new') }}" > {{ _('Add a rifle first') }}< / a >
2026-03-17 17:20:54 +01:00
< / div >
{% endif %}
< / div >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Scope') }}< / label >
2026-03-17 17:20:54 +01:00
< select name = "scope_id"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.93rem;background:#fff;">
2026-03-19 16:42:37 +01:00
< option value = "" > {{ _('— none —') }}< / option >
2026-03-17 17:20:54 +01:00
{% for sc in scopes %}
< option value = "{{ sc.id }}" { % if f and f . scope_id = = sc . id % } selected { % endif % } > {{ sc.name }}< / option >
{% endfor %}
< / select >
< / div >
< / div >
< div style = "display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem;" >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Ammo brand') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "text" name = "ammo_brand" value = "{{ f.ammo_brand if f else '' }}"
2026-03-19 16:42:37 +01:00
placeholder="ex : Lapua, RWS"
2026-03-17 17:20:54 +01:00
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Bullet weight (gr)') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "number" name = "ammo_weight_gr" step = "0.1" min = "0"
value="{{ f.ammo_weight_gr if f and f.ammo_weight_gr is not none else '' }}"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< div >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Lot number') }}< / label >
2026-03-17 17:20:54 +01:00
< input type = "text" name = "ammo_lot" value = "{{ f.ammo_lot if f else '' }}"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
< / div >
< / div >
2026-03-19 16:42:37 +01:00
{# ── Section: notes & visibility ── #}
< h2 > {{ _('Notes & Visibility') }}< / h2 >
2026-03-17 17:20:54 +01:00
< div style = "margin-bottom:1rem;" >
2026-03-19 16:42:37 +01:00
< label class = "fl" > {{ _('Notes') }}< / label >
2026-03-17 17:20:54 +01:00
< textarea name = "notes" rows = "4"
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;resize:vertical;">{{ f.notes if f else '' }}< / textarea >
< / div >
< div style = "margin-bottom:1.5rem;" >
< label style = "display:flex;align-items:center;gap:0.6rem;cursor:pointer;font-size:0.95rem;" >
< input type = "checkbox" name = "is_public" value = "1"
{% if f and f.is_public %}checked{% endif %}
style="width:16px;height:16px;">
2026-03-19 16:42:37 +01:00
{{ _('Make this session public (visible in the community feed)') }}
2026-03-17 17:20:54 +01:00
< / label >
< / div >
< div style = "display:flex;gap:1rem;align-items:center;" >
< button type = "submit"
style="background:#1a1a2e;color:#fff;border:none;border-radius:4px;padding:0.6rem 1.5rem;font-size:0.95rem;cursor:pointer;">
2026-03-19 16:42:37 +01:00
{{ _('Save') if editing else _('Create session') }}
2026-03-17 17:20:54 +01:00
< / button >
< a href = "{{ url_for('sessions.detail', session_id=session.id) if editing else url_for('sessions.index') }}"
2026-03-19 16:42:37 +01:00
style="font-size:0.9rem;color:#666;">{{ _('Cancel') }}< / a >
2026-03-17 17:20:54 +01:00
< / div >
< / form >
< style > . fl { display : block ; font-size : .88 rem ; font-weight : 600 ; color : #444 ; margin-bottom : .3 rem ; } < / style >
2026-03-19 16:42:37 +01:00
< script >
(function () {
var LR_POS = {{ (long_range_positions | tojson) if long_range_positions else '[]' }};
var P25_POS = {{ (pistol_25m_positions | tojson) if pistol_25m_positions else '[]' }};
function buildOptions(sel, opts, currentVal) {
sel.innerHTML = '';
opts.forEach(function(o) {
var opt = document.createElement('option');
opt.value = o[0]; opt.textContent = o[1];
if (o[0] === currentVal) opt.selected = true;
sel.appendChild(opt);
});
}
var currentPosition = {{ ((f.shooting_position if f else None) or '') | tojson }};
function applyType(t) {
var distWrap = document.getElementById('field_distance_wrap');
var posSection = document.getElementById('section_position');
var posSelect = document.getElementById('field_position');
if (t === 'prs') {
if (distWrap) distWrap.style.display = 'none';
if (posSection) posSection.style.display = 'none';
} else if (t === 'pistol_25m') {
if (distWrap) distWrap.style.display = '';
if (posSection) posSection.style.display = '';
buildOptions(posSelect, P25_POS, currentPosition);
var distField = document.getElementById('field_distance');
if (distField & & !distField.value) distField.value = '25';
} else {
// long_range
if (distWrap) distWrap.style.display = '';
if (posSection) posSection.style.display = '';
buildOptions(posSelect, LR_POS, currentPosition);
}
}
document.addEventListener('DOMContentLoaded', function () {
applyType({{ eff_type | tojson }});
});
// Expose for the type selector in edit mode
window.applyType = applyType;
})();
< / script >
2026-03-17 17:20:54 +01:00
{% endblock %}