// ── Storage ─────────────────────────────────────────────────────────────────── const PLANNER_KEY = 'ff14-planner-plans'; function loadPlans() { try { return JSON.parse(localStorage.getItem(PLANNER_KEY) || '[]'); } catch { return []; } } function savePlans(plans) { localStorage.setItem(PLANNER_KEY, JSON.stringify(plans)); } // ── CRUD ────────────────────────────────────────────────────────────────────── function createPlan(name) { const plan = { id: crypto.randomUUID(), name: name.trim() || 'Unbenannter Plan', createdAt: Date.now(), updatedAt: Date.now(), source: null, jobComposition: Array(8).fill(''), mechanics: [] }; const all = loadPlans(); all.push(plan); savePlans(all); return plan; } function getPlan(id) { return loadPlans().find(p => p.id === id) ?? null; } function updatePlan(id, changes) { const all = loadPlans(); const idx = all.findIndex(p => p.id === id); if (idx === -1) return null; all[idx] = { ...all[idx], ...changes, updatedAt: Date.now() }; savePlans(all); return all[idx]; } function deletePlan(id) { savePlans(loadPlans().filter(p => p.id !== id)); } function copyPlan(id) { const all = loadPlans(); const orig = all.find(p => p.id === id); if (!orig) return null; const copy = { ...JSON.parse(JSON.stringify(orig)), id: crypto.randomUUID(), name: orig.name + ' (Kopie)', createdAt: Date.now(), updatedAt: Date.now() }; all.push(copy); savePlans(all); return copy; } // ── State ───────────────────────────────────────────────────────────────────── let activePlanId = null; // ── Helpers ─────────────────────────────────────────────────────────────────── function escHtml(str) { return String(str ?? '') .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } function fmtDate(ts) { return new Date(ts).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); } function fmtTimestamp(ms) { const s = Math.floor(ms / 1000); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`; } function fmtNumber(n) { return Number(n).toLocaleString('de-DE'); } // ── Rendering: Plan List ────────────────────────────────────────────────────── function renderPlanList() { const el = document.getElementById('plan-list'); if (!el) return; const plans = loadPlans(); if (plans.length === 0) { el.innerHTML = '
Noch keine Pläne vorhanden
'; return; } el.innerHTML = plans.map(p => `
${escHtml(p.name)}
${p.mechanics.length} Mechaniken · ${fmtDate(p.updatedAt)}
`).join(''); el.querySelectorAll('.plan-item').forEach(row => { row.addEventListener('click', e => { if (e.target.closest('.plan-item-actions')) return; openPlan(row.dataset.id); }); }); el.querySelectorAll('.plan-btn-copy').forEach(btn => { btn.addEventListener('click', e => { e.stopPropagation(); const copy = copyPlan(btn.dataset.id); if (copy) { renderPlanList(); openPlan(copy.id); } }); }); el.querySelectorAll('.plan-btn-delete').forEach(btn => { btn.addEventListener('click', e => { e.stopPropagation(); const plan = getPlan(btn.dataset.id); if (!plan) return; if (!confirm(`Plan "${plan.name}" löschen?`)) return; deletePlan(btn.dataset.id); if (activePlanId === btn.dataset.id) { activePlanId = null; renderPlanDetail(null); } renderPlanList(); }); }); } // ── Rendering: Plan Detail ──────────────────────────────────────────────────── function renderPlanDetail(plan) { const noplan = document.getElementById('planner-no-plan'); const content = document.getElementById('plan-content'); if (!noplan || !content) return; if (!plan) { noplan.style.display = ''; content.style.display = 'none'; return; } noplan.style.display = 'none'; content.style.display = ''; content.innerHTML = `
${escHtml(plan.name)}
Erstellt ${fmtDate(plan.createdAt)} · ${plan.mechanics.length} Mechaniken
Jobaufstellung
Jobaufstellung wird in einem späteren Schritt konfigurierbar
Mechaniken
${renderMechanicList(plan)}
`; document.getElementById('plan-name-edit-btn')?.addEventListener('click', () => { startRename(plan.id, plan.name); }); } function renderMechanicList(plan) { if (plan.mechanics.length === 0) { return `
📋

Noch keine Mechaniken

Importiere einen Log aus dem Analyse-Tab

`; } return plan.mechanics.map(m => `
${escHtml(fmtTimestamp(m.timestamp))}
${m.phase ? `
${escHtml(m.phase)}
` : ''}
${escHtml(m.name)}
${m.unmitigatedDamage ? `
${fmtNumber(m.unmitigatedDamage)} unmitigiert
` : '' }
${m.assignments.length === 0 ? 'Keine Zuweisung' : m.assignments.map(a => `${escHtml(a.job)} · ${escHtml(a.ability)}` ).join('') }
${m.notes ? `
${escHtml(m.notes)}
` : ''}
`).join(''); } // ── Rename ──────────────────────────────────────────────────────────────────── function startRename(id, currentName) { const display = document.getElementById('plan-name-display'); const editBtn = document.getElementById('plan-name-edit-btn'); if (!display) return; display.innerHTML = ``; const input = document.getElementById('plan-name-input'); input.focus(); input.select(); if (editBtn) editBtn.style.display = 'none'; let saved = false; const save = () => { if (saved) return; saved = true; const newName = input.value.trim() || currentName; updatePlan(id, { name: newName }); renderPlanList(); openPlan(id); }; input.addEventListener('keydown', e => { if (e.key === 'Enter') save(); if (e.key === 'Escape') { saved = true; display.innerHTML = `${escHtml(currentName)}`; if (editBtn) editBtn.style.display = ''; } }); input.addEventListener('blur', save); } // ── Open plan ───────────────────────────────────────────────────────────────── function openPlan(id) { activePlanId = id; renderPlanList(); renderPlanDetail(getPlan(id)); } // ── New plan form ───────────────────────────────────────────────────────────── function initNewPlanForm() { const btn = document.getElementById('planner-new-btn'); const form = document.getElementById('planner-new-form'); const input = document.getElementById('planner-new-name'); const save = document.getElementById('planner-new-save'); const cancel = document.getElementById('planner-new-cancel'); if (!btn) return; btn.addEventListener('click', () => { form.style.display = ''; input.value = ''; input.focus(); }); cancel.addEventListener('click', () => { form.style.display = 'none'; }); const doCreate = () => { const name = input.value.trim(); if (!name) { input.focus(); return; } const plan = createPlan(name); form.style.display = 'none'; renderPlanList(); openPlan(plan.id); }; save.addEventListener('click', doCreate); input.addEventListener('keydown', e => { if (e.key === 'Enter') doCreate(); if (e.key === 'Escape') form.style.display = 'none'; }); } // ── window.plannerTab (hooks for other tabs) ────────────────────────────────── window.plannerTab = { onTabOpen() { renderPlanList(); if (activePlanId) { openPlan(activePlanId); } else { renderPlanDetail(null); } }, importFromAnalysis(aoeEvents, _refEvents, _options) { // Schritt 3 — noch nicht implementiert console.log('[Planner] importFromAnalysis — not yet implemented', aoeEvents); } }; // ── Init ────────────────────────────────────────────────────────────────────── document.addEventListener('DOMContentLoaded', () => { initNewPlanForm(); renderPlanList(); renderPlanDetail(null); });