113 lines
5.3 KiB
HTML
113 lines
5.3 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Pattern Viewer</title>
|
||
<link rel="stylesheet" href="assets/style.css">
|
||
<style>
|
||
body { background: var(--bg); color: var(--text); padding: 16px; }
|
||
.viewer { max-width: 720px; margin: 0 auto; padding: 16px; background: var(--card-bg); border: 1px solid var(--border); border-radius: 14px; box-shadow: 0 10px 24px rgba(0,0,0,0.1); }
|
||
.viewer h1 { margin: 0 0 6px; }
|
||
.viewer h3 { margin: 12px 0 6px; }
|
||
.viewer pre { white-space: pre-wrap; background: var(--input-bg); padding: 10px; border-radius: 10px; border: 1px solid var(--border); }
|
||
.step-list { display: grid; gap: 10px; margin: 12px 0; }
|
||
.step-card { border: 1px solid var(--border); border-radius: 12px; padding: 12px; background: var(--card-bg); box-shadow: 0 4px 12px rgba(0,0,0,0.05); }
|
||
.step-title { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
|
||
.step-title input { margin-right: 8px; }
|
||
.step-rows { margin-top: 6px; }
|
||
.note-box { width: 100%; min-height: 80px; border-radius: 10px; border: 1px solid var(--border); background: var(--input-bg); color: var(--text); padding: 8px; }
|
||
.meta { display: grid; gap: 6px; margin: 10px 0; font-size: 0.95rem; }
|
||
.pill { display: inline-block; padding: 4px 8px; border-radius: 999px; background: var(--input-bg); border: 1px solid var(--border); font-size: 0.85rem; margin-right: 6px; margin-bottom: 4px; }
|
||
.top-actions { display: flex; justify-content: space-between; align-items: center; gap: 10px; }
|
||
.back-link { text-decoration: none; color: var(--text); font-weight: 700; }
|
||
.back-link:hover { color: var(--project-color); }
|
||
@media (max-width: 600px) { .viewer { padding: 12px; } }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="viewer">
|
||
<div class="top-actions">
|
||
<a class="back-link" href="./">← Back to app</a>
|
||
<span id="viewerStatus" class="pill">Progress saved locally</span>
|
||
</div>
|
||
<h1 id="pvTitle">Pattern</h1>
|
||
<div id="pvDesigner" class="meta"></div>
|
||
<div id="pvMaterials"></div>
|
||
<h3>Steps</h3>
|
||
<div id="pvSteps" class="step-list"></div>
|
||
<h3>Notes</h3>
|
||
<textarea id="pvNotes" class="note-box" placeholder="Your notes..." aria-label="Notes"></textarea>
|
||
</div>
|
||
<script>
|
||
const params = new URLSearchParams(location.search);
|
||
const data = params.get('data');
|
||
let patternDraft = null;
|
||
try {
|
||
if (!data) throw new Error('Missing pattern data');
|
||
const json = decodeURIComponent(escape(atob(data)));
|
||
const parsed = JSON.parse(json);
|
||
if (!parsed.patternDraft) throw new Error('Invalid payload');
|
||
patternDraft = parsed.patternDraft;
|
||
} catch (e) {
|
||
document.body.innerHTML = `<div style="padding:20px;">Failed to load pattern: ${e.message}</div>`;
|
||
throw e;
|
||
}
|
||
const storageKey = `pattern-viewer-${patternDraft.meta?.title || 'pattern'}`;
|
||
let progress = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
||
function saveProgress() {
|
||
localStorage.setItem(storageKey, JSON.stringify(progress));
|
||
}
|
||
function render() {
|
||
document.getElementById('pvTitle').textContent = patternDraft.meta?.title || 'Pattern';
|
||
document.getElementById('pvDesigner').textContent = patternDraft.meta?.designer || '';
|
||
const mats = patternDraft.materials || '';
|
||
document.getElementById('pvMaterials').innerHTML = mats ? `<h3>Materials</h3><pre>${mats}</pre>` : '';
|
||
const stepsEl = document.getElementById('pvSteps');
|
||
stepsEl.innerHTML = '';
|
||
(patternDraft.steps || []).forEach((step, idx) => {
|
||
const card = document.createElement('div');
|
||
card.className = 'step-card';
|
||
const rows = (step.rows || []).map((r,i) => `<div><input type="checkbox" data-step="${idx}" data-row="${i}" ${progress[idx]?.rows?.[i]?'checked':''}> Row ${i+1}: ${r}</div>`).join('');
|
||
card.innerHTML = `
|
||
<div class="step-title">
|
||
<div><strong>Step ${idx+1}</strong> ${step.title ? '– ' + step.title : ''}</div>
|
||
</div>
|
||
<div class="step-rows">${rows || '<em>No rows yet.</em>'}</div>
|
||
<div style="margin-top:6px;">
|
||
<label>Notes</label>
|
||
<textarea data-step="${idx}" class="note-box" placeholder="Notes for this step...">${progress[idx]?.note || ''}</textarea>
|
||
</div>
|
||
`;
|
||
stepsEl.appendChild(card);
|
||
});
|
||
const pvNotes = document.getElementById('pvNotes');
|
||
pvNotes.value = progress.globalNote || '';
|
||
}
|
||
document.addEventListener('change', (e) => {
|
||
if (e.target.matches('input[type="checkbox"][data-step]')) {
|
||
const s = Number(e.target.dataset.step);
|
||
const r = Number(e.target.dataset.row);
|
||
progress[s] = progress[s] || { rows: {} };
|
||
progress[s].rows = progress[s].rows || {};
|
||
progress[s].rows[r] = e.target.checked;
|
||
saveProgress();
|
||
}
|
||
});
|
||
document.addEventListener('input', (e) => {
|
||
if (e.target.matches('textarea[data-step]')) {
|
||
const s = Number(e.target.dataset.step);
|
||
progress[s] = progress[s] || { rows: {} };
|
||
progress[s].note = e.target.value;
|
||
saveProgress();
|
||
}
|
||
if (e.target.id === 'pvNotes') {
|
||
progress.globalNote = e.target.value;
|
||
saveProgress();
|
||
}
|
||
});
|
||
render();
|
||
</script>
|
||
</body>
|
||
</html>
|