feat: timeline refactor, docking UI, location/state dropdowns, sim_time cleanup

Templates:
- mission_timeline_preview: client-side rendering with lane stacking, wheel zoom,
  drag pan, percentage-based layout, event dedup, scale labels
- asset_detail: dock/undock modals with time selection, future event warning,
  Mission Label column replacing Location/State
- asset_entry_form: all_locations expanded to 72 items (draft board hierarchy),
  all_states dynamic from DB, event point dropdown uses same list
- Remove sim_time from all template url_for links
- base.html: remove Today button

JS:
- app.js: sim_time controls use POST /api/v1/sim-time API instead of URL param

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-01 15:29:10 +08:00
co-authored by Claude Opus 4.7
parent cc4c460840
commit 2fef743e83
15 changed files with 1791 additions and 1966 deletions
+54
View File
@@ -0,0 +1,54 @@
// ─── Sidebar ───
function initSidebar() {
document.querySelectorAll('.ni[data-pg]').forEach(el => {
el.addEventListener('click', function(e) {
e.preventDefault();
window.location.href = this.getAttribute('href');
});
});
document.querySelectorAll('.nst').forEach(el => {
el.addEventListener('click', function() { this.classList.toggle('on'); });
});
}
initSidebar();
// ─── Global Time Control ───
function openTimeModal() {
const display = document.getElementById('sim-time-display');
if (!display) return;
const modal = document.getElementById('time-modal');
const input = document.getElementById('time-input');
if (modal && input) {
input.value = display.textContent.trim().replace(' ', 'T');
modal.classList.add('open');
}
}
function closeTimeModal() {
const modal = document.getElementById('time-modal');
if (modal) modal.classList.remove('open');
}
async function applyTime() {
const input = document.getElementById('time-input');
if (!input || !input.value) return;
await setSimTimeAndReload(input.value.replace('T', ' ') + ':00');
}
async function shiftTime(days) {
const display = document.getElementById('sim-time-display');
if (!display) return;
const d = new Date(display.textContent.trim().replace(' ', 'T') + 'Z');
d.setUTCDate(d.getUTCDate() + days);
await setSimTimeAndReload(d.toISOString().slice(0, 10) + ' 09:00');
}
async function resetTime() {
await setSimTimeAndReload('2060-03-12 09:00');
}
async function setSimTimeAndReload(timeStr) {
try {
await fetch('/api/v1/sim-time', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({sim_time: timeStr})
});
} catch(e) {}
location.reload();
}