@unbrained/pm-web 2026.8.7 → 2026.8.14
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/CHANGELOG.md +16 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.js +16 -0
- package/dist/app.js.map +1 -1
- package/dist/auth.d.ts +5 -0
- package/dist/auth.js.map +1 -1
- package/dist/board.d.ts +53 -0
- package/dist/board.js +48 -0
- package/dist/board.js.map +1 -1
- package/dist/crypto.d.ts +23 -0
- package/dist/crypto.js +32 -0
- package/dist/crypto.js.map +1 -1
- package/dist/db.d.ts +19 -0
- package/dist/db.js +19 -0
- package/dist/db.js.map +1 -1
- package/dist/ical.d.ts +72 -0
- package/dist/ical.js +73 -0
- package/dist/ical.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +52 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts +8 -0
- package/dist/middleware/auth.js.map +1 -1
- package/dist/oidc.d.ts +87 -0
- package/dist/oidc.js +113 -0
- package/dist/oidc.js.map +1 -1
- package/dist/routes/admin.js +15 -0
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/github.js +13 -0
- package/dist/routes/github.js.map +1 -1
- package/dist/routes/oidc.d.ts +10 -0
- package/dist/routes/oidc.js +51 -0
- package/dist/routes/oidc.js.map +1 -1
- package/dist/routes/pm.js +132 -1
- package/dist/routes/pm.js.map +1 -1
- package/dist/routes/sharing.js +8 -0
- package/dist/routes/sharing.js.map +1 -1
- package/dist/services/mutation-event-watcher.js +13 -0
- package/dist/services/mutation-event-watcher.js.map +1 -1
- package/dist/services/pm-runner.d.ts +82 -0
- package/dist/services/pm-runner.js +121 -0
- package/dist/services/pm-runner.js.map +1 -1
- package/dist/services/project-watcher.d.ts +22 -0
- package/dist/services/project-watcher.js +51 -0
- package/dist/services/project-watcher.js.map +1 -1
- package/dist/services/realtime-bus.d.ts +46 -0
- package/dist/services/realtime-bus.js +59 -0
- package/dist/services/realtime-bus.js.map +1 -1
- package/dist/services/sse.d.ts +134 -0
- package/dist/services/sse.js +145 -0
- package/dist/services/sse.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +5 -3
- package/public/src/app.js +44 -1
- package/public/src/app.js.map +1 -1
- package/public/src/app.ts +44 -1
- package/public/src/components/modals.js +11 -0
- package/public/src/components/modals.js.map +1 -1
- package/public/src/components/modals.ts +11 -0
- package/public/src/components/toast.js +2 -0
- package/public/src/components/toast.js.map +1 -1
- package/public/src/components/toast.ts +2 -0
- package/public/src/constants.js +21 -4
- package/public/src/constants.js.map +1 -1
- package/public/src/constants.ts +21 -4
- package/public/src/filters.js +10 -7
- package/public/src/filters.js.map +1 -1
- package/public/src/filters.ts +10 -7
- package/public/src/i18n.js +11 -1
- package/public/src/i18n.js.map +1 -1
- package/public/src/i18n.ts +13 -1
- package/public/src/state.js +3 -0
- package/public/src/state.js.map +1 -1
- package/public/src/state.ts +3 -0
- package/public/src/sw.ts +71 -7
- package/public/src/theme.js +13 -1
- package/public/src/theme.js.map +1 -1
- package/public/src/theme.ts +15 -1
- package/public/src/types.ts +2 -0
- package/public/src/utils.js +26 -1
- package/public/src/utils.js.map +1 -1
- package/public/src/utils.ts +26 -1
- package/public/src/views/activity.js +1 -0
- package/public/src/views/activity.js.map +1 -1
- package/public/src/views/activity.ts +1 -0
- package/public/src/views/admin.js +134 -0
- package/public/src/views/admin.js.map +1 -1
- package/public/src/views/admin.ts +134 -0
- package/public/src/views/auth.js +31 -0
- package/public/src/views/auth.js.map +1 -1
- package/public/src/views/auth.ts +31 -0
- package/public/src/views/calendar.js +3 -0
- package/public/src/views/calendar.js.map +1 -1
- package/public/src/views/calendar.ts +3 -0
- package/public/src/views/comments-audit.js +1 -0
- package/public/src/views/comments-audit.js.map +1 -1
- package/public/src/views/comments-audit.ts +1 -0
- package/public/src/views/config.js +67 -0
- package/public/src/views/config.js.map +1 -1
- package/public/src/views/config.ts +67 -0
- package/public/src/views/context.js +2 -0
- package/public/src/views/context.js.map +1 -1
- package/public/src/views/context.ts +2 -0
- package/public/src/views/create.js +3 -0
- package/public/src/views/create.js.map +1 -1
- package/public/src/views/create.ts +3 -0
- package/public/src/views/dedupe.js +1 -0
- package/public/src/views/dedupe.js.map +1 -1
- package/public/src/views/dedupe.ts +1 -0
- package/public/src/views/export.js +6 -0
- package/public/src/views/export.js.map +1 -1
- package/public/src/views/export.ts +6 -0
- package/public/src/views/github.js +58 -0
- package/public/src/views/github.js.map +1 -1
- package/public/src/views/github.ts +58 -0
- package/public/src/views/graph-canvas.js +18 -0
- package/public/src/views/graph-canvas.js.map +1 -1
- package/public/src/views/graph-canvas.ts +20 -0
- package/public/src/views/graph.js +41 -0
- package/public/src/views/graph.js.map +1 -1
- package/public/src/views/graph.ts +42 -1
- package/public/src/views/groups.js +45 -0
- package/public/src/views/groups.js.map +1 -1
- package/public/src/views/groups.ts +45 -0
- package/public/src/views/guide.js +3 -0
- package/public/src/views/guide.js.map +1 -1
- package/public/src/views/guide.ts +3 -0
- package/public/src/views/health.js +3 -0
- package/public/src/views/health.js.map +1 -1
- package/public/src/views/health.ts +3 -0
- package/public/src/views/items.js +196 -6
- package/public/src/views/items.js.map +1 -1
- package/public/src/views/items.ts +196 -6
- package/public/src/views/normalize.js +2 -0
- package/public/src/views/normalize.js.map +1 -1
- package/public/src/views/normalize.ts +2 -0
- package/public/src/views/packages.js +41 -0
- package/public/src/views/packages.js.map +1 -1
- package/public/src/views/packages.ts +41 -0
- package/public/src/views/plan-execution.js +6 -0
- package/public/src/views/plan-execution.js.map +1 -1
- package/public/src/views/plan-execution.ts +8 -0
- package/public/src/views/plan.js +145 -0
- package/public/src/views/plan.js.map +1 -1
- package/public/src/views/plan.ts +146 -1
- package/public/src/views/projects.js +60 -0
- package/public/src/views/projects.js.map +1 -1
- package/public/src/views/projects.ts +60 -0
- package/public/src/views/router.js +43 -0
- package/public/src/views/router.js.map +1 -1
- package/public/src/views/router.ts +43 -0
- package/public/src/views/search.js +5 -0
- package/public/src/views/search.js.map +1 -1
- package/public/src/views/search.ts +5 -0
- package/public/src/views/settings.js +36 -0
- package/public/src/views/settings.js.map +1 -1
- package/public/src/views/settings.ts +36 -0
- package/public/src/views/shared.js +1 -0
- package/public/src/views/shared.js.map +1 -1
- package/public/src/views/shared.ts +1 -0
- package/public/src/views/sharing.js +32 -0
- package/public/src/views/sharing.js.map +1 -1
- package/public/src/views/sharing.ts +32 -0
- package/public/src/views/stats.js +1 -0
- package/public/src/views/stats.js.map +1 -1
- package/public/src/views/stats.ts +1 -0
- package/public/src/views/templates.js +4 -0
- package/public/src/views/templates.js.map +1 -1
- package/public/src/views/templates.ts +4 -0
- package/public/src/views/validate.js +1 -0
- package/public/src/views/validate.js.map +1 -1
- package/public/src/views/validate.ts +1 -0
- package/public/sw.js +60 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"health.js","sourceRoot":"","sources":["health.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["health.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@ function isHistoryDriftIssue(issue: HealthIssue): boolean {
|
|
|
20
20
|
return msg.includes('history') && (msg.includes('drift') || msg.includes('repair') || msg.includes('mismatch'));
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
/** Renders the markup for one health issue, appending Repair and Dry Run buttons when the issue is history drift and a project item id is detected in the message. */
|
|
23
24
|
function renderIssueRow(issue: HealthIssue, projectId: string): string {
|
|
24
25
|
const msg = escHtml(issue.message || issue.description || String(issue));
|
|
25
26
|
if (isHistoryDriftIssue(issue)) {
|
|
@@ -34,6 +35,7 @@ function renderIssueRow(issue: HealthIssue, projectId: string): string {
|
|
|
34
35
|
return `<div class="health-issue-item">⚠ ${msg}</div>`;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/** Runs item history repair (or a dry-run preview) via the project's history-repair endpoint and toasts a summary of entries scanned, rehashed, and patched, including verification failures. */
|
|
37
39
|
export async function repairItemHistory(projectId: string, itemId: string, dryRun: boolean): Promise<void> {
|
|
38
40
|
try {
|
|
39
41
|
const data = await api<HistoryRepairResponse>('POST', `/projects/${projectId}/pm/items/${encodeURIComponent(itemId)}/history-repair`, { dryRun });
|
|
@@ -63,6 +65,7 @@ export async function repairItemHistory(projectId: string, itemId: string, dryRu
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
/** Renders the project health view: a numeric health score card colored by threshold and a list of issues (each rendered with repair actions when applicable), plus an optional summary. */
|
|
66
69
|
export async function renderHealthView(): Promise<void> {
|
|
67
70
|
const el = document.getElementById('content-health');
|
|
68
71
|
if (!el) return;
|
|
@@ -16,9 +16,11 @@ import { EMPTY_FILTERS, filtersToQueryString, filtersFromSearchParams, hasActive
|
|
|
16
16
|
// alive across the Preview → Apply call pair and is GC-friendly.
|
|
17
17
|
const bulkUpdatePayloads = new WeakMap();
|
|
18
18
|
const bulkClosePayloads = new WeakMap();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Mirrors the current item filters into the page URL query string when the
|
|
21
|
+
* items view is active, using history.replaceState so the filtered view is
|
|
22
|
+
* shareable without adding back/forward entries. Does nothing on other views.
|
|
23
|
+
*/
|
|
22
24
|
function syncFiltersToUrl() {
|
|
23
25
|
if (state.currentView !== 'items')
|
|
24
26
|
return;
|
|
@@ -29,8 +31,12 @@ function syncFiltersToUrl() {
|
|
|
29
31
|
}
|
|
30
32
|
catch { /* ignore */ }
|
|
31
33
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Reads item filters from the current URL query string into state. Only
|
|
36
|
+
* replaces the in-memory filters when the URL actually carries filter
|
|
37
|
+
* parameters, so navigating to /items without a query string preserves the
|
|
38
|
+
* existing filters.
|
|
39
|
+
*/
|
|
34
40
|
export function loadFiltersFromUrl() {
|
|
35
41
|
const params = new URLSearchParams(window.location.search);
|
|
36
42
|
// Only override when the URL actually carries filter params, so navigating
|
|
@@ -47,6 +53,12 @@ const DEP_REL_OPTIONS = [
|
|
|
47
53
|
{ value: 'child', label: 'Child' },
|
|
48
54
|
{ value: 'related', label: 'Related' },
|
|
49
55
|
];
|
|
56
|
+
/**
|
|
57
|
+
* Normalizes a raw dependency relationship label to a canonical value by
|
|
58
|
+
* trimming, lowercasing, and converting dashes to underscores, then applying a
|
|
59
|
+
* known-alias map (e.g. blockedby → blocked_by). Returns the alias when one
|
|
60
|
+
* exists, otherwise the normalized input unchanged.
|
|
61
|
+
*/
|
|
50
62
|
function normalizeDepRelation(raw) {
|
|
51
63
|
const aliases = {
|
|
52
64
|
blockedby: 'blocked_by',
|
|
@@ -67,12 +79,22 @@ function normalizeDepRelation(raw) {
|
|
|
67
79
|
const normalized = (raw || '').trim().toLowerCase().replace(/-/g, '_');
|
|
68
80
|
return aliases[normalized] ?? normalized;
|
|
69
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Builds the HTML <option> elements for a dependency relationship dropdown from
|
|
84
|
+
* the fixed DEP_REL_OPTIONS list, marking the option that matches the
|
|
85
|
+
* normalized selected value as selected.
|
|
86
|
+
*/
|
|
70
87
|
function renderDependencyOptions(selected) {
|
|
71
88
|
const current = normalizeDepRelation(selected);
|
|
72
89
|
return DEP_REL_OPTIONS
|
|
73
90
|
.map((option) => `<option value="${option.value}"${option.value === current ? ' selected' : ''}>${option.label}</option>`)
|
|
74
91
|
.join('');
|
|
75
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Returns a human-readable display label for a dependency relationship by
|
|
95
|
+
* looking up its normalized value in a fixed label map, falling back to the
|
|
96
|
+
* original relationship string when no label is defined.
|
|
97
|
+
*/
|
|
76
98
|
function depLabel(rel) {
|
|
77
99
|
const labels = {
|
|
78
100
|
blocked_by: 'Blocked by',
|
|
@@ -92,6 +114,11 @@ function depRelation(dep) {
|
|
|
92
114
|
// ═══════════════════════════════════════════════════════════════
|
|
93
115
|
// BULK UPDATE
|
|
94
116
|
// ═══════════════════════════════════════════════════════════════
|
|
117
|
+
/**
|
|
118
|
+
* Opens the Bulk Update Items modal, which lets the user filter items and
|
|
119
|
+
* choose fields to change across many items at once. Shows an info toast and
|
|
120
|
+
* returns early when no project is currently selected.
|
|
121
|
+
*/
|
|
95
122
|
export function showBulkUpdateModal() {
|
|
96
123
|
if (!state.currentProject) {
|
|
97
124
|
toast('Select a project first', 'info');
|
|
@@ -166,6 +193,12 @@ export function showBulkUpdateModal() {
|
|
|
166
193
|
</div>`, '');
|
|
167
194
|
showModal('bulk-update-modal');
|
|
168
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Runs a dry-run bulk update and renders a preview of the matched items and
|
|
198
|
+
* their field changes inside the modal. Requires at least one field to update,
|
|
199
|
+
* and stashes the actual (non-dry-run) payload on the Apply button so
|
|
200
|
+
* applyBulkUpdate can run without re-reading the form.
|
|
201
|
+
*/
|
|
169
202
|
export async function previewBulkUpdate() {
|
|
170
203
|
const pid = state.currentProject?.id;
|
|
171
204
|
if (!pid)
|
|
@@ -255,6 +288,12 @@ export async function previewBulkUpdate() {
|
|
|
255
288
|
previewEl.innerHTML = `<div style="color:var(--status-open);font-size:13px">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div>`;
|
|
256
289
|
}
|
|
257
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Applies the bulk update payload stashed on the Apply button by
|
|
293
|
+
* previewBulkUpdate, posting to the update-many endpoint. Reports the updated
|
|
294
|
+
* and failed counts via toast, closes the bulk update modal, and refreshes the
|
|
295
|
+
* items list and project badge.
|
|
296
|
+
*/
|
|
258
297
|
export async function applyBulkUpdate() {
|
|
259
298
|
const pid = state.currentProject?.id;
|
|
260
299
|
if (!pid)
|
|
@@ -295,6 +334,11 @@ export async function applyBulkUpdate() {
|
|
|
295
334
|
// ═══════════════════════════════════════════════════════════════
|
|
296
335
|
// BULK CLOSE
|
|
297
336
|
// ═══════════════════════════════════════════════════════════════
|
|
337
|
+
/**
|
|
338
|
+
* Opens the Bulk Close Items modal, which lets the user filter items and close
|
|
339
|
+
* or cancel many of them at once with a required reason. Shows an info toast
|
|
340
|
+
* and returns early when no project is currently selected.
|
|
341
|
+
*/
|
|
298
342
|
export function showBulkCloseModal() {
|
|
299
343
|
if (!state.currentProject) {
|
|
300
344
|
toast('Select a project first', 'info');
|
|
@@ -352,6 +396,12 @@ export function showBulkCloseModal() {
|
|
|
352
396
|
</div>`, '');
|
|
353
397
|
showModal('bulk-close-modal');
|
|
354
398
|
}
|
|
399
|
+
/**
|
|
400
|
+
* Previews the items that would be closed or canceled by running an
|
|
401
|
+
* update-many dry-run, and renders the matched count and first few items
|
|
402
|
+
* inside the modal. Requires a close reason, and stashes the close payload on
|
|
403
|
+
* the Apply button for applyBulkClose to use.
|
|
404
|
+
*/
|
|
355
405
|
export async function previewBulkClose() {
|
|
356
406
|
const pid = state.currentProject?.id;
|
|
357
407
|
if (!pid)
|
|
@@ -421,6 +471,13 @@ export async function previewBulkClose() {
|
|
|
421
471
|
previewEl.innerHTML = `<div style="color:var(--status-blocked);font-size:13px">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div>`;
|
|
422
472
|
}
|
|
423
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Applies the bulk close payload stashed on the Apply button by
|
|
476
|
+
* previewBulkClose, posting to the close-many endpoint which closes or cancels
|
|
477
|
+
* each matched item with the chosen reason. Reports the closed and failed
|
|
478
|
+
* counts via toast, closes the modal, and refreshes the items list and project
|
|
479
|
+
* badge.
|
|
480
|
+
*/
|
|
424
481
|
export async function applyBulkClose() {
|
|
425
482
|
const pid = state.currentProject?.id;
|
|
426
483
|
if (!pid)
|
|
@@ -472,6 +529,12 @@ export async function applyBulkClose() {
|
|
|
472
529
|
}
|
|
473
530
|
}
|
|
474
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* Renders the Items view into the page: a header with refresh and bulk
|
|
534
|
+
* actions, the status filter tabs, the multi-field filter bar, and a loading
|
|
535
|
+
* list placeholder, then fetches and renders the items. Shows an empty state
|
|
536
|
+
* and returns when no project is selected.
|
|
537
|
+
*/
|
|
475
538
|
export async function renderItemsView() {
|
|
476
539
|
const el = document.getElementById('content-items');
|
|
477
540
|
if (!el)
|
|
@@ -521,6 +584,12 @@ export async function renderItemsView() {
|
|
|
521
584
|
<div id="items-list"><div class="loading-state"><div class="loading-spinner"></div></div></div>`;
|
|
522
585
|
await fetchAndRenderItems();
|
|
523
586
|
}
|
|
587
|
+
/**
|
|
588
|
+
* Fetches items for the current project using the active filters (capped at
|
|
589
|
+
* 200, with tags filtered client-side because the list endpoint has no tag
|
|
590
|
+
* flag), stores them in state, updates the count subtitle, and renders the
|
|
591
|
+
* list. Shows an error state in the list element if the request fails.
|
|
592
|
+
*/
|
|
524
593
|
export async function fetchAndRenderItems() {
|
|
525
594
|
const pid = state.currentProject?.id;
|
|
526
595
|
if (!pid)
|
|
@@ -561,6 +630,10 @@ export async function fetchAndRenderItems() {
|
|
|
561
630
|
listEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Failed to load items: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
562
631
|
}
|
|
563
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Renders the items list element from state.items, showing an empty-state
|
|
635
|
+
* placeholder when there are no items or the mapped item rows otherwise.
|
|
636
|
+
*/
|
|
564
637
|
function renderItemsList() {
|
|
565
638
|
const el = document.getElementById('items-list');
|
|
566
639
|
if (!el)
|
|
@@ -575,6 +648,11 @@ function renderItemsList() {
|
|
|
575
648
|
}
|
|
576
649
|
el.innerHTML = `<div class="item-list">${state.items.map(item => renderItemRow(item)).join('')}</div>`;
|
|
577
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* Returns the HTML markup for a single items-list row: a clickable card
|
|
653
|
+
* showing the item's type icon, id, title, tags, priority dot, and status
|
|
654
|
+
* badge that opens the item detail modal when clicked.
|
|
655
|
+
*/
|
|
578
656
|
export function renderItemRow(item) {
|
|
579
657
|
const tags = (item.tags || []).map(t => `<span class="tag">${escHtml(t)}</span>`).join('');
|
|
580
658
|
return `<div class="item-row" onclick="window.__app.openItemDetail('${escHtml(item.id)}')">
|
|
@@ -588,6 +666,11 @@ export function renderItemRow(item) {
|
|
|
588
666
|
</div>
|
|
589
667
|
</div>`;
|
|
590
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Reads the current values from every items filter-bar control into
|
|
671
|
+
* state.itemFilters, mirrors them to the URL, and re-fetches and renders the
|
|
672
|
+
* list.
|
|
673
|
+
*/
|
|
591
674
|
export function applyItemFilters() {
|
|
592
675
|
const fs = document.getElementById('filter-status');
|
|
593
676
|
const ft = document.getElementById('filter-type');
|
|
@@ -606,6 +689,10 @@ export function applyItemFilters() {
|
|
|
606
689
|
syncFiltersToUrl();
|
|
607
690
|
fetchAndRenderItems();
|
|
608
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Resets the item filters to empty, clears every filter-bar control in the
|
|
694
|
+
* DOM, mirrors the cleared filters to the URL, and re-renders the items list.
|
|
695
|
+
*/
|
|
609
696
|
export function clearFilters() {
|
|
610
697
|
state.itemFilters = { ...EMPTY_FILTERS };
|
|
611
698
|
const ids = ['filter-status', 'filter-type', 'filter-priority', 'filter-sprint', 'filter-release', 'filter-assignee', 'filter-tag'];
|
|
@@ -617,7 +704,11 @@ export function clearFilters() {
|
|
|
617
704
|
syncFiltersToUrl();
|
|
618
705
|
fetchAndRenderItems();
|
|
619
706
|
}
|
|
620
|
-
|
|
707
|
+
/**
|
|
708
|
+
* Builds a shareable URL for the current filtered items view and copies it to
|
|
709
|
+
* the clipboard, toasting success or, if the clipboard write fails, toasting
|
|
710
|
+
* the URL itself for manual copy.
|
|
711
|
+
*/
|
|
621
712
|
export async function copyFilterLink() {
|
|
622
713
|
const qs = filtersToQueryString(state.itemFilters);
|
|
623
714
|
const url = `${window.location.origin}/items${qs ? `?${qs}` : ''}`;
|
|
@@ -629,6 +720,10 @@ export async function copyFilterLink() {
|
|
|
629
720
|
toast(url, 'info');
|
|
630
721
|
}
|
|
631
722
|
}
|
|
723
|
+
/**
|
|
724
|
+
* Sets the status filter to the provided value, mirrors it to the URL, and
|
|
725
|
+
* re-renders the entire items view.
|
|
726
|
+
*/
|
|
632
727
|
export function setStatusFilter(status) {
|
|
633
728
|
state.itemFilters.status = status;
|
|
634
729
|
syncFiltersToUrl();
|
|
@@ -637,6 +732,12 @@ export function setStatusFilter(status) {
|
|
|
637
732
|
// ═══════════════════════════════════════════════════════════════
|
|
638
733
|
// ITEM DETAIL MODAL
|
|
639
734
|
// ═══════════════════════════════════════════════════════════════
|
|
735
|
+
/**
|
|
736
|
+
* Opens the item detail modal and renders it. Fetches the item together with
|
|
737
|
+
* its comments, history, dependencies, learnings, notes, tests, and linked
|
|
738
|
+
* files in parallel (each detail call falls back to empty on failure) and
|
|
739
|
+
* builds the full tabbed detail view.
|
|
740
|
+
*/
|
|
640
741
|
export async function openItemDetail(itemId) {
|
|
641
742
|
const pid = state.currentProject?.id;
|
|
642
743
|
if (!pid)
|
|
@@ -969,6 +1070,11 @@ export async function openItemDetail(itemId) {
|
|
|
969
1070
|
bodyEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Failed to load: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
970
1071
|
}
|
|
971
1072
|
}
|
|
1073
|
+
/**
|
|
1074
|
+
* Formats a timestamp as a human-readable relative age string (just now,
|
|
1075
|
+
* Nm/Nh/Nd ago) for recent times, or a localized date for anything older than
|
|
1076
|
+
* a week. Returns an empty string when the timestamp is missing.
|
|
1077
|
+
*/
|
|
972
1078
|
function relTime(ts) {
|
|
973
1079
|
if (!ts)
|
|
974
1080
|
return '';
|
|
@@ -990,6 +1096,12 @@ function fmtDate(ts) {
|
|
|
990
1096
|
return '';
|
|
991
1097
|
return new Date(ts).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
992
1098
|
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Switches the item detail modal to the selected tab: toggles the active class
|
|
1101
|
+
* on the tabs, shows the target tab panel while hiding the others, tears down
|
|
1102
|
+
* the local graph when leaving the graph tab, and initializes the graph along
|
|
1103
|
+
* with its depth-slider listener when entering the graph tab with a nodeId.
|
|
1104
|
+
*/
|
|
993
1105
|
export function switchDetailTab(tabEl, targetId, nodeId) {
|
|
994
1106
|
const allTabs = tabEl.parentElement?.querySelectorAll('.tab');
|
|
995
1107
|
allTabs?.forEach(t => t.classList.remove('active'));
|
|
@@ -1017,6 +1129,11 @@ export function switchDetailTab(tabEl, targetId, nodeId) {
|
|
|
1017
1129
|
});
|
|
1018
1130
|
}
|
|
1019
1131
|
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Posts the comment typed into the detail modal's comment box to the item,
|
|
1134
|
+
* requiring non-empty text. On success it clears the box, toasts confirmation,
|
|
1135
|
+
* and reopens the item detail to refresh the comments tab.
|
|
1136
|
+
*/
|
|
1020
1137
|
export async function addComment(itemId) {
|
|
1021
1138
|
const el = document.getElementById('new-comment');
|
|
1022
1139
|
if (!el)
|
|
@@ -1036,6 +1153,10 @@ export async function addComment(itemId) {
|
|
|
1036
1153
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1037
1154
|
}
|
|
1038
1155
|
}
|
|
1156
|
+
/**
|
|
1157
|
+
* Posts the note typed into the detail modal's note box to the item, requiring
|
|
1158
|
+
* non-empty text. On success it clears the box and toasts confirmation.
|
|
1159
|
+
*/
|
|
1039
1160
|
export async function addNote(itemId) {
|
|
1040
1161
|
const el = document.getElementById('new-note');
|
|
1041
1162
|
if (!el)
|
|
@@ -1054,6 +1175,11 @@ export async function addNote(itemId) {
|
|
|
1054
1175
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1055
1176
|
}
|
|
1056
1177
|
}
|
|
1178
|
+
/**
|
|
1179
|
+
* Appends the text from the detail modal's append box to the item's
|
|
1180
|
+
* description, requiring non-empty text. On success it clears the box and
|
|
1181
|
+
* toasts confirmation.
|
|
1182
|
+
*/
|
|
1057
1183
|
export async function appendItem(itemId) {
|
|
1058
1184
|
const el = document.getElementById('new-append');
|
|
1059
1185
|
if (!el)
|
|
@@ -1072,6 +1198,11 @@ export async function appendItem(itemId) {
|
|
|
1072
1198
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1073
1199
|
}
|
|
1074
1200
|
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Saves the item detail's Update form: reads every edit field, requires a
|
|
1203
|
+
* title, and PATCHes the changed fields to the backend. On success it reopens
|
|
1204
|
+
* the item detail and refreshes the items list and project badge.
|
|
1205
|
+
*/
|
|
1075
1206
|
export async function updateItem(itemId) {
|
|
1076
1207
|
const titleEl = document.getElementById('edit-title');
|
|
1077
1208
|
const statusEl = document.getElementById('edit-status');
|
|
@@ -1132,6 +1263,12 @@ export async function updateItem(itemId) {
|
|
|
1132
1263
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1133
1264
|
}
|
|
1134
1265
|
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Closes or cancels the item with a required reason. For canceled it first
|
|
1268
|
+
* sets the status to canceled via the update endpoint before posting the close
|
|
1269
|
+
* reason; for closed it posts the close reason directly. Reports the outcome,
|
|
1270
|
+
* hides the detail modal, and refreshes the items list and project badge.
|
|
1271
|
+
*/
|
|
1135
1272
|
export async function closeItem(itemId, targetStatus) {
|
|
1136
1273
|
const reasonEl = document.getElementById('close-reason');
|
|
1137
1274
|
const reason = reasonEl?.value?.trim();
|
|
@@ -1157,6 +1294,10 @@ export async function closeItem(itemId, targetStatus) {
|
|
|
1157
1294
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1158
1295
|
}
|
|
1159
1296
|
}
|
|
1297
|
+
/**
|
|
1298
|
+
* Shows a confirmation dialog and, on confirm, DELETEs the item, hides the
|
|
1299
|
+
* detail modal, and refreshes the items list and project badge.
|
|
1300
|
+
*/
|
|
1160
1301
|
export function confirmDeleteItem(itemId) {
|
|
1161
1302
|
confirmDialog('Delete Item?', 'This action cannot be undone. The item and all its data will be permanently removed.', async () => {
|
|
1162
1303
|
try {
|
|
@@ -1175,6 +1316,12 @@ export function confirmDeleteItem(itemId) {
|
|
|
1175
1316
|
// ═══════════════════════════════════════════════════════════════
|
|
1176
1317
|
// CLAIM / RELEASE / START / PAUSE
|
|
1177
1318
|
// ═══════════════════════════════════════════════════════════════
|
|
1319
|
+
/**
|
|
1320
|
+
* Claims the item for the current user via the claim endpoint, dimming the
|
|
1321
|
+
* matching list row optimistically. On success it reopens the detail and
|
|
1322
|
+
* refreshes the list; on failure it restores the row opacity and toasts the
|
|
1323
|
+
* error.
|
|
1324
|
+
*/
|
|
1178
1325
|
export async function claimItem(itemId) {
|
|
1179
1326
|
const row = document.querySelector(`.item-row[onclick*="${itemId}"]`);
|
|
1180
1327
|
if (row)
|
|
@@ -1192,6 +1339,10 @@ export async function claimItem(itemId) {
|
|
|
1192
1339
|
row.style.opacity = '';
|
|
1193
1340
|
}
|
|
1194
1341
|
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Releases the current user's claim on the item via the release endpoint, then
|
|
1344
|
+
* reopens the detail and refreshes the items list.
|
|
1345
|
+
*/
|
|
1195
1346
|
export async function releaseItem(itemId) {
|
|
1196
1347
|
try {
|
|
1197
1348
|
await api('POST', `/projects/${state.currentProject.id}/pm/release/${itemId}`, {});
|
|
@@ -1204,6 +1355,10 @@ export async function releaseItem(itemId) {
|
|
|
1204
1355
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1205
1356
|
}
|
|
1206
1357
|
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Starts the item (moving it into in_progress) via the start-task endpoint,
|
|
1360
|
+
* then reopens the detail and refreshes the items list and project badge.
|
|
1361
|
+
*/
|
|
1207
1362
|
export async function startItem(itemId) {
|
|
1208
1363
|
try {
|
|
1209
1364
|
await api('POST', `/projects/${state.currentProject.id}/pm/start-task/${itemId}`, {});
|
|
@@ -1217,6 +1372,10 @@ export async function startItem(itemId) {
|
|
|
1217
1372
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1218
1373
|
}
|
|
1219
1374
|
}
|
|
1375
|
+
/**
|
|
1376
|
+
* Pauses the in-progress item via the pause-task endpoint, then reopens the
|
|
1377
|
+
* detail and refreshes the items list and project badge.
|
|
1378
|
+
*/
|
|
1220
1379
|
export async function pauseItem(itemId) {
|
|
1221
1380
|
try {
|
|
1222
1381
|
await api('POST', `/projects/${state.currentProject.id}/pm/pause-task/${itemId}`, {});
|
|
@@ -1233,6 +1392,12 @@ export async function pauseItem(itemId) {
|
|
|
1233
1392
|
// ═══════════════════════════════════════════════════════════════
|
|
1234
1393
|
// DEPS / LEARNINGS / TESTS / FILES
|
|
1235
1394
|
// ═══════════════════════════════════════════════════════════════
|
|
1395
|
+
/**
|
|
1396
|
+
* Adds a dependency to the item from the detail modal's dependency inputs:
|
|
1397
|
+
* reads and normalizes the target id and relationship, rejects an empty or
|
|
1398
|
+
* self-referential target, and posts to the deps endpoint. On success it
|
|
1399
|
+
* reopens the item detail.
|
|
1400
|
+
*/
|
|
1236
1401
|
export async function addDep(itemId) {
|
|
1237
1402
|
const targetIdEl = document.getElementById('dep-target-id');
|
|
1238
1403
|
const relEl = document.getElementById('dep-rel');
|
|
@@ -1255,6 +1420,11 @@ export async function addDep(itemId) {
|
|
|
1255
1420
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1256
1421
|
}
|
|
1257
1422
|
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Removes a dependency from the item after confirming with the user. Normalizes
|
|
1425
|
+
* the relationship, requires a target id, and DELETEs the dependency from the
|
|
1426
|
+
* deps endpoint; on success it reopens the item detail.
|
|
1427
|
+
*/
|
|
1258
1428
|
export async function removeDep(itemId, targetId, relation) {
|
|
1259
1429
|
const rel = normalizeDepRelation(relation);
|
|
1260
1430
|
if (!targetId) {
|
|
@@ -1272,6 +1442,11 @@ export async function removeDep(itemId, targetId, relation) {
|
|
|
1272
1442
|
}
|
|
1273
1443
|
}, true);
|
|
1274
1444
|
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Records a learning typed into the detail modal on the item, requiring
|
|
1447
|
+
* non-empty text. On success it toasts confirmation and reopens the item
|
|
1448
|
+
* detail.
|
|
1449
|
+
*/
|
|
1275
1450
|
export async function addLearning(itemId) {
|
|
1276
1451
|
const el = document.getElementById('new-learning');
|
|
1277
1452
|
const text = el?.value?.trim() || '';
|
|
@@ -1288,6 +1463,11 @@ export async function addLearning(itemId) {
|
|
|
1288
1463
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1289
1464
|
}
|
|
1290
1465
|
}
|
|
1466
|
+
/**
|
|
1467
|
+
* Adds a test command (required) with an optional description to the item via
|
|
1468
|
+
* the tests endpoint. On success it toasts confirmation and reopens the item
|
|
1469
|
+
* detail.
|
|
1470
|
+
*/
|
|
1291
1471
|
export async function addTest(itemId) {
|
|
1292
1472
|
const cmdEl = document.getElementById('new-test-cmd');
|
|
1293
1473
|
const descEl = document.getElementById('new-test-desc');
|
|
@@ -1306,6 +1486,10 @@ export async function addTest(itemId) {
|
|
|
1306
1486
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1307
1487
|
}
|
|
1308
1488
|
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Links a file path (required) to the item via the files endpoint. On success
|
|
1491
|
+
* it toasts confirmation and reopens the item detail.
|
|
1492
|
+
*/
|
|
1309
1493
|
export async function addFileLink(itemId) {
|
|
1310
1494
|
const el = document.getElementById('file-path-input');
|
|
1311
1495
|
const filePath = el?.value?.trim() || '';
|
|
@@ -1325,6 +1509,12 @@ export async function addFileLink(itemId) {
|
|
|
1325
1509
|
// ═══════════════════════════════════════════════════════════════
|
|
1326
1510
|
// USE ITEM AS TEMPLATE
|
|
1327
1511
|
// ═══════════════════════════════════════════════════════════════
|
|
1512
|
+
/**
|
|
1513
|
+
* Closes the item detail modal and opens the create view pre-filled from the
|
|
1514
|
+
* given item's fields, prefixing the title with "Copy of" and focusing and
|
|
1515
|
+
* selecting it so the user can immediately edit. The form is filled after a
|
|
1516
|
+
* short delay to let the create view render.
|
|
1517
|
+
*/
|
|
1328
1518
|
export function useItemAsTemplate(item) {
|
|
1329
1519
|
// Close the item detail modal and navigate to create view
|
|
1330
1520
|
hideModal('item-detail-modal');
|