@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,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { Board } from '@stacksjs/orm'
|
|
3
4
|
import { modelBoolean, modelNullableString, modelNumber, modelString, refreshModel } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface BoardInput {
|
|
7
8
|
name?: unknown
|
|
@@ -12,7 +13,7 @@ interface BoardInput {
|
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* `PATCH /api/dashboard/kanban/boards/:id
|
|
16
|
+
* `PATCH /api/dashboard/kanban/boards/:id`.
|
|
16
17
|
*
|
|
17
18
|
* Partial update — only the fields present in the body are written.
|
|
18
19
|
* Omitting `archived` does NOT change the archive flag; passing
|
|
@@ -30,14 +31,13 @@ export default new Action({
|
|
|
30
31
|
description: 'Partial update of a kanban board.',
|
|
31
32
|
method: 'PATCH',
|
|
32
33
|
apiResponse: true,
|
|
33
|
-
async handle(request) {
|
|
34
|
-
const
|
|
35
|
-
const id = Number(rawId)
|
|
34
|
+
async handle(request: RequestInstance<BoardInput>) {
|
|
35
|
+
const id = Number(request.getParam('id'))
|
|
36
36
|
if (!Number.isFinite(id) || id <= 0) {
|
|
37
37
|
return kanbanError('Invalid board id', 400)
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const body = (
|
|
40
|
+
const body = request.all()
|
|
41
41
|
const set: Record<string, unknown> = {}
|
|
42
42
|
|
|
43
43
|
if (typeof body.name === 'string') {
|
|
@@ -83,8 +83,7 @@ export default new Action({
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
catch (err) {
|
|
86
|
-
|
|
87
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
86
|
+
return kanbanActionError(err, 'BoardUpdateAction')
|
|
88
87
|
}
|
|
89
88
|
},
|
|
90
89
|
})
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Action } from '@stacksjs/actions'
|
|
2
2
|
import { db } from '@stacksjs/database'
|
|
3
3
|
import { modelBoolean } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
5
|
|
|
6
6
|
interface BoardRow {
|
|
7
7
|
id: number
|
|
@@ -30,8 +30,7 @@ interface BoardRow {
|
|
|
30
30
|
* group (boards are a dev-mode surface per #1843). The action body
|
|
31
31
|
* doesn't re-check the role; the middleware did.
|
|
32
32
|
*
|
|
33
|
-
*
|
|
34
|
-
* reorder endpoint that moves a board's position.
|
|
33
|
+
* Mutations use the sibling store, update, destroy, and reorder actions.
|
|
35
34
|
*/
|
|
36
35
|
export default new Action({
|
|
37
36
|
name: 'Kanban Boards Index',
|
|
@@ -71,8 +70,7 @@ export default new Action({
|
|
|
71
70
|
return { boards }
|
|
72
71
|
}
|
|
73
72
|
catch (err) {
|
|
74
|
-
|
|
75
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
73
|
+
return kanbanActionError(err, 'BoardsIndexAction')
|
|
76
74
|
}
|
|
77
75
|
},
|
|
78
76
|
})
|
|
@@ -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
|
/**
|
|
@@ -14,7 +15,7 @@ interface ReorderInput {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
* `POST /api/dashboard/kanban/boards/reorder
|
|
18
|
+
* `POST /api/dashboard/kanban/boards/reorder`.
|
|
18
19
|
*
|
|
19
20
|
* Rewrites the `position` column on every board in the submitted list
|
|
20
21
|
* to match its index. Wrapped in a transaction so a partial failure
|
|
@@ -33,8 +34,8 @@ export default new Action({
|
|
|
33
34
|
description: 'Bulk-rewrite `position` on a list of boards to match the submitted order.',
|
|
34
35
|
method: 'POST',
|
|
35
36
|
apiResponse: true,
|
|
36
|
-
async handle(request) {
|
|
37
|
-
const body = (
|
|
37
|
+
async handle(request: RequestInstance<ReorderInput>) {
|
|
38
|
+
const body = request.all()
|
|
38
39
|
if (!Array.isArray(body.order) || body.order.length === 0) {
|
|
39
40
|
return kanbanError('`order` must be a non-empty array of board ids.', 400)
|
|
40
41
|
}
|
|
@@ -66,8 +67,7 @@ export default new Action({
|
|
|
66
67
|
return { reordered: ids.length }
|
|
67
68
|
}
|
|
68
69
|
catch (err) {
|
|
69
|
-
|
|
70
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
70
|
+
return kanbanActionError(err, 'BoardsReorderAction')
|
|
71
71
|
}
|
|
72
72
|
},
|
|
73
73
|
})
|
|
@@ -1,13 +1,14 @@
|
|
|
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 SyncInput {
|
|
6
7
|
userIds?: unknown
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* `POST /api/dashboard/kanban/cards/:id/assignees
|
|
11
|
+
* `POST /api/dashboard/kanban/cards/:id/assignees`.
|
|
11
12
|
*
|
|
12
13
|
* Replaces the set of users assigned to a card. Sync semantics —
|
|
13
14
|
* pass the new full list of user ids, the action diffs against
|
|
@@ -20,21 +21,20 @@ interface SyncInput {
|
|
|
20
21
|
*
|
|
21
22
|
* Cross-validation: each user id must exist. Doesn't validate
|
|
22
23
|
* board-scoped membership — kanban users aren't (currently) team-
|
|
23
|
-
* scoped; any platform user can be assigned to any card.
|
|
24
|
-
*
|
|
24
|
+
* scoped; any platform user can be assigned to any card. A future
|
|
25
|
+
* board-membership feature can tighten this contract.
|
|
25
26
|
*/
|
|
26
27
|
export default new Action({
|
|
27
28
|
name: 'Kanban Card Assignees Sync',
|
|
28
29
|
description: 'Replaces the set of users assigned to a card.',
|
|
29
30
|
method: 'POST',
|
|
30
31
|
apiResponse: true,
|
|
31
|
-
async handle(request) {
|
|
32
|
-
const
|
|
33
|
-
const cardId = Number(rawId)
|
|
32
|
+
async handle(request: RequestInstance<SyncInput>) {
|
|
33
|
+
const cardId = Number(request.getParam('id'))
|
|
34
34
|
if (!Number.isFinite(cardId) || cardId <= 0)
|
|
35
35
|
return kanbanError('Invalid card id', 400)
|
|
36
36
|
|
|
37
|
-
const body = (
|
|
37
|
+
const body = request.all()
|
|
38
38
|
if (!Array.isArray(body.userIds))
|
|
39
39
|
return kanbanError('`userIds` must be an array of user ids (possibly empty).', 400)
|
|
40
40
|
|
|
@@ -63,8 +63,10 @@ export default new Action({
|
|
|
63
63
|
return kanbanError('One or more user ids do not exist.', 400)
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
const requester =
|
|
67
|
-
const assignedByUserId = requester &&
|
|
66
|
+
const requester = await request.user()
|
|
67
|
+
const assignedByUserId = requester && Number.isInteger(Number(requester.id))
|
|
68
|
+
? Number(requester.id)
|
|
69
|
+
: null
|
|
68
70
|
|
|
69
71
|
await db.transaction(async (rawTrx) => {
|
|
70
72
|
const qb = rawTrx as unknown as typeof db
|
|
@@ -92,8 +94,7 @@ export default new Action({
|
|
|
92
94
|
return { cardId, assignees }
|
|
93
95
|
}
|
|
94
96
|
catch (err) {
|
|
95
|
-
|
|
96
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
97
|
+
return kanbanActionError(err, 'CardAssigneesSyncAction')
|
|
97
98
|
}
|
|
98
99
|
},
|
|
99
100
|
})
|
|
@@ -1,34 +1,31 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
|
-
import {
|
|
3
|
-
import { kanbanError } from './kanban-response'
|
|
3
|
+
import { CardComment } from '@stacksjs/orm'
|
|
4
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* `DELETE /api/dashboard/kanban/comments/:id
|
|
7
|
-
*
|
|
8
|
-
* Hard-deletes a card comment. No authorship check yet — Phase 4
|
|
9
|
-
* could add "only the author or an admin can delete" semantics by
|
|
10
|
-
* looking at `request.user.id` vs the comment's `user_id`. For now
|
|
11
|
-
* the dashboard's role-gated visibility (only admin+dev see the
|
|
12
|
-
* surface) is the gate.
|
|
7
|
+
* `DELETE /api/dashboard/kanban/comments/:id`.
|
|
8
|
+
* The dashboard route applies its local-development or admin guard.
|
|
13
9
|
*/
|
|
14
10
|
export default new Action({
|
|
15
11
|
name: 'Kanban Card Comment Destroy',
|
|
16
12
|
description: 'Hard-deletes a single card comment.',
|
|
17
13
|
method: 'DELETE',
|
|
18
14
|
apiResponse: true,
|
|
19
|
-
async handle(request) {
|
|
20
|
-
const
|
|
21
|
-
const id = Number(rawId)
|
|
15
|
+
async handle(request: RequestInstance) {
|
|
16
|
+
const id = Number(request.getParam('id'))
|
|
22
17
|
if (!Number.isFinite(id) || id <= 0)
|
|
23
18
|
return kanbanError('Invalid comment id', 400)
|
|
24
19
|
|
|
25
20
|
try {
|
|
26
|
-
|
|
21
|
+
const comment = await CardComment.find(id)
|
|
22
|
+
if (!comment)
|
|
23
|
+
return kanbanError('Comment not found.', 404)
|
|
24
|
+
await comment.delete()
|
|
27
25
|
return { deleted: true, id }
|
|
28
26
|
}
|
|
29
27
|
catch (err) {
|
|
30
|
-
|
|
31
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
28
|
+
return kanbanActionError(err, 'CardCommentDestroyAction')
|
|
32
29
|
}
|
|
33
30
|
},
|
|
34
31
|
})
|
|
@@ -1,83 +1,58 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
3
|
+
import { Card, CardComment } from '@stacksjs/orm'
|
|
4
|
+
import { cardCommentResponse } from './kanban-comment'
|
|
5
|
+
import { refreshModel } from './kanban-model'
|
|
6
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
4
7
|
|
|
5
8
|
interface CommentInput {
|
|
6
9
|
body?: unknown
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
|
-
* `POST /api/dashboard/kanban/cards/:id/comments
|
|
13
|
+
* `POST /api/dashboard/kanban/cards/:id/comments`.
|
|
11
14
|
*
|
|
12
15
|
* Appends a comment to a card. Stamps `user_id` from the request's
|
|
13
16
|
* authenticated user when available; leaves it null on the no-auth
|
|
14
17
|
* dev dashboard so the surface stays usable locally.
|
|
15
18
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* `/cards/:id`.
|
|
19
|
+
* Writes through the model so generated API validation, timestamps, and
|
|
20
|
+
* observers stay aligned with the rest of the application.
|
|
19
21
|
*/
|
|
20
22
|
export default new Action({
|
|
21
23
|
name: 'Kanban Card Comment Store',
|
|
22
24
|
description: 'Adds a comment to a card.',
|
|
23
25
|
method: 'POST',
|
|
24
26
|
apiResponse: true,
|
|
25
|
-
async handle(request) {
|
|
26
|
-
const
|
|
27
|
-
const cardId = Number(rawId)
|
|
27
|
+
async handle(request: RequestInstance<CommentInput>) {
|
|
28
|
+
const cardId = Number(request.getParam('id'))
|
|
28
29
|
if (!Number.isFinite(cardId) || cardId <= 0)
|
|
29
30
|
return kanbanError('Invalid card id', 400)
|
|
30
31
|
|
|
31
|
-
const body = (
|
|
32
|
+
const body = request.all()
|
|
32
33
|
const text = typeof body.body === 'string' ? body.body.trim() : ''
|
|
33
34
|
if (!text || text.length > 10000)
|
|
34
35
|
return kanbanError('`body` is required and must be 1-10000 characters.', 400)
|
|
35
36
|
|
|
36
37
|
try {
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
38
|
+
const card = await Card.find(cardId)
|
|
39
|
+
if (!card)
|
|
39
40
|
return kanbanError('Card not found.', 404)
|
|
40
41
|
|
|
41
|
-
const user =
|
|
42
|
-
const userId = user &&
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
user_id: userId,
|
|
42
|
+
const user = await request.user()
|
|
43
|
+
const userId = user && Number.isInteger(Number(user.id)) ? Number(user.id) : null
|
|
44
|
+
const comment = await refreshModel(await CardComment.create({
|
|
45
|
+
cardId,
|
|
46
|
+
userId,
|
|
47
47
|
body: text,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
// Pull the inserted row + author denormalisation. Ordered by id
|
|
51
|
-
// DESC so the freshest matching row wins.
|
|
52
|
-
const rows = await db.unsafe(
|
|
53
|
-
`SELECT cc.id, cc.uuid, cc.user_id, cc.body, cc.created_at, cc.updated_at, u.name, u.email
|
|
54
|
-
FROM card_comments cc
|
|
55
|
-
LEFT JOIN users u ON u.id = cc.user_id
|
|
56
|
-
WHERE cc.card_id = ? AND cc.body = ?
|
|
57
|
-
ORDER BY cc.id DESC
|
|
58
|
-
LIMIT 1`,
|
|
59
|
-
[cardId, text],
|
|
60
|
-
).execute() as Array<{ id: number, uuid: string | null, user_id: number | null, body: string, created_at: string | null, updated_at: string | null, name: string | null, email: string | null }>
|
|
61
|
-
const r = rows?.[0]
|
|
62
|
-
if (!r)
|
|
63
|
-
return kanbanError('Comment insert succeeded but follow-up read returned nothing.', 500)
|
|
48
|
+
}))
|
|
64
49
|
|
|
65
50
|
return {
|
|
66
|
-
comment:
|
|
67
|
-
id: r.id,
|
|
68
|
-
uuid: r.uuid,
|
|
69
|
-
userId: r.user_id,
|
|
70
|
-
body: r.body,
|
|
71
|
-
authorName: r.name,
|
|
72
|
-
authorEmail: r.email,
|
|
73
|
-
createdAt: r.created_at,
|
|
74
|
-
updatedAt: r.updated_at,
|
|
75
|
-
},
|
|
51
|
+
comment: cardCommentResponse(comment, user),
|
|
76
52
|
}
|
|
77
53
|
}
|
|
78
54
|
catch (err) {
|
|
79
|
-
|
|
80
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
55
|
+
return kanbanActionError(err, 'CardCommentStoreAction')
|
|
81
56
|
}
|
|
82
57
|
},
|
|
83
58
|
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
2
|
+
import { Action } from '@stacksjs/actions'
|
|
3
|
+
import { CardComment, User } from '@stacksjs/orm'
|
|
4
|
+
import { cardCommentResponse } from './kanban-comment'
|
|
5
|
+
import { modelNullableNumber, refreshModel } from './kanban-model'
|
|
6
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
7
|
+
|
|
8
|
+
interface CommentInput {
|
|
9
|
+
body?: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** `PATCH /api/dashboard/kanban/comments/:id`. */
|
|
13
|
+
export default new Action({
|
|
14
|
+
name: 'Kanban Card Comment Update',
|
|
15
|
+
description: 'Edits the body of a card comment.',
|
|
16
|
+
method: 'PATCH',
|
|
17
|
+
apiResponse: true,
|
|
18
|
+
async handle(request: RequestInstance<CommentInput>) {
|
|
19
|
+
const id = Number(request.getParam('id'))
|
|
20
|
+
if (!Number.isFinite(id) || id <= 0)
|
|
21
|
+
return kanbanError('Invalid comment id', 400)
|
|
22
|
+
|
|
23
|
+
const input = request.all()
|
|
24
|
+
const body = typeof input.body === 'string' ? input.body.trim() : ''
|
|
25
|
+
if (!body || body.length > 10000)
|
|
26
|
+
return kanbanError('`body` is required and must be 1-10000 characters.', 400)
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const comment = await CardComment.find(id)
|
|
30
|
+
if (!comment)
|
|
31
|
+
return kanbanError('Comment not found.', 404)
|
|
32
|
+
|
|
33
|
+
const updated = await refreshModel(await comment.update({ body }))
|
|
34
|
+
const userId = modelNullableNumber(updated, 'userId', 'user_id')
|
|
35
|
+
const author = userId === null ? null : await User.find(userId)
|
|
36
|
+
|
|
37
|
+
return { comment: cardCommentResponse(updated, author) }
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return kanbanActionError(error, 'CardCommentUpdateAction')
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
})
|
|
@@ -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/cards/:id
|
|
7
|
+
* `DELETE /api/dashboard/kanban/cards/:id`.
|
|
7
8
|
*
|
|
8
9
|
* Hard-deletes a card and clears its pivot rows in `card_labels` /
|
|
9
10
|
* `card_assignees`. The columns and board are untouched — only the
|
|
@@ -18,9 +19,8 @@ export default new Action({
|
|
|
18
19
|
description: 'Hard-deletes a card and its pivot rows.',
|
|
19
20
|
method: 'DELETE',
|
|
20
21
|
apiResponse: true,
|
|
21
|
-
async handle(request) {
|
|
22
|
-
const
|
|
23
|
-
const id = Number(rawId)
|
|
22
|
+
async handle(request: RequestInstance) {
|
|
23
|
+
const id = Number(request.getParam('id'))
|
|
24
24
|
if (!Number.isFinite(id) || id <= 0) {
|
|
25
25
|
return kanbanError('Invalid card id', 400)
|
|
26
26
|
}
|
|
@@ -30,15 +30,14 @@ export default new Action({
|
|
|
30
30
|
const qb = rawTrx as unknown as typeof db
|
|
31
31
|
await qb.deleteFrom('card_labels').where('card_id', '=', id).execute()
|
|
32
32
|
await qb.deleteFrom('card_assignees').where('card_id', '=', id).execute()
|
|
33
|
-
// Card comments
|
|
33
|
+
// Card comments are card-scoped children.
|
|
34
34
|
await qb.deleteFrom('card_comments').where('card_id', '=', id).execute()
|
|
35
35
|
await qb.deleteFrom('cards').where('id', '=', id).execute()
|
|
36
36
|
})
|
|
37
37
|
return { deleted: true, id }
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
|
-
|
|
41
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
40
|
+
return kanbanActionError(err, 'CardDestroyAction')
|
|
42
41
|
}
|
|
43
42
|
},
|
|
44
43
|
})
|
|
@@ -1,13 +1,14 @@
|
|
|
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 SyncInput {
|
|
6
7
|
labelIds?: unknown
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
|
-
* `POST /api/dashboard/kanban/cards/:id/labels
|
|
11
|
+
* `POST /api/dashboard/kanban/cards/:id/labels`.
|
|
11
12
|
*
|
|
12
13
|
* Replaces the set of labels attached to a card. Sync semantics —
|
|
13
14
|
* pass the new full list of label ids, the action diffs against
|
|
@@ -26,13 +27,12 @@ export default new Action({
|
|
|
26
27
|
description: 'Replaces the set of labels attached to a card.',
|
|
27
28
|
method: 'POST',
|
|
28
29
|
apiResponse: true,
|
|
29
|
-
async handle(request) {
|
|
30
|
-
const
|
|
31
|
-
const cardId = Number(rawId)
|
|
30
|
+
async handle(request: RequestInstance<SyncInput>) {
|
|
31
|
+
const cardId = Number(request.getParam('id'))
|
|
32
32
|
if (!Number.isFinite(cardId) || cardId <= 0)
|
|
33
33
|
return kanbanError('Invalid card id', 400)
|
|
34
34
|
|
|
35
|
-
const body = (
|
|
35
|
+
const body = request.all()
|
|
36
36
|
if (!Array.isArray(body.labelIds))
|
|
37
37
|
return kanbanError('`labelIds` must be an array of label ids (possibly empty).', 400)
|
|
38
38
|
|
|
@@ -56,7 +56,7 @@ export default new Action({
|
|
|
56
56
|
return kanbanError('Card not found.', 404)
|
|
57
57
|
|
|
58
58
|
// Every label must belong to the same board (labels are
|
|
59
|
-
// board-scoped
|
|
59
|
+
// board-scoped through `labels.board_id`).
|
|
60
60
|
if (uniqueLabelIds.length > 0) {
|
|
61
61
|
const placeholders = uniqueLabelIds.map(() => '?').join(',')
|
|
62
62
|
const labelRows = await db.unsafe(
|
|
@@ -92,8 +92,7 @@ export default new Action({
|
|
|
92
92
|
return { cardId, labels }
|
|
93
93
|
}
|
|
94
94
|
catch (err) {
|
|
95
|
-
|
|
96
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
95
|
+
return kanbanActionError(err, 'CardLabelsSyncAction')
|
|
97
96
|
}
|
|
98
97
|
},
|
|
99
98
|
})
|
|
@@ -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 { modelBoolean } from './kanban-model'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface CardRow {
|
|
7
8
|
id: number
|
|
@@ -20,7 +21,7 @@ interface CardRow {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
* `GET /api/dashboard/kanban/cards/:id
|
|
24
|
+
* `GET /api/dashboard/kanban/cards/:id`.
|
|
24
25
|
*
|
|
25
26
|
* Fetches a single card with everything the detail modal needs in one
|
|
26
27
|
* round-trip: labels, assignees, comments. The board view's cards
|
|
@@ -39,9 +40,8 @@ export default new Action({
|
|
|
39
40
|
description: 'Returns a single card with its labels, assignees, and comments thread.',
|
|
40
41
|
method: 'GET',
|
|
41
42
|
apiResponse: true,
|
|
42
|
-
async handle(request) {
|
|
43
|
-
const
|
|
44
|
-
const id = Number(rawId)
|
|
43
|
+
async handle(request: RequestInstance) {
|
|
44
|
+
const id = Number(request.getParam('id'))
|
|
45
45
|
if (!Number.isFinite(id) || id <= 0) {
|
|
46
46
|
return kanbanError('Invalid card id', 400)
|
|
47
47
|
}
|
|
@@ -118,8 +118,7 @@ export default new Action({
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
catch (err) {
|
|
121
|
-
|
|
122
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
121
|
+
return kanbanActionError(err, 'CardShowAction')
|
|
123
122
|
}
|
|
124
123
|
},
|
|
125
124
|
})
|
|
@@ -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 { BoardColumn, Card } from '@stacksjs/orm'
|
|
4
|
-
import { kanbanError } from './kanban-response'
|
|
5
|
+
import { kanbanActionError, kanbanError } from './kanban-response'
|
|
5
6
|
|
|
6
7
|
interface CardInput {
|
|
7
8
|
columnId?: unknown
|
|
@@ -11,7 +12,7 @@ interface CardInput {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* `POST /api/dashboard/kanban/cards
|
|
15
|
+
* `POST /api/dashboard/kanban/cards`.
|
|
15
16
|
*
|
|
16
17
|
* Creates a card at the end of the target column. `boardId` is
|
|
17
18
|
* resolved server-side from `column.board_id` (denormalised onto the
|
|
@@ -19,7 +20,7 @@ interface CardInput {
|
|
|
19
20
|
* would invite drift if the column was moved between boards (which
|
|
20
21
|
* isn't a feature, but still).
|
|
21
22
|
*
|
|
22
|
-
* Stamps `created_by_user_id` from `request.user` when the request is
|
|
23
|
+
* Stamps `created_by_user_id` from `request.user()` when the request is
|
|
23
24
|
* authenticated; leaves it null for the no-auth dev dashboard so the
|
|
24
25
|
* surface still works on localhost.
|
|
25
26
|
*/
|
|
@@ -28,8 +29,8 @@ export default new Action({
|
|
|
28
29
|
description: 'Creates a new card in a column.',
|
|
29
30
|
method: 'POST',
|
|
30
31
|
apiResponse: true,
|
|
31
|
-
async handle(request) {
|
|
32
|
-
const body = (
|
|
32
|
+
async handle(request: RequestInstance<CardInput>) {
|
|
33
|
+
const body = request.all()
|
|
33
34
|
|
|
34
35
|
const columnId = Number(body.columnId)
|
|
35
36
|
if (!Number.isFinite(columnId) || columnId <= 0) {
|
|
@@ -57,8 +58,8 @@ export default new Action({
|
|
|
57
58
|
).execute() as Array<{ m: number }>
|
|
58
59
|
const nextPosition = (Number(maxRow?.[0]?.m ?? -1) + 1) || 0
|
|
59
60
|
|
|
60
|
-
const user =
|
|
61
|
-
const createdByUserId = user &&
|
|
61
|
+
const user = await request.user()
|
|
62
|
+
const createdByUserId = user && Number.isInteger(Number(user.id)) ? Number(user.id) : null
|
|
62
63
|
|
|
63
64
|
const card = await Card.create({
|
|
64
65
|
columnId,
|
|
@@ -89,8 +90,7 @@ export default new Action({
|
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
92
|
catch (err) {
|
|
92
|
-
|
|
93
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
93
|
+
return kanbanActionError(err, 'CardStoreAction')
|
|
94
94
|
}
|
|
95
95
|
},
|
|
96
96
|
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import type { RequestInstance } from '@stacksjs/types'
|
|
1
2
|
import { Action } from '@stacksjs/actions'
|
|
2
3
|
import { Card } from '@stacksjs/orm'
|
|
3
4
|
import { modelBoolean, 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 CardInput {
|
|
7
8
|
title?: unknown
|
|
@@ -11,7 +12,7 @@ interface CardInput {
|
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* `PATCH /api/dashboard/kanban/cards/:id
|
|
15
|
+
* `PATCH /api/dashboard/kanban/cards/:id`.
|
|
15
16
|
*
|
|
16
17
|
* Partial update for the card body: title, description, due date,
|
|
17
18
|
* archive flag. `position` and `columnId` are NOT updatable here —
|
|
@@ -25,14 +26,13 @@ export default new Action({
|
|
|
25
26
|
description: 'Partial update of a card body (title, description, due date, archive).',
|
|
26
27
|
method: 'PATCH',
|
|
27
28
|
apiResponse: true,
|
|
28
|
-
async handle(request) {
|
|
29
|
-
const
|
|
30
|
-
const id = Number(rawId)
|
|
29
|
+
async handle(request: RequestInstance<CardInput>) {
|
|
30
|
+
const id = Number(request.getParam('id'))
|
|
31
31
|
if (!Number.isFinite(id) || id <= 0) {
|
|
32
32
|
return kanbanError('Invalid card id', 400)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const body = (
|
|
35
|
+
const body = request.all()
|
|
36
36
|
const set: Record<string, unknown> = {}
|
|
37
37
|
|
|
38
38
|
if (typeof body.title === 'string') {
|
|
@@ -81,8 +81,7 @@ export default new Action({
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
catch (err) {
|
|
84
|
-
|
|
85
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
84
|
+
return kanbanActionError(err, 'CardUpdateAction')
|
|
86
85
|
}
|
|
87
86
|
},
|
|
88
87
|
})
|
|
@@ -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 ColumnReorder {
|
|
6
7
|
columnId: number
|
|
@@ -31,7 +32,7 @@ interface ReorderInput {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
|
-
* `POST /api/dashboard/kanban/cards/reorder
|
|
35
|
+
* `POST /api/dashboard/kanban/cards/reorder`.
|
|
35
36
|
*
|
|
36
37
|
* The drag-and-drop hot path. Rewrites card positions and column
|
|
37
38
|
* assignments across one or more columns of a single board in a single
|
|
@@ -47,8 +48,8 @@ export default new Action({
|
|
|
47
48
|
description: 'Bulk-rewrite cards.{column_id, position} across one or more columns of the same board.',
|
|
48
49
|
method: 'POST',
|
|
49
50
|
apiResponse: true,
|
|
50
|
-
async handle(request) {
|
|
51
|
-
const body = (
|
|
51
|
+
async handle(request: RequestInstance<ReorderInput>) {
|
|
52
|
+
const body = request.all()
|
|
52
53
|
|
|
53
54
|
if (!Array.isArray(body.columns) || body.columns.length === 0) {
|
|
54
55
|
return kanbanError('`columns` must be a non-empty array.', 400)
|
|
@@ -134,8 +135,7 @@ export default new Action({
|
|
|
134
135
|
return { moved: allCardIds.size, boardId }
|
|
135
136
|
}
|
|
136
137
|
catch (err) {
|
|
137
|
-
|
|
138
|
-
return kanbanError(err instanceof Error ? err.message : 'unknown error', 500)
|
|
138
|
+
return kanbanActionError(err, 'CardsReorderAction')
|
|
139
139
|
}
|
|
140
140
|
},
|
|
141
141
|
})
|