@stacksjs/defaults 0.70.354 → 0.70.355
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/ai/skills/stacks-browse/SKILL.md +17 -2
- package/ai/skills/stacks-browse/scripts/browse.ts +207 -6
- package/ai/skills/stacks-email/SKILL.md +35 -15
- package/app/Actions/Dashboard/Auth/MeAction.ts +8 -7
- package/app/Actions/Dashboard/Ci/RepoRunJobsAction.ts +10 -15
- package/app/Actions/Dashboard/Ci/RepoRunsAction.ts +12 -14
- package/app/Actions/Dashboard/Ci/RunnerHistoryAction.ts +11 -10
- package/app/Actions/Dashboard/Ci/StatusAction.ts +2 -11
- package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +3 -4
- package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/PaymentRefundAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +3 -2
- package/app/Actions/Dashboard/Deployments/GetDeployment.ts +1 -0
- package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +3 -2
- package/app/Actions/Dashboard/Email/InboxActivityAction.ts +28 -16
- package/app/Actions/Dashboard/Email/InboxAttachmentDownloadAction.ts +45 -0
- package/app/Actions/Dashboard/Email/InboxDeleteAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxIndexAction.ts +5 -7
- package/app/Actions/Dashboard/Email/InboxMarkReadAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxMarkUnreadAction.ts +11 -6
- package/app/Actions/Dashboard/Email/InboxPreferenceShowAction.ts +10 -4
- package/app/Actions/Dashboard/Email/InboxPreferenceUpdateAction.ts +8 -9
- package/app/Actions/Dashboard/Email/InboxShowAction.ts +7 -7
- package/app/Actions/Dashboard/Email/InboxStatsAction.ts +5 -7
- package/app/Actions/Dashboard/Email/inbox-request.ts +33 -0
- package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +3 -4
- package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +3 -6
- package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +8 -15
- package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +4 -3
- package/app/Actions/Dashboard/Jobs/JobShowAction.ts +3 -3
- package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +9 -16
- package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +1 -2
- package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +11 -9
- package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +10 -12
- package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +19 -43
- package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +44 -0
- package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/CardShowAction.ts +4 -4
- package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +7 -6
- package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +4 -4
- package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +4 -3
- package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +5 -5
- package/app/Actions/Dashboard/Kanban/UsersListAction.ts +1 -1
- package/app/Actions/Dashboard/Kanban/kanban-comment.ts +14 -0
- package/app/Actions/Dashboard/Kanban/kanban-model.test.ts +36 -0
- package/app/Actions/Dashboard/Kanban/kanban-model.ts +14 -7
- package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +4 -3
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +15 -18
- package/app/Actions/Dashboard/Models/ModelStoreAction.ts +8 -8
- package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +9 -9
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +4 -3
- package/app/Actions/Dashboard/Models/model-api.ts +27 -0
- package/app/Actions/Dashboard/Models/model-write.ts +2 -10
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +3 -4
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +6 -4
- package/app/Actions/Dashboard/Queries/QueryShowAction.ts +1 -0
- package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +4 -4
- package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +3 -2
- package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +4 -4
- package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +4 -4
- package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +4 -3
- package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +3 -2
- package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +4 -5
- package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +4 -4
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +1 -0
- package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +3 -3
- package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +2 -2
- package/app/Actions/Dashboard/dashboard-request.ts +13 -0
- package/app/Models/CardComment.ts +3 -4
- package/functions/dashboard-api.ts +51 -10
- package/functions/inbox.test.ts +2 -0
- package/functions/inbox.ts +27 -2
- package/functions/public-application-url.test.ts +13 -0
- package/functions/public-application-url.ts +27 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +1 -1
- package/resources/components/Dashboard/Auth/AccessTokens.stx +15 -0
- package/resources/components/Dashboard/Ci/CiDashboard.stx +24 -6
- package/resources/components/Dashboard/Commerce/CommerceTaxDialog.stx +1 -1
- package/resources/components/Dashboard/Commerce/ProductWaitlistDashboard.stx +8 -7
- package/resources/components/Dashboard/Commerce/RestaurantWaitlistDashboard.stx +8 -7
- package/resources/components/Dashboard/Content/BlogDashboard.stx +14 -2
- package/resources/components/Dashboard/Content/PostsDashboard.stx +7 -1
- package/resources/components/Dashboard/Deployments/DeploymentDetail.stx +16 -2
- package/resources/components/Dashboard/Email/EmailNavigation.stx +5 -5
- package/resources/components/Dashboard/Email/EmailSidebar.stx +1 -1
- package/resources/components/Dashboard/Email/InboxAttachmentList.stx +53 -0
- package/resources/components/Dashboard/Email/InboxDashboard.stx +5 -4
- package/resources/components/Dashboard/Email/InboxMessageDetail.stx +6 -4
- package/resources/components/Dashboard/Kanban/KanbanBoardDashboard.stx +63 -6
- package/resources/components/Dashboard/Kanban/KanbanBoardsDashboard.stx +3 -6
- package/resources/components/Dashboard/Management/PermissionsDashboard.stx +4 -4
- package/resources/components/Dashboard/NotFoundDashboard.stx +2 -5
- package/resources/components/Dashboard/Queries/QueryDashboard.stx +6 -2
- package/resources/components/Dashboard/Settings/AppearanceSettingsDashboard.stx +4 -4
- package/resources/components/Dashboard/UI/EmptyState.stx +2 -2
- package/resources/components/Dashboard/UI/PageLayout.stx +3 -3
- package/resources/functions/dashboard/sidebar.ts +4 -4
- package/routes/dashboard-api.ts +8 -20
- package/views/dashboard/.discovered-models.json +24 -0
- package/views/dashboard/layouts/default.stx +19 -16
- package/views/dashboard/stores/ci.ts +13 -7
- package/views/dashboard/stores/kanban.ts +48 -11
package/routes/dashboard-api.ts
CHANGED
|
@@ -264,28 +264,15 @@ route.group({ prefix: '/api/dashboard', apiResponse: true }, () => {
|
|
|
264
264
|
// requests — see the action for the soft-fallback shape.
|
|
265
265
|
route.get('/auth/me', 'Actions/Dashboard/Auth/MeAction')
|
|
266
266
|
|
|
267
|
-
// Kanban
|
|
268
|
-
//
|
|
269
|
-
// Phase 2 lands write endpoints under the same prefix:
|
|
270
|
-
// POST /kanban/boards — store
|
|
271
|
-
// PATCH /kanban/boards/{id} — update
|
|
272
|
-
// DELETE /kanban/boards/{id} — destroy
|
|
273
|
-
// POST /kanban/boards/reorder — bulk position update
|
|
274
|
-
// POST /kanban/columns — store
|
|
275
|
-
// PATCH /kanban/columns/{id} — update
|
|
276
|
-
// POST /kanban/columns/reorder — bulk position update
|
|
277
|
-
// POST /kanban/cards — store
|
|
278
|
-
// PATCH /kanban/cards/{id} — update (incl. column move)
|
|
279
|
-
// POST /kanban/cards/reorder — bulk position update
|
|
280
|
-
//
|
|
267
|
+
// Kanban reads and mutations share the guarded dashboard surface.
|
|
281
268
|
// Kanban data is local-only without authentication. Outside local
|
|
282
269
|
// environments every read and write uses the same auth + admin guard as
|
|
283
270
|
// the other operational dashboard surfaces.
|
|
284
|
-
// Reads
|
|
271
|
+
// Reads
|
|
285
272
|
guard(route.get('/kanban/boards', 'Actions/Dashboard/Kanban/BoardsIndexAction'))
|
|
286
273
|
guard(route.get('/kanban/boards/{id}', 'Actions/Dashboard/Kanban/BoardShowAction'))
|
|
287
274
|
|
|
288
|
-
// Writes
|
|
275
|
+
// Writes. The reorder endpoints are POST not PATCH because
|
|
289
276
|
// their semantics — "here's the full new state of this slice of the
|
|
290
277
|
// board" — match the resource-replacement intent better than PATCH's
|
|
291
278
|
// "apply this delta" verb. They also accept a body shape that PATCH
|
|
@@ -305,7 +292,7 @@ route.group({ prefix: '/api/dashboard', apiResponse: true }, () => {
|
|
|
305
292
|
guard(route.delete('/kanban/cards/{id}', 'Actions/Dashboard/Kanban/CardDestroyAction'))
|
|
306
293
|
guard(route.post('/kanban/cards/reorder', 'Actions/Dashboard/Kanban/CardsReorderAction'))
|
|
307
294
|
|
|
308
|
-
//
|
|
295
|
+
// Card detail, labels, assignees, and comments.
|
|
309
296
|
//
|
|
310
297
|
// The card-show endpoint is the only "single card with everything"
|
|
311
298
|
// read; the boards/{id} response already embeds labels + assignees
|
|
@@ -325,10 +312,10 @@ route.group({ prefix: '/api/dashboard', apiResponse: true }, () => {
|
|
|
325
312
|
guard(route.post('/kanban/cards/{id}/labels', 'Actions/Dashboard/Kanban/CardLabelsSyncAction'))
|
|
326
313
|
guard(route.post('/kanban/cards/{id}/assignees', 'Actions/Dashboard/Kanban/CardAssigneesSyncAction'))
|
|
327
314
|
|
|
328
|
-
//
|
|
329
|
-
//
|
|
330
|
-
// until someone explicitly asks for editing.
|
|
315
|
+
// Card-scoped comment thread. Writes stay under the dashboard guard while
|
|
316
|
+
// the CardComment model also exposes its conventional generated API.
|
|
331
317
|
guard(route.post('/kanban/cards/{id}/comments', 'Actions/Dashboard/Kanban/CardCommentStoreAction'))
|
|
318
|
+
guard(route.patch('/kanban/comments/{id}', 'Actions/Dashboard/Kanban/CardCommentUpdateAction'))
|
|
332
319
|
guard(route.delete('/kanban/comments/{id}', 'Actions/Dashboard/Kanban/CardCommentDestroyAction'))
|
|
333
320
|
|
|
334
321
|
// Lightweight user list for the assignee picker. Distinct from the
|
|
@@ -466,6 +453,7 @@ route.group({ prefix: '/api/dashboard', apiResponse: true }, () => {
|
|
|
466
453
|
// the per-mailbox inbox.json index. Both are guarded outside local/dev
|
|
467
454
|
// because they touch real mailbox state and PII.
|
|
468
455
|
guard(route.get('/email/inbox', 'Actions/Dashboard/Email/InboxIndexAction'))
|
|
456
|
+
guard(route.get('/email/inbox/{id}/attachments/{attachmentId}', 'Actions/Dashboard/Email/InboxAttachmentDownloadAction'))
|
|
469
457
|
guard(route.get('/email/inbox/{id}', 'Actions/Dashboard/Email/InboxShowAction'))
|
|
470
458
|
guard(route.get('/email/stats', 'Actions/Dashboard/Email/InboxStatsAction'))
|
|
471
459
|
guard(route.get('/email/activity', 'Actions/Dashboard/Email/InboxActivityAction'))
|
|
@@ -138,6 +138,16 @@
|
|
|
138
138
|
"highlight": true
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
"id": "delivery-stop",
|
|
143
|
+
"name": "DeliveryStop",
|
|
144
|
+
"icon": "tablecells.fill",
|
|
145
|
+
"hasDedicatedPage": false,
|
|
146
|
+
"category": "commerce",
|
|
147
|
+
"dashboard": {
|
|
148
|
+
"highlight": true
|
|
149
|
+
}
|
|
150
|
+
},
|
|
141
151
|
{
|
|
142
152
|
"id": "deployment",
|
|
143
153
|
"name": "Deployment",
|
|
@@ -168,6 +178,13 @@
|
|
|
168
178
|
"highlight": true
|
|
169
179
|
}
|
|
170
180
|
},
|
|
181
|
+
{
|
|
182
|
+
"id": "driver-ping",
|
|
183
|
+
"name": "DriverPing",
|
|
184
|
+
"icon": "tablecells.fill",
|
|
185
|
+
"hasDedicatedPage": false,
|
|
186
|
+
"category": "commerce"
|
|
187
|
+
},
|
|
171
188
|
{
|
|
172
189
|
"id": "email-list",
|
|
173
190
|
"name": "EmailList",
|
|
@@ -456,6 +473,13 @@
|
|
|
456
473
|
"highlight": true
|
|
457
474
|
}
|
|
458
475
|
},
|
|
476
|
+
{
|
|
477
|
+
"id": "social-account",
|
|
478
|
+
"name": "SocialAccount",
|
|
479
|
+
"icon": "tablecells.fill",
|
|
480
|
+
"hasDedicatedPage": false,
|
|
481
|
+
"category": "data"
|
|
482
|
+
},
|
|
459
483
|
{
|
|
460
484
|
"id": "social-post",
|
|
461
485
|
"name": "SocialPost",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
// Dashboard chrome. The navigation pane is `@stacksjs/components`' <Sidebar>
|
|
3
|
-
// in its `macos` theme
|
|
3
|
+
// in its `macos` theme - the same component Craft apps use, so the dashboard
|
|
4
4
|
// and a native Craft build render the identical source list.
|
|
5
5
|
//
|
|
6
6
|
// This used to be a hand-rolled <aside> plus ~180 lines of plain CSS, because
|
|
@@ -89,7 +89,7 @@ canvas[id$="_chart"] {
|
|
|
89
89
|
* solid body background would paint over it. So the body stays clear and the
|
|
90
90
|
* content color lives on the scrolling pane, which lets the frosted material
|
|
91
91
|
* read through the translucent sidebar. (Do not write angle-bracket tag names in
|
|
92
|
-
* a layout style-block comment
|
|
92
|
+
* a layout style-block comment - stx parses them as elements and breaks out.)
|
|
93
93
|
*/
|
|
94
94
|
body {
|
|
95
95
|
background: transparent;
|
|
@@ -104,6 +104,9 @@ body {
|
|
|
104
104
|
[data-stx-content] {
|
|
105
105
|
min-width: 0;
|
|
106
106
|
min-height: 100vh;
|
|
107
|
+
/* Wide tables scroll inside their own overflow containers without widening
|
|
108
|
+
the document or moving the fixed dashboard shell. */
|
|
109
|
+
overflow-x: hidden;
|
|
107
110
|
margin-left: var(--stx-sidebar-width, 250px);
|
|
108
111
|
background: #f4f4f5;
|
|
109
112
|
transition: margin-left 0.3s ease-out;
|
|
@@ -158,15 +161,15 @@ body {
|
|
|
158
161
|
* Two halves, and only one of them is the sidebar.
|
|
159
162
|
*
|
|
160
163
|
* THE SHELL. Arc's defining move is that the content is not a pane butted up
|
|
161
|
-
* against a sidebar
|
|
164
|
+
* against a sidebar - it is a card floating ON the sidebar's background, inset
|
|
162
165
|
* and rounded on every side. So the panel color moves to the body, the sidebar
|
|
163
166
|
* goes transparent on top of it, and the content gets a margin all the way
|
|
164
167
|
* round. No sidebar theme can do this part; it belongs to the layout.
|
|
165
168
|
*
|
|
166
169
|
* THE ROWS. The installed @stacksjs/components predates its own `arc` theme,
|
|
167
170
|
* so the component renders macOS rows and this restyles them. The values are
|
|
168
|
-
* copied from that theme on purpose
|
|
169
|
-
* a hairline and a one-pixel shadow instead of an accent fill
|
|
171
|
+
* copied from that theme on purpose - 30px rows, 8px radius, a white card with
|
|
172
|
+
* a hairline and a one-pixel shadow instead of an accent fill - so that when
|
|
170
173
|
* the package ships it, the component and this skin agree and these rules
|
|
171
174
|
* become a no-op rather than a second, competing look.
|
|
172
175
|
*
|
|
@@ -215,7 +218,7 @@ html[data-appearance="arc"] [data-stx-content] {
|
|
|
215
218
|
properties wedges the interpolation in WebKit: switching appearance leaves
|
|
216
219
|
it stuck at the old value until a reload. Inheriting the base declaration
|
|
217
220
|
means the property never changes when the appearance does, so there is
|
|
218
|
-
nothing to wedge
|
|
221
|
+
nothing to wedge - and the collapse animation keeps working identically in
|
|
219
222
|
both styles.
|
|
220
223
|
|
|
221
224
|
The panel strip down the card's left side is the sidebar's own trailing
|
|
@@ -224,20 +227,20 @@ html[data-appearance="arc"] [data-stx-content] {
|
|
|
224
227
|
margin-right: var(--dash-arc-gap);
|
|
225
228
|
margin-bottom: var(--dash-arc-gap);
|
|
226
229
|
/* Beats the base rule's `min-height: 100vh`, which would otherwise hold the
|
|
227
|
-
card a full viewport tall and push its bottom edge off-screen
|
|
230
|
+
card a full viewport tall and push its bottom edge off-screen - losing the
|
|
228
231
|
one gap that shows it is floating. */
|
|
229
232
|
min-height: 0;
|
|
230
233
|
/* The card is exactly the viewport minus its own gaps, and scrolls its own
|
|
231
234
|
content. Letting the page scroll instead would grow the card past the
|
|
232
|
-
bottom of the window, so the one edge that proves it is floating
|
|
233
|
-
panel showing beneath it
|
|
235
|
+
bottom of the window, so the one edge that proves it is floating - the
|
|
236
|
+
panel showing beneath it - would only ever appear at the end of a long
|
|
234
237
|
page. This is also what Arc does: the sidebar is stationary and the card
|
|
235
238
|
scrolls inside its frame. */
|
|
236
239
|
height: calc(100vh - (2 * var(--dash-arc-gap)));
|
|
237
240
|
overflow-y: auto;
|
|
238
241
|
border-radius: var(--dash-arc-radius);
|
|
239
242
|
background: #fff;
|
|
240
|
-
/* Clip page content to the rounded corners
|
|
243
|
+
/* Clip page content to the rounded corners - full-bleed headers and tables
|
|
241
244
|
otherwise square them off at the top. */
|
|
242
245
|
overflow-x: hidden;
|
|
243
246
|
box-shadow:
|
|
@@ -352,7 +355,7 @@ html[data-appearance="arc"].dark [data-sidebar-selection="accent"][data-window-k
|
|
|
352
355
|
}
|
|
353
356
|
|
|
354
357
|
/* The icon, count and chevron turn white on a macOS accent fill. On a white
|
|
355
|
-
card they must not
|
|
358
|
+
card they must not - they keep their own tint, the same as an idle row. */
|
|
356
359
|
html[data-appearance="arc"] [data-sidebar-selection="accent"][data-window-key="true"] [data-sidebar-item][data-active="true"] [data-sidebar-icon],
|
|
357
360
|
html[data-appearance="arc"] [data-sidebar-selection="accent"][data-window-key="true"] [data-sidebar-item][data-active="true"] [data-sidebar-chevron] {
|
|
358
361
|
color: var(--stx-sidebar-icon, currentColor);
|
|
@@ -372,7 +375,7 @@ html[data-appearance="arc"] [data-sidebar-section] > div:first-child {
|
|
|
372
375
|
|
|
373
376
|
/* A viewer can hide navigation sections they never use. Kept as CSS on an
|
|
374
377
|
attribute rather than a DOM removal so the rows stay hydrated and a later
|
|
375
|
-
change re-shows them without a re-render
|
|
378
|
+
change re-shows them without a re-render - the same approach the role
|
|
376
379
|
filter above uses. */
|
|
377
380
|
[data-sidebar-section][data-viewer-hidden="true"] {
|
|
378
381
|
display: none;
|
|
@@ -397,7 +400,7 @@ registerStoresClient({ authStore, kanbanStore, rbacStore })
|
|
|
397
400
|
|
|
398
401
|
// Helpers below return COMPLETE class strings (static + dynamic) and are
|
|
399
402
|
// applied via `:class="fn(...)"` because stx's `{{ ... }}` placeholder is
|
|
400
|
-
// not reactively bound inside a `class` attribute
|
|
403
|
+
// not reactively bound inside a `class` attribute - only inside text content.
|
|
401
404
|
const toastIconClass = (type) => `flex-shrink-0 h-5 w-5 mt-0.5 ${({
|
|
402
405
|
success: 'i-hugeicons-checkmark-circle-02 text-green-500 dark:text-green-400',
|
|
403
406
|
error: 'i-hugeicons-alert-02 text-red-500 dark:text-red-400',
|
|
@@ -466,7 +469,7 @@ effect(() => {
|
|
|
466
469
|
// The inline bootstrap at the top of this layout already stamped the root
|
|
467
470
|
// attributes for the first paint. These effects keep them in step afterwards:
|
|
468
471
|
// re-running `applyAppearance` whenever a preference signal changes is what
|
|
469
|
-
// makes the settings page feel immediate
|
|
472
|
+
// makes the settings page feel immediate - the shell restyles under the user
|
|
470
473
|
// while they are still looking at the picker, with no reload and no navigation.
|
|
471
474
|
// ---------------------------------------------------------------------------
|
|
472
475
|
|
|
@@ -487,7 +490,7 @@ effect(() => {
|
|
|
487
490
|
function applySectionVisibility() {
|
|
488
491
|
// The signal is read BEFORE the panes are looked up, and that order is the
|
|
489
492
|
// whole point. Read after an early return, it never gets tracked on the first
|
|
490
|
-
// pass
|
|
493
|
+
// pass - the panes are not mounted when effects first run - so the effect
|
|
491
494
|
// ends up with no dependency and never runs again. The toggle then appears to
|
|
492
495
|
// do nothing until a reload.
|
|
493
496
|
const hidden = appearance.hiddenSections()
|
|
@@ -501,7 +504,7 @@ function applySectionVisibility() {
|
|
|
501
504
|
|
|
502
505
|
// The effect covers later toggles. It cannot cover the FIRST paint on its own:
|
|
503
506
|
// effects run before the layout's refs are populated, so the very first pass
|
|
504
|
-
// finds no panes, and on a fresh load the preference never changes afterwards
|
|
507
|
+
// finds no panes, and on a fresh load the preference never changes afterwards -
|
|
505
508
|
// leaving hidden sections visible until something else happens to touch the
|
|
506
509
|
// signal. `onMount` below runs it again once the panes exist.
|
|
507
510
|
effect(applySectionVisibility)
|
|
@@ -115,6 +115,7 @@ export const ciStore = defineStore('ci', () => {
|
|
|
115
115
|
// Lazy-loaded on first view of an org's tab.
|
|
116
116
|
const runnerHistoryByOrg = state<Record<string, Array<{ queued: number, sampledAt: string }>>>({})
|
|
117
117
|
const loadingRunnerHistory = state<Record<string, boolean>>({})
|
|
118
|
+
const runnerHistoryErrors = state<Record<string, string>>({})
|
|
118
119
|
|
|
119
120
|
const reposForTab = derived(() => {
|
|
120
121
|
const tab = activeTab()
|
|
@@ -227,33 +228,37 @@ export const ciStore = defineStore('ci', () => {
|
|
|
227
228
|
drilldownError.set(null)
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
async function loadRunnerHistory(org: string): Promise<void> {
|
|
231
|
+
async function loadRunnerHistory(org: string, force = false): Promise<void> {
|
|
231
232
|
// Already loaded? Don't re-fetch — runner samples have a 30s
|
|
232
233
|
// refresh cadence anyway. If the user wants a fresh view they
|
|
233
234
|
// can reload the page.
|
|
234
|
-
if (runnerHistoryByOrg()[org] !== undefined) return
|
|
235
|
+
if (!force && runnerHistoryByOrg()[org] !== undefined) return
|
|
235
236
|
if (loadingRunnerHistory()[org]) return
|
|
236
237
|
|
|
237
238
|
loadingRunnerHistory.set({ ...loadingRunnerHistory(), [org]: true })
|
|
239
|
+
runnerHistoryErrors.set({ ...runnerHistoryErrors(), [org]: '' })
|
|
238
240
|
try {
|
|
239
241
|
const data = await dashboardApi<{
|
|
240
242
|
samples?: Array<{ queued: number, sampledAt: string }>
|
|
241
243
|
error?: string
|
|
242
244
|
disabled?: boolean
|
|
243
245
|
}>(`/api/dashboard/ci/runner-history?org=${encodeURIComponent(org)}`)
|
|
244
|
-
if (data.disabled
|
|
246
|
+
if (data.disabled) {
|
|
245
247
|
runnerHistoryByOrg.set({ ...runnerHistoryByOrg(), [org]: [] })
|
|
246
248
|
return
|
|
247
249
|
}
|
|
250
|
+
if (data.error)
|
|
251
|
+
throw new Error(data.error)
|
|
248
252
|
runnerHistoryByOrg.set({
|
|
249
253
|
...runnerHistoryByOrg(),
|
|
250
254
|
[org]: (data.samples ?? []).map(s => ({ queued: s.queued, sampledAt: s.sampledAt })),
|
|
251
255
|
})
|
|
252
256
|
}
|
|
253
|
-
catch {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
+
catch (e) {
|
|
258
|
+
runnerHistoryErrors.set({
|
|
259
|
+
...runnerHistoryErrors(),
|
|
260
|
+
[org]: e instanceof Error ? e.message : 'Runner history could not be loaded.',
|
|
261
|
+
})
|
|
257
262
|
}
|
|
258
263
|
finally {
|
|
259
264
|
loadingRunnerHistory.set({ ...loadingRunnerHistory(), [org]: false })
|
|
@@ -327,6 +332,7 @@ export const ciStore = defineStore('ci', () => {
|
|
|
327
332
|
// Runner-pressure history (#1850)
|
|
328
333
|
runnerHistoryByOrg,
|
|
329
334
|
loadingRunnerHistory,
|
|
335
|
+
runnerHistoryErrors,
|
|
330
336
|
loadRunnerHistory,
|
|
331
337
|
}
|
|
332
338
|
}, {
|
|
@@ -41,8 +41,8 @@ interface CardRecord {
|
|
|
41
41
|
archived: boolean
|
|
42
42
|
createdAt: string | null
|
|
43
43
|
updatedAt: string | null
|
|
44
|
-
// Optional pivot-derived fields. BoardShowAction populates them
|
|
45
|
-
//
|
|
44
|
+
// Optional pivot-derived fields. BoardShowAction populates them;
|
|
45
|
+
// CardStoreAction's optimistic insert leaves them empty.
|
|
46
46
|
labels?: CardLabel[]
|
|
47
47
|
assignees?: CardAssignee[]
|
|
48
48
|
}
|
|
@@ -89,10 +89,9 @@ interface BoardDetail {
|
|
|
89
89
|
/**
|
|
90
90
|
* Kanban dashboard store (stacksjs/stacks#1846).
|
|
91
91
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* in Phase 2 / Phase 3.
|
|
92
|
+
* Lists boards for the index page and fetches a board for its detail page.
|
|
93
|
+
* Drag-and-drop reorder, write mutations, labels, assignees, and comments
|
|
94
|
+
* share this store so optimistic updates and rollback have one owner.
|
|
96
95
|
*
|
|
97
96
|
* Persistence: only the last-viewed board id is cached in
|
|
98
97
|
* sessionStorage so the user lands back where they left off when they
|
|
@@ -110,7 +109,7 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
110
109
|
const error = state<string | null>(null)
|
|
111
110
|
const errorBoard = state<string | null>(null)
|
|
112
111
|
|
|
113
|
-
//
|
|
112
|
+
// Card detail modal state. Holding card + comments here
|
|
114
113
|
// (instead of one of column.cards) keeps the modal independent of
|
|
115
114
|
// its source: opening from a card click on the board uses the
|
|
116
115
|
// already-loaded preview; refresh-mid-modal hits /cards/:id and
|
|
@@ -174,14 +173,14 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
174
173
|
}
|
|
175
174
|
}
|
|
176
175
|
|
|
177
|
-
//
|
|
176
|
+
// Mutations
|
|
178
177
|
//
|
|
179
178
|
// Every mutation follows the same shape:
|
|
180
179
|
// 1. Apply optimistically — update the local store immediately so
|
|
181
180
|
// the UI feels snappy.
|
|
182
181
|
// 2. Fire the network request.
|
|
183
182
|
// 3. On failure, restore the pre-mutation snapshot + set an error
|
|
184
|
-
// message.
|
|
183
|
+
// message. The dashboard toast helper handles the rollback path
|
|
185
184
|
// so the user actually sees the failure.
|
|
186
185
|
//
|
|
187
186
|
// The mutations return the new entity (or null on failure) so
|
|
@@ -360,7 +359,7 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
360
359
|
}
|
|
361
360
|
}
|
|
362
361
|
|
|
363
|
-
//
|
|
362
|
+
// Card detail mutations
|
|
364
363
|
|
|
365
364
|
/**
|
|
366
365
|
* Helper: project a CardRecord (board view) onto a partial
|
|
@@ -584,6 +583,43 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
584
583
|
}
|
|
585
584
|
}
|
|
586
585
|
|
|
586
|
+
async function updateComment(commentId: number, body: string): Promise<boolean> {
|
|
587
|
+
const oc = openCard()
|
|
588
|
+
const trimmed = body.trim()
|
|
589
|
+
if (!oc || !trimmed) return false
|
|
590
|
+
const snapshot = oc.comments
|
|
591
|
+
const existing = snapshot.find(comment => comment.id === commentId)
|
|
592
|
+
if (!existing || existing.body === trimmed) return true
|
|
593
|
+
|
|
594
|
+
patchOpenCard({
|
|
595
|
+
comments: snapshot.map(comment => comment.id === commentId
|
|
596
|
+
? { ...comment, body: trimmed }
|
|
597
|
+
: comment),
|
|
598
|
+
})
|
|
599
|
+
try {
|
|
600
|
+
const data = await dashboardApi<{ comment?: CardComment, error?: string }>(`/api/dashboard/kanban/comments/${commentId}`, {
|
|
601
|
+
method: 'PATCH',
|
|
602
|
+
body: { body: trimmed },
|
|
603
|
+
})
|
|
604
|
+
if (data.error || !data.comment)
|
|
605
|
+
throw new Error(data.error ?? 'Comment update failed')
|
|
606
|
+
const current = openCard()
|
|
607
|
+
if (current) {
|
|
608
|
+
patchOpenCard({
|
|
609
|
+
comments: current.comments.map(comment => comment.id === commentId
|
|
610
|
+
? data.comment as CardComment
|
|
611
|
+
: comment),
|
|
612
|
+
})
|
|
613
|
+
}
|
|
614
|
+
return true
|
|
615
|
+
}
|
|
616
|
+
catch (error) {
|
|
617
|
+
patchOpenCard({ comments: snapshot })
|
|
618
|
+
errorCard.set(error instanceof Error ? error.message : String(error))
|
|
619
|
+
return false
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
587
623
|
// ─── Label CRUD (board scope) ──────────────────────────────────────
|
|
588
624
|
|
|
589
625
|
async function createLabel(input: { boardId: number, name: string, color?: string }): Promise<LabelRecord | null> {
|
|
@@ -652,7 +688,7 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
652
688
|
deleteCard,
|
|
653
689
|
reorderCards,
|
|
654
690
|
reorderColumns,
|
|
655
|
-
//
|
|
691
|
+
// Card detail
|
|
656
692
|
openCard,
|
|
657
693
|
loadingCard,
|
|
658
694
|
errorCard,
|
|
@@ -665,6 +701,7 @@ export const kanbanStore = defineStore('kanban', () => {
|
|
|
665
701
|
syncCardLabels,
|
|
666
702
|
syncCardAssignees,
|
|
667
703
|
addComment,
|
|
704
|
+
updateComment,
|
|
668
705
|
deleteComment,
|
|
669
706
|
createLabel,
|
|
670
707
|
deleteLabel,
|