240 lines
11 KiB
HTML
240 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
{% set editing = session is not none %}
|
|
|
|
{# 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 %}
|
|
{% block content %}
|
|
|
|
<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>
|
|
|
|
{% set f = prefill or session %}
|
|
|
|
<form method="post"
|
|
action="{{ url_for('sessions.edit', session_id=session.id) if editing else url_for('sessions.new') }}"
|
|
style="max-width:600px;">
|
|
|
|
<input type="hidden" name="session_type" id="session_type_hidden" value="{{ eff_type }}">
|
|
|
|
{# 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>
|
|
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;">
|
|
<div>
|
|
<label class="fl">{{ _('Date *') }}</label>
|
|
<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>
|
|
|
|
{# 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 '') }}"
|
|
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;">
|
|
<label class="fl">{{ _('Location') }}</label>
|
|
<input type="text" name="location_name" value="{{ f.location_name if f else '' }}"
|
|
placeholder="ex : Nom du stand, commune"
|
|
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
|
|
</div>
|
|
|
|
{# ── 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>
|
|
|
|
{# ── Section: weather ── #}
|
|
<h2>{{ _('Weather') }}</h2>
|
|
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:1rem;margin-bottom:1rem;">
|
|
<div>
|
|
<label class="fl">{{ _('Conditions') }}</label>
|
|
<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>
|
|
<label class="fl">{{ _('Temp. (°C)') }}</label>
|
|
<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>
|
|
<label class="fl">{{ _('Wind (km/h)') }}</label>
|
|
<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>
|
|
|
|
{# ── Section: equipment & ammunition ── #}
|
|
<h2>{{ _('Equipment & Ammunition') }}</h2>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem;">
|
|
<div>
|
|
<label class="fl">{{ _('Rifle / Handgun') }}</label>
|
|
<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;">
|
|
<option value="">{{ _('— none —') }}</option>
|
|
{% 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;">
|
|
<a href="{{ url_for('equipment.new') }}">{{ _('Add a rifle first') }}</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div>
|
|
<label class="fl">{{ _('Scope') }}</label>
|
|
<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;">
|
|
<option value="">{{ _('— none —') }}</option>
|
|
{% 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>
|
|
<label class="fl">{{ _('Ammo brand') }}</label>
|
|
<input type="text" name="ammo_brand" value="{{ f.ammo_brand if f else '' }}"
|
|
placeholder="ex : Lapua, RWS"
|
|
style="width:100%;padding:0.55rem 0.75rem;border:1px solid #ccc;border-radius:4px;font-size:0.95rem;">
|
|
</div>
|
|
<div>
|
|
<label class="fl">{{ _('Bullet weight (gr)') }}</label>
|
|
<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>
|
|
<label class="fl">{{ _('Lot number') }}</label>
|
|
<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>
|
|
|
|
{# ── Section: notes & visibility ── #}
|
|
<h2>{{ _('Notes & Visibility') }}</h2>
|
|
<div style="margin-bottom:1rem;">
|
|
<label class="fl">{{ _('Notes') }}</label>
|
|
<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;">
|
|
{{ _('Make this session public (visible in the community feed)') }}
|
|
</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;">
|
|
{{ _('Save') if editing else _('Create session') }}
|
|
</button>
|
|
<a href="{{ url_for('sessions.detail', session_id=session.id) if editing else url_for('sessions.index') }}"
|
|
style="font-size:0.9rem;color:#666;">{{ _('Cancel') }}</a>
|
|
</div>
|
|
</form>
|
|
|
|
<style>.fl { display:block; font-size:.88rem; font-weight:600; color:#444; margin-bottom:.3rem; }</style>
|
|
|
|
<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>
|
|
{% endblock %}
|