diff --git a/JournalAppPrototype/journal.html b/JournalAppPrototype/journal.html index e081616..a04d452 100644 --- a/JournalAppPrototype/journal.html +++ b/JournalAppPrototype/journal.html @@ -538,6 +538,7 @@ .side-panel-actions { display: flex; justify-content: flex-end; + gap: 8px; margin-top: 8px; } @@ -556,6 +557,7 @@ gap: 8px; padding: 6px 0; border-bottom: 1px solid var(--bg-accent); + align-items: flex-start; } .panel-entry:last-child { border-bottom: none; } @@ -573,6 +575,60 @@ color: var(--text-primary); line-height: 1.45; word-break: break-word; + flex: 1; + } + + .panel-entry-body textarea { + width: 100%; + background: var(--bg-accent); + border: 1px solid var(--accent); + border-radius: 4px; + color: var(--text-primary); + font-size: 0.85rem; + line-height: 1.45; + padding: 4px 6px; + resize: vertical; + min-height: 48px; + box-sizing: border-box; + } + + .panel-entry-actions { + display: flex; + gap: 4px; + flex-shrink: 0; + padding-top: 1px; + } + + .panel-entry-action-btn { + background: none; + border: none; + cursor: pointer; + padding: 2px 5px; + border-radius: 4px; + font-size: 0.9rem; + line-height: 1; + color: var(--text-secondary); + transition: background 0.15s, color 0.15s; + } + + .panel-entry-action-btn:hover { background: var(--bg-accent); color: var(--text-primary); } + .panel-entry-action-btn.del:hover { color: #e05252; } + + .panel-edit-mode-btn { + background: transparent; + border: 1px solid var(--text-secondary); + color: var(--text-secondary); + border-radius: 6px; + padding: 8px 14px; + font-size: 0.85rem; + cursor: pointer; + transition: border-color 0.15s, color 0.15s; + } + + .panel-edit-mode-btn:hover, + .panel-edit-mode-btn.active { + border-color: var(--accent); + color: var(--accent); } /* ── Breakout card in main timeline ─────────────────────── */ @@ -1481,6 +1537,7 @@
+
@@ -2068,6 +2125,10 @@ async function openProjectPanel(projectId, projectName) { activePanelProjectId = projectId; + panelEditMode = false; + const editBtn = document.getElementById('panelEditModeBtn'); + editBtn.textContent = 'Edit'; + editBtn.classList.remove('active'); document.getElementById('sidePanelTitle').textContent = '🔗 ' + projectName; document.getElementById('sidePanel').style.display = 'flex'; document.querySelector('main').classList.add('main--panel-open'); @@ -2079,8 +2140,14 @@ document.getElementById('sidePanel').style.display = 'none'; document.querySelector('main').classList.remove('main--panel-open'); activePanelProjectId = null; + panelEditMode = false; + const editBtn = document.getElementById('panelEditModeBtn'); + editBtn.textContent = 'Edit'; + editBtn.classList.remove('active'); } + let panelEditMode = false; + async function renderProjectTimeline(projectId) { const container = document.getElementById('sidePanelTimeline'); container.innerHTML = '
Loading…
'; @@ -2119,9 +2186,16 @@ const sourceLabel = e._source === 'timeline' ? `[journal]` : ''; - return `
+ const actions = (panelEditMode && e._source === 'panel') + ? `
+ + +
` + : ''; + return `
${time}${sourceLabel}
${mood}${escapeHtml(e.text)}${tags ? '
'+tags+'
' : ''}
+ ${actions}
`; }).join(''); return `
${label}
${rows}
`; @@ -2277,6 +2351,33 @@ }); } + function deleteProjectEntry(id) { + return new Promise((resolve, reject) => { + const tx = db.transaction(PROJECT_ENTRIES_STORE, 'readwrite'); + const store = tx.objectStore(PROJECT_ENTRIES_STORE); + const req = store.delete(id); + req.onsuccess = () => resolve(); + req.onerror = () => reject(req.error); + }); + } + + function updateProjectEntry(id, text) { + return new Promise((resolve, reject) => { + const tx = db.transaction(PROJECT_ENTRIES_STORE, 'readwrite'); + const store = tx.objectStore(PROJECT_ENTRIES_STORE); + const getReq = store.get(id); + getReq.onsuccess = () => { + const entry = getReq.result; + if (!entry) { reject(new Error('Entry not found')); return; } + entry.text = text; + const putReq = store.put(entry); + putReq.onsuccess = () => resolve(); + putReq.onerror = () => reject(putReq.error); + }; + getReq.onerror = () => reject(getReq.error); + }); + } + function getProjectEntries(projectId) { return new Promise((resolve, reject) => { const tx = db.transaction(PROJECT_ENTRIES_STORE, 'readonly'); @@ -2906,6 +3007,70 @@ if (e.key === 'Enter' && e.ctrlKey) { e.preventDefault(); if (dbReady) panelSubmit(); } }); + document.getElementById('panelEditModeBtn').addEventListener('click', () => { + panelEditMode = !panelEditMode; + const btn = document.getElementById('panelEditModeBtn'); + btn.textContent = panelEditMode ? 'Done' : 'Edit'; + btn.classList.toggle('active', panelEditMode); + if (activePanelProjectId != null) renderProjectTimeline(activePanelProjectId); + }); + + document.getElementById('sidePanelTimeline').addEventListener('click', async e => { + const actionBtn = e.target.closest('[data-action]'); + if (!actionBtn) return; + const entry = actionBtn.closest('.panel-entry'); + if (!entry || entry.dataset.source !== 'panel') return; + const id = parseInt(entry.dataset.id); + + if (actionBtn.dataset.action === 'delete') { + try { + await deleteProjectEntry(id); + await renderProjectTimeline(activePanelProjectId); + const countEl = document.querySelector(`.breakout-card[data-project-id="${activePanelProjectId}"] .breakout-card-count`); + if (countEl) { + const count = await getCombinedProjectEntryCount(activePanelProjectId); + countEl.textContent = `${count} ${count === 1 ? 'entry' : 'entries'}`; + } + showToast('Entry deleted'); + } catch (err) { + console.error('Failed to delete project entry:', err); + showToast('Error deleting entry'); + } + return; + } + + if (actionBtn.dataset.action === 'edit') { + const body = entry.querySelector('.panel-entry-body'); + const currentText = entry.dataset.text; + body.innerHTML = ``; + const textarea = body.querySelector('textarea'); + textarea.focus(); + textarea.setSelectionRange(textarea.value.length, textarea.value.length); + + const save = async () => { + const newText = textarea.value.trim(); + if (!newText || newText === currentText) { + await renderProjectTimeline(activePanelProjectId); + return; + } + try { + await updateProjectEntry(id, newText); + await renderProjectTimeline(activePanelProjectId); + showToast('Entry updated'); + } catch (err) { + console.error('Failed to update project entry:', err); + showToast('Error updating entry'); + } + }; + + textarea.addEventListener('blur', save, { once: true }); + textarea.addEventListener('keydown', ke => { + if (ke.key === 'Enter' && !ke.shiftKey) { ke.preventDefault(); textarea.removeEventListener('blur', save); save(); } + if (ke.key === 'Escape') { textarea.removeEventListener('blur', save); renderProjectTimeline(activePanelProjectId); } + }); + } + }); + init();