cctd-web 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/index.html +72 -7
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -164,6 +164,20 @@ a{color:var(--accent);text-decoration:none}
|
|
|
164
164
|
</div>
|
|
165
165
|
|
|
166
166
|
<div class="filter-bar" id="filterBar">
|
|
167
|
+
<label>Story Status:</label>
|
|
168
|
+
<select id="filterStoryStatus">
|
|
169
|
+
<option value="">All</option>
|
|
170
|
+
<option value="active">Active (non-DONE)</option>
|
|
171
|
+
<option value="BACKLOG">BACKLOG</option>
|
|
172
|
+
<option value="DEFINED">DEFINED</option>
|
|
173
|
+
<option value="AI_READY">AI_READY</option>
|
|
174
|
+
<option value="IN_PROGRESS">IN_PROGRESS</option>
|
|
175
|
+
<option value="TESTING">TESTING</option>
|
|
176
|
+
<option value="REVIEW">REVIEW</option>
|
|
177
|
+
<option value="DONE">DONE</option>
|
|
178
|
+
</select>
|
|
179
|
+
<label>Story Label:</label>
|
|
180
|
+
<select id="filterStoryLabel"><option value="">All</option></select>
|
|
167
181
|
<label>Story:</label>
|
|
168
182
|
<select id="filterStory"><option value="">All</option></select>
|
|
169
183
|
<label>Priority:</label>
|
|
@@ -189,7 +203,7 @@ const STATUS_COLORS = {BACKLOG:'var(--backlog)',DEFINED:'var(--defined)',AI_READ
|
|
|
189
203
|
let data = {stories:[], tasks:[]};
|
|
190
204
|
let activityLog = [];
|
|
191
205
|
let view = 'kanban';
|
|
192
|
-
let filters = {story:'', priority:''};
|
|
206
|
+
let filters = {storyStatus:'', storyLabel:'', story:'', priority:''};
|
|
193
207
|
|
|
194
208
|
// Normalize status
|
|
195
209
|
function norm(s){return STATUS_ALIASES[s?.toUpperCase()] || s?.toUpperCase() || 'BACKLOG';}
|
|
@@ -229,6 +243,8 @@ document.querySelectorAll('.view-toggle button').forEach(btn => {
|
|
|
229
243
|
});
|
|
230
244
|
|
|
231
245
|
// Filters
|
|
246
|
+
document.getElementById('filterStoryStatus').addEventListener('change', e => {filters.storyStatus = e.target.value; render();});
|
|
247
|
+
document.getElementById('filterStoryLabel').addEventListener('change', e => {filters.storyLabel = e.target.value; render();});
|
|
232
248
|
document.getElementById('filterStory').addEventListener('change', e => {filters.story = e.target.value; render();});
|
|
233
249
|
document.getElementById('filterPriority').addEventListener('change', e => {filters.priority = e.target.value; render();});
|
|
234
250
|
|
|
@@ -320,9 +336,38 @@ function esc(s){
|
|
|
320
336
|
return d.innerHTML;
|
|
321
337
|
}
|
|
322
338
|
|
|
339
|
+
function getFilteredStories(){
|
|
340
|
+
let stories = data.stories;
|
|
341
|
+
if(filters.storyStatus){
|
|
342
|
+
if(filters.storyStatus === 'active'){
|
|
343
|
+
stories = stories.filter(s => norm(s.status) !== 'DONE');
|
|
344
|
+
} else {
|
|
345
|
+
stories = stories.filter(s => norm(s.status) === filters.storyStatus);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if(filters.storyLabel){
|
|
349
|
+
stories = stories.filter(s => (s.labels||[]).some(l => l.toLowerCase() === filters.storyLabel.toLowerCase()));
|
|
350
|
+
}
|
|
351
|
+
if(filters.story){
|
|
352
|
+
stories = stories.filter(s => s.id === filters.story);
|
|
353
|
+
}
|
|
354
|
+
return stories;
|
|
355
|
+
}
|
|
356
|
+
|
|
323
357
|
function getFiltered(){
|
|
358
|
+
const matchedStories = getFilteredStories();
|
|
359
|
+
const storyIds = new Set(matchedStories.map(s => s.id));
|
|
324
360
|
let tasks = data.tasks;
|
|
325
|
-
|
|
361
|
+
|
|
362
|
+
// If any story filter is active, restrict to matching stories' tasks
|
|
363
|
+
const hasStoryFilter = filters.storyStatus || filters.storyLabel || filters.story;
|
|
364
|
+
if(hasStoryFilter){
|
|
365
|
+
tasks = tasks.filter(t => {
|
|
366
|
+
const sid = t.story || (t.id?.includes('-') ? t.id.split('-')[0] : '');
|
|
367
|
+
return sid && storyIds.has(sid);
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
326
371
|
if(filters.priority) tasks = tasks.filter(t => (t.priority||'medium').toLowerCase() === filters.priority);
|
|
327
372
|
return tasks;
|
|
328
373
|
}
|
|
@@ -348,19 +393,35 @@ function updateStats(){
|
|
|
348
393
|
}
|
|
349
394
|
|
|
350
395
|
function updateStoryFilter(){
|
|
396
|
+
// Story dropdown — show only stories that pass status+label filters
|
|
397
|
+
const filtered = getFilteredStories();
|
|
351
398
|
const sel = document.getElementById('filterStory');
|
|
352
399
|
const cur = sel.value;
|
|
353
|
-
const opts =
|
|
400
|
+
const opts = filtered.map(s => `<option value="${esc(s.id)}">${esc(s.id)} - ${esc(s.title)}</option>`);
|
|
354
401
|
sel.innerHTML = `<option value="">All</option>` + opts.join('');
|
|
355
402
|
sel.value = cur;
|
|
403
|
+
|
|
404
|
+
// Story label dropdown — collect all unique labels
|
|
405
|
+
const labelSel = document.getElementById('filterStoryLabel');
|
|
406
|
+
const curLabel = labelSel.value;
|
|
407
|
+
const allLabels = new Set();
|
|
408
|
+
data.stories.forEach(s => (s.labels||[]).forEach(l => allLabels.add(l)));
|
|
409
|
+
const labelOpts = [...allLabels].sort().map(l => `<option value="${esc(l)}">${esc(l)}</option>`);
|
|
410
|
+
labelSel.innerHTML = `<option value="">All</option>` + labelOpts.join('');
|
|
411
|
+
labelSel.value = curLabel;
|
|
356
412
|
}
|
|
357
413
|
|
|
358
414
|
function renderKanban(){
|
|
359
415
|
const tasks = getFiltered();
|
|
360
416
|
const main = document.getElementById('main');
|
|
417
|
+
const hasStoryFilter = filters.storyStatus || filters.storyLabel || filters.story;
|
|
361
418
|
|
|
362
|
-
if(!tasks.length
|
|
363
|
-
|
|
419
|
+
if(!tasks.length){
|
|
420
|
+
if(!data.stories.length && !data.tasks.length){
|
|
421
|
+
main.innerHTML = `<div class="empty"><h2>No tasks found</h2><p>Create tasks with CCTD to see them here.</p><code>/cctd init</code></div>`;
|
|
422
|
+
} else {
|
|
423
|
+
main.innerHTML = `<div class="empty"><h2>No matching tasks</h2><p>Try adjusting your filters.</p></div>`;
|
|
424
|
+
}
|
|
364
425
|
return;
|
|
365
426
|
}
|
|
366
427
|
|
|
@@ -393,8 +454,12 @@ function renderList(){
|
|
|
393
454
|
const tasks = getFiltered();
|
|
394
455
|
const main = document.getElementById('main');
|
|
395
456
|
|
|
396
|
-
if(!tasks.length
|
|
397
|
-
|
|
457
|
+
if(!tasks.length){
|
|
458
|
+
if(!data.stories.length && !data.tasks.length){
|
|
459
|
+
main.innerHTML = `<div class="empty"><h2>No tasks found</h2><p>Create tasks with CCTD to see them here.</p><code>/cctd init</code></div>`;
|
|
460
|
+
} else {
|
|
461
|
+
main.innerHTML = `<div class="empty"><h2>No matching tasks</h2><p>Try adjusting your filters.</p></div>`;
|
|
462
|
+
}
|
|
398
463
|
return;
|
|
399
464
|
}
|
|
400
465
|
|