@stacksjs/defaults 0.70.354 → 0.70.356
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 +73 -16
- package/app/Actions/ContactAction.ts +2 -2
- package/app/Actions/Dashboard/Analytics/EventAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/MarketingAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/SalesAnalyticsAction.ts +2 -3
- package/app/Actions/Dashboard/Analytics/WebAnalyticsAction.ts +9 -3
- package/app/Actions/Dashboard/Auth/MeAction.ts +12 -14
- package/app/Actions/Dashboard/Buddy/BuddyChatAction.ts +2 -3
- 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/Cloud/CloudIndexAction.ts +8 -1
- package/app/Actions/Dashboard/Cloud/ServerlessIndexAction.ts +15 -8
- package/app/Actions/Dashboard/Commerce/CommerceCategoriesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceCouponsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceCustomersAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceDashboardAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceDeliveryAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceGiftCardsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceOrdersAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePaymentsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePosAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePosCheckoutAction.ts +76 -61
- package/app/Actions/Dashboard/Commerce/CommercePrintDevicesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommercePrintLogsAction.ts +4 -7
- package/app/Actions/Dashboard/Commerce/CommerceProductDetailAction.ts +4 -4
- package/app/Actions/Dashboard/Commerce/CommerceProductsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/CommerceTaxesAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DeliveryRouteIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DigitalDeliveryIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/DriverIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/LicenseKeyIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/LicenseKeyOptionsAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ManufacturerIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/PaymentRefundAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/ProductUnitDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/Commerce/ProductUnitIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ProductVariantIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ProductWaitlistIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/RestaurantWaitlistIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ReviewIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingMethodIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingRateIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/ShippingZoneIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Commerce/TaxRateDefaultAction.ts +1 -0
- package/app/Actions/Dashboard/DashboardActivityAction.ts +2 -5
- package/app/Actions/Dashboard/Data/ActivityIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/SubscriberIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/TeamIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Data/UserIndexAction.ts +2 -5
- package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +26 -21
- package/app/Actions/Dashboard/Deployments/GetAverageDeploymentTime.ts +8 -2
- package/app/Actions/Dashboard/Deployments/GetDeployScript.ts +3 -6
- package/app/Actions/Dashboard/Deployments/GetDeployment.ts +5 -5
- package/app/Actions/Dashboard/Deployments/GetDeploymentCount.ts +7 -1
- package/app/Actions/Dashboard/Deployments/GetDeploymentLiveTerminalOutput.ts +10 -9
- package/app/Actions/Dashboard/Deployments/GetDeployments.ts +2 -4
- package/app/Actions/Dashboard/Deployments/GetRecentDeployments.ts +8 -2
- package/app/Actions/Dashboard/Deployments/UpdateDeployScript.ts +21 -7
- package/app/Actions/Dashboard/Email/CapturedMailIndexAction.ts +2 -4
- package/app/Actions/Dashboard/Email/CapturedMailShowAction.ts +3 -4
- 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/InsightsAction.ts +15 -14
- package/app/Actions/Dashboard/Infrastructure/LogIndexAction.ts +4 -11
- package/app/Actions/Dashboard/Infrastructure/ServerShowAction.ts +3 -6
- package/app/Actions/Dashboard/Jobs/JobIndexAction.ts +9 -18
- package/app/Actions/Dashboard/Jobs/JobRetryAction.ts +4 -3
- package/app/Actions/Dashboard/Jobs/JobShowAction.ts +16 -11
- package/app/Actions/Dashboard/Jobs/JobStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Kanban/BoardDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/BoardShowAction.ts +11 -19
- package/app/Actions/Dashboard/Kanban/BoardStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/BoardUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/BoardsIndexAction.ts +3 -5
- package/app/Actions/Dashboard/Kanban/BoardsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/CardAssigneesSyncAction.ts +13 -12
- package/app/Actions/Dashboard/Kanban/CardCommentDestroyAction.ts +12 -15
- package/app/Actions/Dashboard/Kanban/CardCommentStoreAction.ts +21 -46
- package/app/Actions/Dashboard/Kanban/CardCommentUpdateAction.ts +43 -0
- package/app/Actions/Dashboard/Kanban/CardDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/CardLabelsSyncAction.ts +8 -9
- package/app/Actions/Dashboard/Kanban/CardShowAction.ts +6 -7
- package/app/Actions/Dashboard/Kanban/CardStoreAction.ts +9 -9
- package/app/Actions/Dashboard/Kanban/CardUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/CardsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/ColumnDestroyAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/ColumnStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/ColumnUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/ColumnsReorderAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/LabelDestroyAction.ts +6 -7
- package/app/Actions/Dashboard/Kanban/LabelStoreAction.ts +6 -6
- package/app/Actions/Dashboard/Kanban/LabelUpdateAction.ts +7 -8
- package/app/Actions/Dashboard/Kanban/UsersListAction.ts +3 -4
- 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/Kanban/kanban-response.ts +5 -0
- package/app/Actions/Dashboard/Library/CreateComponent.ts +15 -6
- package/app/Actions/Dashboard/Library/CreateFunction.ts +15 -6
- package/app/Actions/Dashboard/Library/DependencyIndexAction.ts +88 -77
- package/app/Actions/Dashboard/Library/GetAverageReleaseTime.ts +21 -15
- package/app/Actions/Dashboard/Library/GetComponents.ts +14 -7
- package/app/Actions/Dashboard/Library/GetDownloadCount.ts +14 -5
- package/app/Actions/Dashboard/Library/GetFunctions.ts +12 -5
- package/app/Actions/Dashboard/Library/GetReleaseCount.ts +14 -5
- package/app/Actions/Dashboard/Library/GetReleases.ts +17 -10
- package/app/Actions/Dashboard/Library/PackageIndexAction.ts +14 -7
- package/app/Actions/Dashboard/Models/ModelDestroyAction.ts +11 -8
- package/app/Actions/Dashboard/Models/ModelShowAction.ts +97 -192
- package/app/Actions/Dashboard/Models/ModelStoreAction.ts +14 -12
- package/app/Actions/Dashboard/Models/ModelUpdateAction.ts +18 -16
- package/app/Actions/Dashboard/Models/ModelsIndexAction.ts +50 -19
- package/app/Actions/Dashboard/Models/model-api.ts +27 -0
- package/app/Actions/Dashboard/Models/model-write.ts +22 -15
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryHistoryAction.ts +49 -40
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryIndexAction.ts +20 -10
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryOverviewAction.ts +96 -84
- package/app/Actions/Dashboard/Notifications/NotificationDeliveryRetryAction.ts +34 -21
- package/app/Actions/Dashboard/Queries/QueryDashboardAction.ts +2 -4
- package/app/Actions/Dashboard/Queries/QueryShowAction.ts +3 -3
- package/app/Actions/Dashboard/Queue/QueueStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Queue/QueueWorkersAction.ts +2 -4
- package/app/Actions/Dashboard/Rbac/PermissionDestroyAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/PermissionStoreAction.ts +5 -4
- package/app/Actions/Dashboard/Rbac/PermissionsIndexAction.ts +2 -3
- package/app/Actions/Dashboard/Rbac/RoleDestroyAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/RolePermissionsShowAction.ts +6 -6
- package/app/Actions/Dashboard/Rbac/RolePermissionsSyncAction.ts +6 -9
- package/app/Actions/Dashboard/Rbac/RoleStoreAction.ts +5 -4
- package/app/Actions/Dashboard/Rbac/RolesIndexAction.ts +4 -6
- package/app/Actions/Dashboard/Rbac/UserRolesShowAction.ts +6 -7
- package/app/Actions/Dashboard/Rbac/UserRolesSyncAction.ts +6 -13
- package/app/Actions/Dashboard/Rbac/UsersListAction.ts +2 -3
- package/app/Actions/Dashboard/Rbac/rbac-response.ts +10 -0
- package/app/Actions/Dashboard/Realtime/RealtimeStatsAction.ts +2 -4
- package/app/Actions/Dashboard/Search/GlobalSearchAction.ts +69 -55
- package/app/Actions/Dashboard/Teams/TeamInvitationDestroyAction.ts +34 -16
- package/app/Actions/Dashboard/Teams/TeamInvitationResendAction.ts +52 -29
- package/app/Actions/Dashboard/Teams/TeamInviteAction.ts +104 -73
- package/app/Actions/Dashboard/Teams/TeamMemberDestroyAction.ts +36 -17
- package/app/Actions/Dashboard/Teams/TeamMemberUpdateAction.ts +41 -28
- package/app/Actions/Dashboard/Teams/TeamPeopleIndexAction.ts +71 -58
- package/app/Actions/Dashboard/Teams/team-invitation-delivery.ts +29 -7
- package/app/Actions/Dashboard/Teams/team-member-count.ts +4 -2
- package/app/Actions/Dashboard/Teams/team-records.test.ts +2 -0
- package/app/Actions/Dashboard/Teams/team-records.ts +2 -0
- package/app/Actions/Dashboard/dashboard-request.ts +13 -0
- package/app/Actions/Dashboard/dashboard-response.ts +16 -0
- package/app/Actions/SendWelcomeEmail.ts +1 -1
- package/app/Actions/Teams/AcceptInvitationAction.ts +74 -51
- package/app/Actions/Teams/ShowInvitationAction.ts +21 -14
- package/app/Actions/Teams/team-response.ts +23 -0
- package/app/Mail/OrderConfirmation.ts +1 -1
- package/app/Mail/SubscriptionConfirmation.ts +1 -1
- package/app/Mail/TeamInvitation.ts +1 -1
- package/app/Models/CardComment.ts +3 -4
- package/app/Models/EmailIdempotency.ts +64 -0
- package/app/Models/EmailSuppression.ts +52 -0
- package/app/Models/EmailWebhookEvent.ts +51 -0
- package/app/Models/TeamInvitation.ts +13 -0
- package/functions/dashboard-api.ts +51 -10
- package/functions/data.ts +0 -1
- package/functions/inbox.test.ts +2 -0
- package/functions/inbox.ts +27 -2
- package/functions/jobs.ts +0 -1
- 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/Data/DataRecords.stx +0 -4
- 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/Jobs/JobDetail.stx +3 -2
- 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/Models/ModelRecordsDashboard.stx +7 -13
- package/resources/components/Dashboard/Models/ModelsOverview.stx +1 -1
- 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 +54 -0
- package/views/dashboard/layouts/default.stx +19 -16
- package/views/dashboard/stores/auth.ts +15 -14
- package/views/dashboard/stores/ci.ts +13 -7
- package/views/dashboard/stores/kanban.ts +48 -11
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* `DELETE /api/dashboard/kanban/columns/:id
|
|
7
|
+
* `DELETE /api/dashboard/kanban/columns/:id`.
|
|
7
8
|
*
|
|
8
9
|
* Hard-deletes the column and cascade-cleans every card inside it
|
|
9
10
|
* (plus the pivot rows referencing those cards). Same convention as
|
|
@@ -19,9 +20,8 @@ export default new Action({
|
|
|
19
20
|
description: 'Hard-deletes a column and its cards.',
|
|
20
21
|
method: 'DELETE',
|
|
21
22
|
apiResponse: true,
|
|
22
|
-
async handle(request) {
|
|
23
|
-
const
|
|
24
|
-
const id = Number(rawId)
|
|
23
|
+
async handle(request: RequestInstance) {
|
|
24
|
+
const id = Number(request.getParam('id'))
|
|
25
25
|
if (!Number.isFinite(id) || id <= 0) {
|
|
26
26
|
return kanbanError('Invalid column id', 400)
|
|
27
27
|
}
|
|
@@ -37,7 +37,7 @@ export default new Action({
|
|
|
37
37
|
'DELETE FROM card_assignees WHERE card_id IN (SELECT id FROM cards WHERE column_id = ?)',
|
|
38
38
|
[id],
|
|
39
39
|
).execute()
|
|
40
|
-
// Card comments
|
|
40
|
+
// Card comments are card-scoped children.
|
|
41
41
|
await qb.unsafe(
|
|
42
42
|
'DELETE FROM card_comments WHERE card_id IN (SELECT id FROM cards WHERE column_id = ?)',
|
|
43
43
|
[id],
|
|
@@ -48,8 +48,7 @@ export default new Action({
|
|
|
48
48
|
return { deleted: true, id }
|
|
49
49
|
}
|
|
50
50
|
catch (err) {
|
|
51
|
-
|
|
52
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
51
|
+
return kanbanActionError(err, 'ColumnDestroyAction')
|
|
53
52
|
}
|
|
54
53
|
},
|
|
55
54
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
4
|
import { Board, BoardColumn } from '@stacksjs/orm'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface ColumnInput {
|
|
7
8
|
boardId?: unknown
|
|
@@ -11,7 +12,7 @@ interface ColumnInput {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* `POST /api/dashboard/kanban/columns
|
|
15
|
+
* `POST /api/dashboard/kanban/columns`.
|
|
15
16
|
*
|
|
16
17
|
* Appends a new column to the end of the target board. Validates that
|
|
17
18
|
* `boardId` points at a real board so a typo doesn't silently create
|
|
@@ -22,8 +23,8 @@ export default new Action({
|
|
|
22
23
|
description: 'Creates a new column on a board.',
|
|
23
24
|
method: 'POST',
|
|
24
25
|
apiResponse: true,
|
|
25
|
-
async handle(request) {
|
|
26
|
-
const body = (
|
|
26
|
+
async handle(request: RequestInstance<ColumnInput>) {
|
|
27
|
+
const body = request.all()
|
|
27
28
|
|
|
28
29
|
const boardId = Number(body.boardId)
|
|
29
30
|
if (!Number.isFinite(boardId) || boardId <= 0) {
|
|
@@ -74,8 +75,7 @@ export default new Action({
|
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
catch (err) {
|
|
77
|
-
|
|
78
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
78
|
+
return kanbanActionError(err, 'ColumnStoreAction')
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { BoardColumn } from '@stacksjs/orm'
|
|
3
4
|
import { modelNullableString, modelNumber, modelString, modelValue, refreshModel } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface ColumnInput {
|
|
7
8
|
name?: unknown
|
|
@@ -10,7 +11,7 @@ interface ColumnInput {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* `PATCH /api/dashboard/kanban/columns/:id
|
|
14
|
+
* `PATCH /api/dashboard/kanban/columns/:id`.
|
|
14
15
|
*
|
|
15
16
|
* Partial update for name / color / card limit. `position` and
|
|
16
17
|
* `boardId` are NOT updatable here — moving a column between boards
|
|
@@ -23,14 +24,13 @@ export default new Action({
|
|
|
23
24
|
description: 'Partial update of a board column.',
|
|
24
25
|
method: 'PATCH',
|
|
25
26
|
apiResponse: true,
|
|
26
|
-
async handle(request) {
|
|
27
|
-
const
|
|
28
|
-
const id = Number(rawId)
|
|
27
|
+
async handle(request: RequestInstance<ColumnInput>) {
|
|
28
|
+
const id = Number(request.getParam('id'))
|
|
29
29
|
if (!Number.isFinite(id) || id <= 0) {
|
|
30
30
|
return kanbanError('Invalid column id', 400)
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const body = (
|
|
33
|
+
const body = request.all()
|
|
34
34
|
const set: Record<string, unknown> = {}
|
|
35
35
|
|
|
36
36
|
if (typeof body.name === 'string') {
|
|
@@ -80,8 +80,7 @@ export default new Action({
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
catch (err) {
|
|
83
|
-
|
|
84
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
83
|
+
return kanbanActionError(err, 'ColumnUpdateAction')
|
|
85
84
|
}
|
|
86
85
|
},
|
|
87
86
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
interface ReorderInput {
|
|
6
7
|
boardId?: unknown
|
|
@@ -8,7 +9,7 @@ interface ReorderInput {
|
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
|
-
* `POST /api/dashboard/kanban/columns/reorder
|
|
12
|
+
* `POST /api/dashboard/kanban/columns/reorder`.
|
|
12
13
|
*
|
|
13
14
|
* Bulk rewrite column positions for a board. The submitted `order`
|
|
14
15
|
* is an array of column ids in their new display order; index becomes
|
|
@@ -27,8 +28,8 @@ export default new Action({
|
|
|
27
28
|
description: 'Bulk-rewrite `position` on a board\'s columns.',
|
|
28
29
|
method: 'POST',
|
|
29
30
|
apiResponse: true,
|
|
30
|
-
async handle(request) {
|
|
31
|
-
const body = (
|
|
31
|
+
async handle(request: RequestInstance<ReorderInput>) {
|
|
32
|
+
const body = request.all()
|
|
32
33
|
|
|
33
34
|
const boardId = Number(body.boardId)
|
|
34
35
|
if (!Number.isFinite(boardId) || boardId <= 0) {
|
|
@@ -74,8 +75,7 @@ export default new Action({
|
|
|
74
75
|
return { reordered: ids.length }
|
|
75
76
|
}
|
|
76
77
|
catch (err) {
|
|
77
|
-
|
|
78
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
78
|
+
return kanbanActionError(err, 'ColumnsReorderAction')
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
81
|
})
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* `DELETE /api/dashboard/kanban/labels/:id
|
|
7
|
+
* `DELETE /api/dashboard/kanban/labels/:id`.
|
|
7
8
|
*
|
|
8
9
|
* Removes a label and detaches it from every card that carries it.
|
|
9
10
|
* The label row goes from the `labels` table; every pivot row in
|
|
@@ -15,9 +16,8 @@ export default new Action({
|
|
|
15
16
|
description: 'Hard-deletes a label and its card_labels pivot rows.',
|
|
16
17
|
method: 'DELETE',
|
|
17
18
|
apiResponse: true,
|
|
18
|
-
async handle(request) {
|
|
19
|
-
const
|
|
20
|
-
const id = Number(rawId)
|
|
19
|
+
async handle(request: RequestInstance) {
|
|
20
|
+
const id = Number(request.getParam('id'))
|
|
21
21
|
if (!Number.isFinite(id) || id <= 0)
|
|
22
22
|
return kanbanError('Invalid label id', 400)
|
|
23
23
|
|
|
@@ -30,8 +30,7 @@ export default new Action({
|
|
|
30
30
|
return { deleted: true, id }
|
|
31
31
|
}
|
|
32
32
|
catch (err) {
|
|
33
|
-
|
|
34
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
33
|
+
return kanbanActionError(err, 'LabelDestroyAction')
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
36
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { Board, Label } from '@stacksjs/orm'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
interface LabelInput {
|
|
6
7
|
boardId?: unknown
|
|
@@ -9,7 +10,7 @@ interface LabelInput {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* `POST /api/dashboard/kanban/labels
|
|
13
|
+
* `POST /api/dashboard/kanban/labels`.
|
|
13
14
|
*
|
|
14
15
|
* Creates a label scoped to a board. Labels are unique per
|
|
15
16
|
* `(board_id, name)` — the migration enforces this via a unique
|
|
@@ -21,8 +22,8 @@ export default new Action({
|
|
|
21
22
|
description: 'Creates a label on a board.',
|
|
22
23
|
method: 'POST',
|
|
23
24
|
apiResponse: true,
|
|
24
|
-
async handle(request) {
|
|
25
|
-
const body = (
|
|
25
|
+
async handle(request: RequestInstance<LabelInput>) {
|
|
26
|
+
const body = request.all()
|
|
26
27
|
|
|
27
28
|
const boardId = Number(body.boardId)
|
|
28
29
|
if (!Number.isFinite(boardId) || boardId <= 0) {
|
|
@@ -50,8 +51,7 @@ export default new Action({
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
catch (err) {
|
|
53
|
-
|
|
54
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
54
|
+
return kanbanActionError(err, 'LabelStoreAction')
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
57
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { Label } from '@stacksjs/orm'
|
|
3
4
|
import { modelNumber, modelString, refreshModel } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface LabelInput {
|
|
7
8
|
name?: unknown
|
|
@@ -9,7 +10,7 @@ interface LabelInput {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
* `PATCH /api/dashboard/kanban/labels/:id
|
|
13
|
+
* `PATCH /api/dashboard/kanban/labels/:id`.
|
|
13
14
|
*
|
|
14
15
|
* Partial update for label name + color. `board_id` is not movable —
|
|
15
16
|
* a label on board A renaming to "Bug" must not collide with a
|
|
@@ -21,14 +22,13 @@ export default new Action({
|
|
|
21
22
|
description: 'Partial update of a label name / color.',
|
|
22
23
|
method: 'PATCH',
|
|
23
24
|
apiResponse: true,
|
|
24
|
-
async handle(request) {
|
|
25
|
-
const
|
|
26
|
-
const id = Number(rawId)
|
|
25
|
+
async handle(request: RequestInstance<LabelInput>) {
|
|
26
|
+
const id = Number(request.getParam('id'))
|
|
27
27
|
if (!Number.isFinite(id) || id <= 0) {
|
|
28
28
|
return kanbanError('Invalid label id', 400)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const body = (
|
|
31
|
+
const body = request.all()
|
|
32
32
|
const set: Record<string, unknown> = {}
|
|
33
33
|
let renamingTo: string | null = null
|
|
34
34
|
|
|
@@ -74,8 +74,7 @@ export default new Action({
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
catch (err) {
|
|
77
|
-
|
|
78
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
77
|
+
return kanbanActionError(err, 'LabelUpdateAction')
|
|
79
78
|
}
|
|
80
79
|
},
|
|
81
80
|
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
3
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* `GET /api/dashboard/kanban/users
|
|
6
|
+
* `GET /api/dashboard/kanban/users`.
|
|
7
7
|
*
|
|
8
8
|
* Lightweight user list for the assignee picker on the card detail
|
|
9
9
|
* modal. Returns `{ id, name, email }` only — no roles, no profile,
|
|
@@ -30,8 +30,7 @@ export default new Action({
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
catch (err) {
|
|
33
|
-
|
|
34
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
33
|
+
return kanbanActionError(err, 'UsersListAction')
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
36
|
})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { modelNullableNumber, modelNullableString, modelNumber, modelString } from './kanban-model'
|
|
2
|
+
|
|
3
|
+
export function cardCommentResponse(record: object, author?: object | null) {
|
|
4
|
+
return {
|
|
5
|
+
id: modelNumber(record, 'id'),
|
|
6
|
+
uuid: modelNullableString(record, 'uuid'),
|
|
7
|
+
userId: modelNullableNumber(record, 'userId', 'user_id'),
|
|
8
|
+
body: modelString(record, 'body'),
|
|
9
|
+
authorName: author ? modelNullableString(author, 'name') : null,
|
|
10
|
+
authorEmail: author ? modelNullableString(author, 'email') : null,
|
|
11
|
+
createdAt: modelNullableString(record, 'createdAt', 'created_at'),
|
|
12
|
+
updatedAt: modelNullableString(record, 'updatedAt', 'updated_at'),
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { cardCommentResponse } from './kanban-comment'
|
|
2
3
|
import {
|
|
3
4
|
modelBoolean,
|
|
5
|
+
modelNullableNumber,
|
|
4
6
|
modelNullableString,
|
|
5
7
|
modelNumber,
|
|
6
8
|
modelString,
|
|
@@ -31,12 +33,26 @@ describe('kanban model response values', () => {
|
|
|
31
33
|
})
|
|
32
34
|
|
|
33
35
|
test('normalizes nullable strings and persisted booleans', () => {
|
|
36
|
+
expect(modelNullableNumber(record({ value: null }), 'value')).toBeNull()
|
|
37
|
+
expect(modelNullableNumber(record({ value: '42' }), 'value')).toBe(42)
|
|
34
38
|
expect(modelNullableString(record({ value: null }), 'value')).toBeNull()
|
|
35
39
|
expect(modelBoolean(record({ enabled: 1 }), 'enabled')).toBe(true)
|
|
36
40
|
expect(modelBoolean(record({ enabled: 'true' }), 'enabled')).toBe(true)
|
|
37
41
|
expect(modelBoolean(record({ enabled: 0 }), 'enabled')).toBe(false)
|
|
38
42
|
})
|
|
39
43
|
|
|
44
|
+
test('reads raw query rows as well as ORM model records', () => {
|
|
45
|
+
const row = {
|
|
46
|
+
board_id: 4,
|
|
47
|
+
archived: 1,
|
|
48
|
+
created_at: '2026-08-10 12:00:00',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
expect(modelNumber(row, 'boardId', 'board_id')).toBe(4)
|
|
52
|
+
expect(modelBoolean(row, 'archived')).toBe(true)
|
|
53
|
+
expect(modelString(row, 'createdAt', 'created_at')).toBe('2026-08-10 12:00:00')
|
|
54
|
+
})
|
|
55
|
+
|
|
40
56
|
test('refreshes generated database values after a write', async () => {
|
|
41
57
|
const stale = {
|
|
42
58
|
...record({ updated_at: null }),
|
|
@@ -46,4 +62,24 @@ describe('kanban model response values', () => {
|
|
|
46
62
|
const refreshed = await refreshModel(stale)
|
|
47
63
|
expect(modelString(refreshed, 'updatedAt', 'updated_at')).toBe('2026-07-31 04:00:00')
|
|
48
64
|
})
|
|
65
|
+
|
|
66
|
+
test('serializes model-backed comments with their author', () => {
|
|
67
|
+
expect(cardCommentResponse(record({
|
|
68
|
+
id: 7,
|
|
69
|
+
uuid: 'comment-uuid',
|
|
70
|
+
userId: 3,
|
|
71
|
+
body: 'Updated note',
|
|
72
|
+
createdAt: '2026-08-10 20:00:00',
|
|
73
|
+
updatedAt: '2026-08-10 21:00:00',
|
|
74
|
+
}), record({ name: 'Chris', email: 'chris@example.com' }))).toEqual({
|
|
75
|
+
id: 7,
|
|
76
|
+
uuid: 'comment-uuid',
|
|
77
|
+
userId: 3,
|
|
78
|
+
body: 'Updated note',
|
|
79
|
+
authorName: 'Chris',
|
|
80
|
+
authorEmail: 'chris@example.com',
|
|
81
|
+
createdAt: '2026-08-10 20:00:00',
|
|
82
|
+
updatedAt: '2026-08-10 21:00:00',
|
|
83
|
+
})
|
|
84
|
+
})
|
|
49
85
|
})
|
|
@@ -10,28 +10,35 @@ export async function refreshModel(record: RefreshableKanbanModelRecord): Promis
|
|
|
10
10
|
return await record.fresh() ?? record
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function modelValue(record:
|
|
14
|
-
const
|
|
13
|
+
export function modelValue(record: object, camelKey: string, snakeKey = camelKey): unknown {
|
|
14
|
+
const model = record as Partial<KanbanModelRecord>
|
|
15
|
+
const row = record as Record<string, unknown>
|
|
16
|
+
const camelValue = typeof model.get === 'function' ? model.get(camelKey) : row[camelKey]
|
|
15
17
|
if (camelValue !== undefined)
|
|
16
18
|
return camelValue
|
|
17
|
-
return
|
|
19
|
+
return typeof model.get === 'function' ? model.get(snakeKey) : row[snakeKey]
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
export function modelNumber(record:
|
|
22
|
+
export function modelNumber(record: object, camelKey: string, snakeKey = camelKey): number {
|
|
21
23
|
return Number(modelValue(record, camelKey, snakeKey))
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
export function
|
|
26
|
+
export function modelNullableNumber(record: object, camelKey: string, snakeKey = camelKey): number | null {
|
|
27
|
+
const value = modelValue(record, camelKey, snakeKey)
|
|
28
|
+
return value === undefined || value === null ? null : Number(value)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function modelString(record: object, camelKey: string, snakeKey = camelKey): string {
|
|
25
32
|
const value = modelValue(record, camelKey, snakeKey)
|
|
26
33
|
return value === undefined || value === null ? '' : String(value)
|
|
27
34
|
}
|
|
28
35
|
|
|
29
|
-
export function modelNullableString(record:
|
|
36
|
+
export function modelNullableString(record: object, camelKey: string, snakeKey = camelKey): string | null {
|
|
30
37
|
const value = modelValue(record, camelKey, snakeKey)
|
|
31
38
|
return value === undefined || value === null ? null : String(value)
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
export function modelBoolean(record:
|
|
41
|
+
export function modelBoolean(record: object, camelKey: string, snakeKey = camelKey): boolean {
|
|
35
42
|
const value = modelValue(record, camelKey, snakeKey)
|
|
36
43
|
return value === true || value === 1 || value === '1' || value === 'true'
|
|
37
44
|
}
|
|
@@ -3,3 +3,8 @@ import { response } from '@stacksjs/router'
|
|
|
3
3
|
export function kanbanError(error: string, status: number): Response {
|
|
4
4
|
return response.json({ error }, status)
|
|
5
5
|
}
|
|
6
|
+
|
|
7
|
+
export function kanbanActionError(error: unknown, action: string): Response {
|
|
8
|
+
console.error(`[dashboard/kanban] ${action} failed:`, error)
|
|
9
|
+
return kanbanError('The Kanban request could not be completed.', 500)
|
|
10
|
+
}
|
|
@@ -3,12 +3,14 @@ import { existsSync } from 'node:fs'
|
|
|
3
3
|
import { Action, createComponent as scaffoldComponent } from '@stacksjs/actions'
|
|
4
4
|
import { userComponentsPath } from '@stacksjs/path'
|
|
5
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
6
7
|
import { componentSourceRows } from './library-source'
|
|
7
8
|
|
|
8
9
|
export default new Action({
|
|
9
10
|
name: 'CreateComponent',
|
|
10
11
|
description: 'Creates a new STX component.',
|
|
11
12
|
method: 'POST',
|
|
13
|
+
apiResponse: true,
|
|
12
14
|
|
|
13
15
|
async handle(request: RequestInstance) {
|
|
14
16
|
const name = String(request.get('name') || '').trim()
|
|
@@ -16,13 +18,20 @@ export default new Action({
|
|
|
16
18
|
if (!/^[A-Z][A-Za-z0-9]*$/.test(name))
|
|
17
19
|
return response.json({ message: 'Use a PascalCase component name.' }, 422)
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
try {
|
|
22
|
+
const destination = userComponentsPath(`${name}.stx`)
|
|
23
|
+
if (existsSync(destination))
|
|
24
|
+
return response.json({ message: 'A component with that name already exists.' }, 409)
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
await scaffoldComponent({ name })
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const created = componentSourceRows().find(component => component.name === name)
|
|
29
|
+
if (!created)
|
|
30
|
+
throw new Error(`Scaffolded component ${name} could not be found in resources/components.`)
|
|
31
|
+
return response.json({ component: created }, 201)
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return dashboardOperationalError(error, 'The component could not be created.', 'CreateComponent', 500)
|
|
35
|
+
}
|
|
27
36
|
},
|
|
28
37
|
})
|
|
@@ -3,12 +3,14 @@ import { existsSync } from 'node:fs'
|
|
|
3
3
|
import { Action, createFunction as scaffoldFunction } from '@stacksjs/actions'
|
|
4
4
|
import { userFunctionsPath } from '@stacksjs/path'
|
|
5
5
|
import { response } from '@stacksjs/router'
|
|
6
|
+
import { dashboardOperationalError } from '../dashboard-response'
|
|
6
7
|
import { functionSourceRows } from './library-source'
|
|
7
8
|
|
|
8
9
|
export default new Action({
|
|
9
10
|
name: 'CreateFunction',
|
|
10
11
|
description: 'Creates a new function.',
|
|
11
12
|
method: 'POST',
|
|
13
|
+
apiResponse: true,
|
|
12
14
|
|
|
13
15
|
async handle(request: RequestInstance) {
|
|
14
16
|
const name = String(request.get('name') || '').trim().toLowerCase()
|
|
@@ -16,13 +18,20 @@ export default new Action({
|
|
|
16
18
|
if (!/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/.test(name))
|
|
17
19
|
return response.json({ message: 'Use a lowercase kebab-case function name.' }, 422)
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
try {
|
|
22
|
+
const destination = userFunctionsPath(`${name}.ts`)
|
|
23
|
+
if (existsSync(destination))
|
|
24
|
+
return response.json({ message: 'A function with that name already exists.' }, 409)
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
await scaffoldFunction({ name })
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
const created = functionSourceRows().find(fn => fn.name === name)
|
|
29
|
+
if (!created)
|
|
30
|
+
throw new Error(`Scaffolded function ${name} could not be found in resources/functions.`)
|
|
31
|
+
return response.json({ function: created }, 201)
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return dashboardOperationalError(error, 'The function could not be created.', 'CreateFunction', 500)
|
|
35
|
+
}
|
|
27
36
|
},
|
|
28
37
|
})
|