Skip to main content

max / makenotwork

4.8 KB · 102 lines History Blame Raw
1 {% include "wizards/partials/step_nav.html" %}
2
3 <div class="wizard-step">
4 <h2 class="subtitle-h2">Sections</h2>
5 <p class="step-description">Add tabbed content sections (e.g. Features, Installation, Specs). Sections are optional.</p>
6
7 <div id="sections-list">
8 {% for section in sections %}
9 <div class="section-row" data-id="{{ section.id }}">
10 <span class="section-row-title">{{ section.title }}</span>
11 <span class="section-row-length">{{ section.body.chars().count() }} chars</span>
12 <button type="button" class="btn-secondary section-delete-btn" data-id="{{ section.id }}">Delete</button>
13 </div>
14 {% endfor %}
15 </div>
16
17 <details class="section-add-details">
18 <summary>Add Section</summary>
19 <div class="section-add-body">
20 <div class="form-group">
21 <label for="new-section-title">Title</label>
22 <input type="text" id="new-section-title" placeholder="e.g. Features, Installation..." autocomplete="off">
23 </div>
24 <div class="form-group">
25 <label for="new-section-body">Body (Markdown)</label>
26 <textarea id="new-section-body" rows="6" placeholder="Section content..."></textarea>
27 </div>
28 <button type="button" class="btn-secondary" id="add-section-btn">Add Section</button>
29 <span id="section-add-status" class="field-status ml-2"></span>
30 </div>
31 </details>
32
33 <form hx-post="/dashboard/project/{{ project_slug }}/new-item/{{ item_id }}/step/sections"
34 hx-target="#wizard-step" hx-swap="innerHTML"
35 hx-push-url="/dashboard/project/{{ project_slug }}/new-item/{{ item_id }}/step/pricing">
36 <div class="wizard-actions">
37 <button type="button" class="btn-secondary"
38 hx-get="/dashboard/project/{{ project_slug }}/new-item/{{ item_id }}/step/content"
39 hx-target="#wizard-step" hx-swap="innerHTML"
40 hx-push-url="/dashboard/project/{{ project_slug }}/new-item/{{ item_id }}/step/content">Back</button>
41 <button type="submit" class="btn-primary">Continue</button>
42 </div>
43 </form>
44 </div>
45
46 <script>
47 (function() {
48 var itemId = '{{ item_id }}';
49
50 document.getElementById('add-section-btn').addEventListener('click', function() {
51 var title = document.getElementById('new-section-title').value.trim();
52 var body = document.getElementById('new-section-body').value;
53 var status = document.getElementById('section-add-status');
54 if (!title) { status.textContent = 'Title is required'; return; }
55 this.disabled = true;
56 status.textContent = '';
57
58 fetch('/api/items/' + itemId + '/sections', {
59 method: 'POST',
60 headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
61 body: JSON.stringify({ title: title, body: body })
62 })
63 .then(function(res) {
64 if (!res.ok) return res.json().then(function(d) { throw new Error(d.error || 'Failed'); });
65 return res.json();
66 })
67 .then(function(sec) {
68 var row = document.createElement('div');
69 row.className = 'section-row';
70 row.dataset.id = sec.id;
71 row.innerHTML =
72 '<span class="section-row-title"></span>' +
73 '<span class="section-row-length"></span>' +
74 '<button type="button" class="btn-secondary section-delete-btn"></button>';
75 row.querySelector('.section-row-title').textContent = sec.title;
76 row.querySelector('.section-row-length').textContent = (sec.body || '').length + ' chars';
77 var delBtn = row.querySelector('.section-delete-btn');
78 delBtn.dataset.id = sec.id;
79 delBtn.textContent = 'Delete';
80 document.getElementById('sections-list').appendChild(row);
81 attachDeleteHandler(delBtn);
82 document.getElementById('new-section-title').value = '';
83 document.getElementById('new-section-body').value = '';
84 })
85 .catch(function(err) { status.textContent = err.message; })
86 .finally(function() { document.getElementById('add-section-btn').disabled = false; });
87 });
88
89 function attachDeleteHandler(btn) {
90 btn.addEventListener('click', function() {
91 var id = this.dataset.id;
92 var row = this.closest('.section-row');
93 fetch('/api/sections/' + id, { method: 'DELETE', headers: csrfHeaders() })
94 .then(function(res) { if (res.ok) row.remove(); else showToast('Failed to delete'); })
95 .catch(function() { showToast('Failed to delete'); });
96 });
97 }
98
99 document.querySelectorAll('.section-delete-btn').forEach(attachDeleteHandler);
100 })();
101 </script>
102