@beastmode-develeap/beastmode 0.1.213 → 0.1.214
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/dist/web/board.html +186 -31
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +1 -1
package/dist/web/board.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
17
|
<!--BOARD_DATA-->
|
|
18
|
-
<script>window.__BUILD_STAMP__ = "20260509-
|
|
18
|
+
<script>window.__BUILD_STAMP__ = "20260509-230834-09eb714";</script>
|
|
19
19
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
20
20
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
21
21
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -2184,6 +2184,95 @@ input[type="range"]::-webkit-slider-thumb {
|
|
|
2184
2184
|
.filter-toggle:hover { border-color: var(--text-muted); color: var(--text); }
|
|
2185
2185
|
.filter-toggle.active { border-color: var(--accent); color: var(--accent); background: var(--accent-subtle); }
|
|
2186
2186
|
|
|
2187
|
+
/* Sort toggle button — mirrors filter-toggle */
|
|
2188
|
+
.sort-toggle {
|
|
2189
|
+
display: inline-flex;
|
|
2190
|
+
align-items: center;
|
|
2191
|
+
gap: 6px;
|
|
2192
|
+
padding: 0 14px;
|
|
2193
|
+
height: 36px;
|
|
2194
|
+
background: var(--bg-card);
|
|
2195
|
+
border: 1px solid var(--border);
|
|
2196
|
+
border-radius: var(--radius-sm);
|
|
2197
|
+
color: var(--text-secondary);
|
|
2198
|
+
font-size: 13px;
|
|
2199
|
+
font-family: var(--font-sans);
|
|
2200
|
+
cursor: pointer;
|
|
2201
|
+
transition: all 0.15s;
|
|
2202
|
+
white-space: nowrap;
|
|
2203
|
+
position: relative;
|
|
2204
|
+
}
|
|
2205
|
+
.sort-toggle:hover { border-color: var(--text-muted); color: var(--text); }
|
|
2206
|
+
.sort-toggle.active { border-color: var(--accent); color: var(--accent); background: var(--accent-subtle); }
|
|
2207
|
+
|
|
2208
|
+
.sort-active-label {
|
|
2209
|
+
font-size: 11px;
|
|
2210
|
+
font-weight: 600;
|
|
2211
|
+
color: var(--accent);
|
|
2212
|
+
margin-left: 2px;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
/* Sort dropdown */
|
|
2216
|
+
.sort-dropdown {
|
|
2217
|
+
position: absolute;
|
|
2218
|
+
top: 100%;
|
|
2219
|
+
right: 0;
|
|
2220
|
+
margin-top: 4px;
|
|
2221
|
+
background: var(--bg-card);
|
|
2222
|
+
border: 1px solid var(--border);
|
|
2223
|
+
border-radius: var(--radius-sm);
|
|
2224
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
2225
|
+
z-index: 100;
|
|
2226
|
+
min-width: 180px;
|
|
2227
|
+
padding: 4px;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
.sort-options {
|
|
2231
|
+
display: flex;
|
|
2232
|
+
flex-direction: column;
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
.sort-option {
|
|
2236
|
+
display: flex;
|
|
2237
|
+
align-items: center;
|
|
2238
|
+
justify-content: space-between;
|
|
2239
|
+
padding: 8px 12px;
|
|
2240
|
+
background: none;
|
|
2241
|
+
border: none;
|
|
2242
|
+
color: var(--text-secondary);
|
|
2243
|
+
font-size: 13px;
|
|
2244
|
+
font-family: var(--font-sans);
|
|
2245
|
+
cursor: pointer;
|
|
2246
|
+
border-radius: var(--radius-sm);
|
|
2247
|
+
transition: background 0.1s;
|
|
2248
|
+
width: 100%;
|
|
2249
|
+
text-align: left;
|
|
2250
|
+
}
|
|
2251
|
+
.sort-option:hover { background: var(--bg-hover); color: var(--text); }
|
|
2252
|
+
.sort-option.active { color: var(--accent); font-weight: 600; }
|
|
2253
|
+
|
|
2254
|
+
.sort-dir-icon {
|
|
2255
|
+
font-size: 14px;
|
|
2256
|
+
font-weight: 700;
|
|
2257
|
+
color: var(--accent);
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
.sort-clear {
|
|
2261
|
+
display: block;
|
|
2262
|
+
width: 100%;
|
|
2263
|
+
padding: 8px 12px;
|
|
2264
|
+
background: none;
|
|
2265
|
+
border: none;
|
|
2266
|
+
border-top: 1px solid var(--border);
|
|
2267
|
+
color: var(--text-muted);
|
|
2268
|
+
font-size: 12px;
|
|
2269
|
+
font-family: var(--font-sans);
|
|
2270
|
+
cursor: pointer;
|
|
2271
|
+
text-align: center;
|
|
2272
|
+
margin-top: 4px;
|
|
2273
|
+
}
|
|
2274
|
+
.sort-clear:hover { color: var(--danger); }
|
|
2275
|
+
|
|
2187
2276
|
.view-toggle {
|
|
2188
2277
|
display: inline-flex;
|
|
2189
2278
|
border: 1px solid var(--border);
|
|
@@ -3852,6 +3941,7 @@ function Icon({ name, size = 18, className = '' }) {
|
|
|
3852
3941
|
lightbulb: html`<path d="M8 1a5 5 0 00-3 9c.5.6.8 1.2 1 1.8V13h4v-1.2c.2-.6.5-1.2 1-1.8A5 5 0 008 1z"/><line x1="6" y1="13.5" x2="10" y2="13.5"/><line x1="6.5" y1="15" x2="9.5" y2="15"/>`,
|
|
3853
3942
|
help: html`<circle cx="8" cy="8" r="6.5"/><path d="M6 6.5a2 2 0 013.7 1c0 1.5-1.7 1.5-1.7 3"/><circle cx="8" cy="12" r="0.5" fill="currentColor" stroke="none"/>`,
|
|
3854
3943
|
filter: html`<polygon points="1.5,2 14.5,2 9.5,8.5 9.5,13 6.5,14.5 6.5,8.5"/>`,
|
|
3944
|
+
'arrow-up-down': html`<path d="M4 6l4-4 4 4"/><path d="M4 10l4 4 4-4"/>`,
|
|
3855
3945
|
costs: html`<circle cx="8" cy="8" r="6.5"/><path d="M8 3.5v1m0 7v1M6 10.5c0 .8.9 1.5 2 1.5s2-.7 2-1.5S9.1 9 8 9s-2-.7-2-1.5S6.9 6 8 6s2 .7 2 1.5"/>`,
|
|
3856
3946
|
};
|
|
3857
3947
|
|
|
@@ -5895,8 +5985,7 @@ function PipelineView({
|
|
|
5895
5985
|
selectedProject,
|
|
5896
5986
|
setSelectedItem,
|
|
5897
5987
|
deleteItem,
|
|
5898
|
-
|
|
5899
|
-
cycleSort,
|
|
5988
|
+
globalSort,
|
|
5900
5989
|
sortColumnItems,
|
|
5901
5990
|
costsByItem,
|
|
5902
5991
|
envVerifyByItem,
|
|
@@ -5995,8 +6084,7 @@ function PipelineView({
|
|
|
5995
6084
|
${columns.map(colId => {
|
|
5996
6085
|
const meta = getColumnMeta(colId);
|
|
5997
6086
|
const rawColItems = laneItemList.filter(item => getItemColumn(item, columns) === colId);
|
|
5998
|
-
const colItems = sortColumnItems(rawColItems,
|
|
5999
|
-
const sortMode = columnSorts[colId] || '';
|
|
6087
|
+
const colItems = sortColumnItems(rawColItems, globalSort);
|
|
6000
6088
|
return html`
|
|
6001
6089
|
<div class="pipeline-column"
|
|
6002
6090
|
data-testid=${'pipeline-col-' + colId}
|
|
@@ -6007,9 +6095,8 @@ function PipelineView({
|
|
|
6007
6095
|
onDrop=${e => onDrop(e, colId)}>
|
|
6008
6096
|
<div class="pipeline-column-header"
|
|
6009
6097
|
style=${'--col-color: ' + meta.color}
|
|
6010
|
-
title=${meta.tooltip}
|
|
6011
|
-
|
|
6012
|
-
<span>${meta.label}${sortMode && html`<span class="sort-indicator">${sortMode === 'priority' ? '\u25BC' : '\u25B2'}</span>`}</span>
|
|
6098
|
+
title=${meta.tooltip}>
|
|
6099
|
+
<span>${meta.label}</span>
|
|
6013
6100
|
<span class=${'kanban-count' + (colItems.length === 0 ? ' kanban-count-zero' : '')}>${colItems.length}</span>
|
|
6014
6101
|
</div>
|
|
6015
6102
|
<div class="pipeline-column-items">
|
|
@@ -6260,7 +6347,14 @@ function BoardPage({ selectedProject }) {
|
|
|
6260
6347
|
const allKeys = ((window.PIPELINE_CONFIG && window.PIPELINE_CONFIG.swimlanes) || []).map(s => s.key);
|
|
6261
6348
|
return new Set(allKeys);
|
|
6262
6349
|
});
|
|
6263
|
-
const [
|
|
6350
|
+
const [globalSort, setGlobalSort] = useState(() => {
|
|
6351
|
+
try {
|
|
6352
|
+
const saved = localStorage.getItem('beastmode-sort-mode');
|
|
6353
|
+
if (saved) return JSON.parse(saved);
|
|
6354
|
+
} catch {}
|
|
6355
|
+
return { field: '', direction: 'asc' };
|
|
6356
|
+
});
|
|
6357
|
+
const [sortOpen, setSortOpen] = useState(false);
|
|
6264
6358
|
const [epicCollapseKey, setEpicCollapseKey] = useState(0);
|
|
6265
6359
|
const [costsByItem, setCostsByItem] = useState({});
|
|
6266
6360
|
const [envVerifyByItem, setEnvVerifyByItem] = useState({});
|
|
@@ -6699,6 +6793,25 @@ function BoardPage({ selectedProject }) {
|
|
|
6699
6793
|
} catch {}
|
|
6700
6794
|
}, [activeSwimlanesSet]);
|
|
6701
6795
|
|
|
6796
|
+
// Persist sort preference to localStorage whenever it changes.
|
|
6797
|
+
useEffect(() => {
|
|
6798
|
+
localStorage.setItem('beastmode-sort-mode', JSON.stringify(globalSort));
|
|
6799
|
+
}, [globalSort]);
|
|
6800
|
+
|
|
6801
|
+
// Close sort dropdown when clicking outside.
|
|
6802
|
+
useEffect(() => {
|
|
6803
|
+
if (!sortOpen) return;
|
|
6804
|
+
const handler = (e) => {
|
|
6805
|
+
const dropdown = document.querySelector('.sort-dropdown');
|
|
6806
|
+
const toggle = document.querySelector('[data-testid="sort-toggle"]');
|
|
6807
|
+
if (dropdown && !dropdown.contains(e.target) && toggle && !toggle.contains(e.target)) {
|
|
6808
|
+
setSortOpen(false);
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
document.addEventListener('click', handler, true);
|
|
6812
|
+
return () => document.removeEventListener('click', handler, true);
|
|
6813
|
+
}, [sortOpen]);
|
|
6814
|
+
|
|
6702
6815
|
// Expose swimlane state for external scenario verification.
|
|
6703
6816
|
useEffect(() => {
|
|
6704
6817
|
const swimlanes = (window.PIPELINE_CONFIG && window.PIPELINE_CONFIG.swimlanes) || [];
|
|
@@ -6780,28 +6893,32 @@ function BoardPage({ selectedProject }) {
|
|
|
6780
6893
|
const activeFilterCount = _baseFilterCount + (_swimlaneFilterActive ? 1 : 0);
|
|
6781
6894
|
|
|
6782
6895
|
// ── Column sorting ──
|
|
6896
|
+
const SORT_LABELS = {
|
|
6897
|
+
priority: 'Priority',
|
|
6898
|
+
name: 'Name',
|
|
6899
|
+
created: 'Created',
|
|
6900
|
+
updated: 'Updated',
|
|
6901
|
+
status_age: 'Time in Status',
|
|
6902
|
+
};
|
|
6783
6903
|
const PRIORITY_ORDER = { 'Critical': 0, 'High': 1, 'Medium': 2, 'Low': 3, '': 4 };
|
|
6784
|
-
const sortColumnItems = (colItems,
|
|
6785
|
-
if (!
|
|
6904
|
+
const sortColumnItems = (colItems, sort) => {
|
|
6905
|
+
if (!sort || !sort.field) return colItems;
|
|
6906
|
+
const dir = sort.direction === 'desc' ? -1 : 1;
|
|
6786
6907
|
const sorted = [...colItems];
|
|
6787
|
-
if (
|
|
6788
|
-
sorted.sort((a, b) => (PRIORITY_ORDER[a.priority || ''] ?? 4) - (PRIORITY_ORDER[b.priority || ''] ?? 4));
|
|
6789
|
-
} else if (
|
|
6790
|
-
sorted.sort((a, b) => (a.name || a.title || '').localeCompare(b.name || b.title || ''));
|
|
6908
|
+
if (sort.field === 'priority') {
|
|
6909
|
+
sorted.sort((a, b) => dir * ((PRIORITY_ORDER[a.priority || ''] ?? 4) - (PRIORITY_ORDER[b.priority || ''] ?? 4)));
|
|
6910
|
+
} else if (sort.field === 'name') {
|
|
6911
|
+
sorted.sort((a, b) => dir * (a.name || a.title || '').localeCompare(b.name || b.title || ''));
|
|
6912
|
+
} else if (sort.field === 'created') {
|
|
6913
|
+
sorted.sort((a, b) => dir * ((a.created_at || '').localeCompare(b.created_at || '')));
|
|
6914
|
+
} else if (sort.field === 'updated') {
|
|
6915
|
+
sorted.sort((a, b) => dir * ((a.updated_at || '').localeCompare(b.updated_at || '')));
|
|
6916
|
+
} else if (sort.field === 'status_age') {
|
|
6917
|
+
sorted.sort((a, b) => dir * ((a.status_changed_at || a.updated_at || '').localeCompare(b.status_changed_at || b.updated_at || '')));
|
|
6791
6918
|
}
|
|
6792
6919
|
return sorted;
|
|
6793
6920
|
};
|
|
6794
6921
|
|
|
6795
|
-
const cycleSort = (colId) => {
|
|
6796
|
-
setColumnSorts(prev => {
|
|
6797
|
-
const current = prev[colId] || '';
|
|
6798
|
-
const next = current === '' ? 'priority' : current === 'priority' ? 'name' : '';
|
|
6799
|
-
const copy = { ...prev };
|
|
6800
|
-
if (next) copy[colId] = next; else delete copy[colId];
|
|
6801
|
-
return copy;
|
|
6802
|
-
});
|
|
6803
|
-
};
|
|
6804
|
-
|
|
6805
6922
|
// Unique project IDs and parent epics for filter dropdowns
|
|
6806
6923
|
const uniqueProjects = [...new Set(items.map(i => i.project_id).filter(Boolean))];
|
|
6807
6924
|
const uniqueEpics = [...new Set(items.map(i => i.parent_epic).filter(Boolean))].map(String);
|
|
@@ -6887,6 +7004,46 @@ function BoardPage({ selectedProject }) {
|
|
|
6887
7004
|
${activeFilterCount > 0 && html`<span class="filter-active-count">${activeFilterCount}</span>`}
|
|
6888
7005
|
</button>
|
|
6889
7006
|
|
|
7007
|
+
<div style="position: relative;">
|
|
7008
|
+
<button class=${'sort-toggle' + (sortOpen || globalSort.field ? ' active' : '')}
|
|
7009
|
+
onClick=${() => setSortOpen(v => !v)}
|
|
7010
|
+
data-testid="sort-toggle">
|
|
7011
|
+
<${Icon} name="arrow-up-down" size=${13} />
|
|
7012
|
+
Sort
|
|
7013
|
+
${globalSort.field && html`<span class="sort-active-label">${SORT_LABELS[globalSort.field]}</span>`}
|
|
7014
|
+
</button>
|
|
7015
|
+
${sortOpen && html`
|
|
7016
|
+
<div class="sort-dropdown" data-testid="sort-dropdown">
|
|
7017
|
+
<div class="sort-options">
|
|
7018
|
+
${Object.entries(SORT_LABELS).map(([key, label]) => html`
|
|
7019
|
+
<button key=${key}
|
|
7020
|
+
class=${'sort-option' + (globalSort.field === key ? ' active' : '')}
|
|
7021
|
+
data-testid=${'sort-option-' + key}
|
|
7022
|
+
onClick=${() => {
|
|
7023
|
+
if (globalSort.field === key) {
|
|
7024
|
+
setGlobalSort(s => ({ ...s, direction: s.direction === 'asc' ? 'desc' : 'asc' }));
|
|
7025
|
+
} else {
|
|
7026
|
+
const defaultDir = (key === 'created' || key === 'updated') ? 'desc' : 'asc';
|
|
7027
|
+
setGlobalSort({ field: key, direction: defaultDir });
|
|
7028
|
+
}
|
|
7029
|
+
}}>
|
|
7030
|
+
<span>${label}</span>
|
|
7031
|
+
${globalSort.field === key && html`
|
|
7032
|
+
<span class="sort-dir-icon">${globalSort.direction === 'asc' ? '↑' : '↓'}</span>
|
|
7033
|
+
`}
|
|
7034
|
+
</button>
|
|
7035
|
+
`)}
|
|
7036
|
+
</div>
|
|
7037
|
+
${globalSort.field && html`
|
|
7038
|
+
<button class="sort-clear" data-testid="sort-clear"
|
|
7039
|
+
onClick=${() => { setGlobalSort({ field: '', direction: 'asc' }); setSortOpen(false); }}>
|
|
7040
|
+
Clear sort
|
|
7041
|
+
</button>
|
|
7042
|
+
`}
|
|
7043
|
+
</div>
|
|
7044
|
+
`}
|
|
7045
|
+
</div>
|
|
7046
|
+
|
|
6890
7047
|
<button class="btn btn-primary" onClick=${() => setShowCreateDialog(true)}>
|
|
6891
7048
|
<${Icon} name="plus" size=${14} />
|
|
6892
7049
|
New Task
|
|
@@ -6984,8 +7141,7 @@ function BoardPage({ selectedProject }) {
|
|
|
6984
7141
|
selectedProject=${selectedProject}
|
|
6985
7142
|
setSelectedItem=${setSelectedItem}
|
|
6986
7143
|
deleteItem=${deleteItem}
|
|
6987
|
-
|
|
6988
|
-
cycleSort=${cycleSort}
|
|
7144
|
+
globalSort=${globalSort}
|
|
6989
7145
|
sortColumnItems=${sortColumnItems}
|
|
6990
7146
|
costsByItem=${costsByItem}
|
|
6991
7147
|
envVerifyByItem=${envVerifyByItem}
|
|
@@ -7020,15 +7176,14 @@ function BoardPage({ selectedProject }) {
|
|
|
7020
7176
|
if (col.also && col.also.includes(i.status)) return true;
|
|
7021
7177
|
return false;
|
|
7022
7178
|
});
|
|
7023
|
-
const colItems = sortColumnItems(rawColItems,
|
|
7024
|
-
const sortMode = columnSorts[col.id] || '';
|
|
7179
|
+
const colItems = sortColumnItems(rawColItems, globalSort);
|
|
7025
7180
|
return html`
|
|
7026
7181
|
<div class="kanban-column" key=${col.id}
|
|
7027
7182
|
onDragOver=${e => onDragOver(e, col.id)}
|
|
7028
7183
|
onDragLeave=${onDragLeave}
|
|
7029
7184
|
onDrop=${e => onDrop(e, col.id)}>
|
|
7030
|
-
<div class="kanban-column-header" style=${'--col-color: ' + col.color} title=${col.tooltip}
|
|
7031
|
-
<span>${col.label}
|
|
7185
|
+
<div class="kanban-column-header" style=${'--col-color: ' + col.color} title=${col.tooltip}>
|
|
7186
|
+
<span>${col.label}</span>
|
|
7032
7187
|
<span class=${'kanban-count' + (colItems.length === 0 ? ' kanban-count-zero' : '')}>${colItems.length}</span>
|
|
7033
7188
|
</div>
|
|
7034
7189
|
<div class="kanban-items">
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
09eb71470e076891f6ed9304e081cc9372bad7fc
|
package/dist/web/build-stamp.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
20260509-
|
|
1
|
+
20260509-230834-09eb714
|