@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
|
@@ -121,7 +121,7 @@ async function openEmail(value: DashboardInboxEmail | CustomEvent<DashboardInbox
|
|
|
121
121
|
deletePending.set(false)
|
|
122
122
|
replyText.set('')
|
|
123
123
|
|
|
124
|
-
if (!email.
|
|
124
|
+
if (!email.detailsLoaded) {
|
|
125
125
|
try {
|
|
126
126
|
const body = await fetchDashboardInboxBody(email.id, mailbox())
|
|
127
127
|
emails.set(emails().map(item => item.id === email.id ? { ...item, ...body } : item))
|
|
@@ -228,7 +228,7 @@ onMount(() => {
|
|
|
228
228
|
</script>
|
|
229
229
|
|
|
230
230
|
<div class="overflow-hidden bg-white dark:bg-neutral-800 border border-gray-200 rounded-lg dark:border-neutral-700">
|
|
231
|
-
<div class="flex min-h-[calc(100vh-8rem)]">
|
|
231
|
+
<div class="flex flex-col md:flex-row min-h-[calc(100vh-8rem)]">
|
|
232
232
|
<EmailSidebar
|
|
233
233
|
activeFolder="inbox"
|
|
234
234
|
:folders="folders"
|
|
@@ -237,10 +237,10 @@ onMount(() => {
|
|
|
237
237
|
@compose="compose"
|
|
238
238
|
/>
|
|
239
239
|
|
|
240
|
-
<div class="flex overflow-hidden flex-1 flex-col">
|
|
240
|
+
<div class="flex overflow-hidden flex-1 flex-col min-w-0">
|
|
241
241
|
<header class="px-4 py-3 bg-white dark:bg-neutral-700 border-b border-gray-200 dark:border-neutral-600">
|
|
242
242
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
243
|
-
<div class="flex gap-4 items-center">
|
|
243
|
+
<div class="flex flex-col gap-2 sm:flex-row sm:gap-4 sm:items-center min-w-0">
|
|
244
244
|
<h1 class="font-bold text-gray-900 text-xl dark:text-white">Inbox</h1>
|
|
245
245
|
<EmailNavigation active="inbox" />
|
|
246
246
|
</div>
|
|
@@ -277,6 +277,7 @@ onMount(() => {
|
|
|
277
277
|
:replying="isReplying"
|
|
278
278
|
:replyText="replyText"
|
|
279
279
|
:canSendReply="canSendReply"
|
|
280
|
+
:mailbox="mailbox"
|
|
280
281
|
@close="closeEmail"
|
|
281
282
|
@toggle-read="toggleSelectedRead"
|
|
282
283
|
@request-delete="requestDelete"
|
|
@@ -7,6 +7,7 @@ const deleting = useReactiveProp('deleting', false)
|
|
|
7
7
|
const replying = useReactiveProp('replying', false)
|
|
8
8
|
const replyText = useReactiveProp('replyText', '')
|
|
9
9
|
const canSendReply = useReactiveProp('canSendReply', false)
|
|
10
|
+
const mailbox = useReactiveProp('mailbox', '')
|
|
10
11
|
const emit = defineEmits()
|
|
11
12
|
|
|
12
13
|
function message(): DashboardInboxEmail | null {
|
|
@@ -69,10 +70,11 @@ function formatDate(value: string): string {
|
|
|
69
70
|
:text="message()?.bodyText || ''"
|
|
70
71
|
:title="message()?.subject || 'Inbound email preview'"
|
|
71
72
|
/>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
<InboxAttachmentList
|
|
74
|
+
:attachments="message()?.attachments || []"
|
|
75
|
+
:mailbox="mailbox"
|
|
76
|
+
:messageId="message()?.id || ''"
|
|
77
|
+
/>
|
|
76
78
|
</div>
|
|
77
79
|
|
|
78
80
|
<div class="p-4 bg-white dark:bg-neutral-700 border-gray-200 border-t dark:border-neutral-600">
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script client>
|
|
2
2
|
import type { DashboardJob } from '../../../../functions/jobs'
|
|
3
|
+
import { DashboardApiError } from '../../../../functions/dashboard-api'
|
|
3
4
|
import { fetchJob, retryJob } from '../../../../functions/jobs'
|
|
4
5
|
import { pushToast } from '../../../../functions/toasts'
|
|
5
6
|
|
|
@@ -53,11 +54,11 @@ async function loadJob(): Promise<void> {
|
|
|
53
54
|
try {
|
|
54
55
|
const response = await fetchJob(jobId())
|
|
55
56
|
job.set(response.job)
|
|
56
|
-
loadError.set(response.error || '')
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
59
|
job.set(null)
|
|
60
|
-
|
|
60
|
+
if (!(error instanceof DashboardApiError && error.status === 404))
|
|
61
|
+
loadError.set(error instanceof Error ? error.message : 'Job could not be loaded.')
|
|
61
62
|
}
|
|
62
63
|
finally {
|
|
63
64
|
isLoading.set(false)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script client>
|
|
2
|
-
// Kanban board view
|
|
2
|
+
// Kanban board view.
|
|
3
3
|
//
|
|
4
4
|
// Optimistic-update pattern:
|
|
5
5
|
// 1. The DnD handler mutates the store immediately (so the card
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
const columns = derived(() => kanban.currentColumns())
|
|
30
30
|
const allLabels = derived(() => kanban.currentLabels())
|
|
31
31
|
|
|
32
|
-
// Card detail modal state
|
|
32
|
+
// Card detail modal state - read straight from the store. The
|
|
33
33
|
// modal itself lives at the bottom of this file.
|
|
34
34
|
const openCard = derived(() => kanban.openCard())
|
|
35
35
|
const loadingCard = derived(() => kanban.loadingCard())
|
|
@@ -48,6 +48,9 @@
|
|
|
48
48
|
const titleDraft = state('')
|
|
49
49
|
const descDraft = state('')
|
|
50
50
|
const commentDraft = state('')
|
|
51
|
+
const editingCommentId = state(null)
|
|
52
|
+
const commentEditDraft = state('')
|
|
53
|
+
const savingCommentId = state(null)
|
|
51
54
|
const showLabelPicker = state(false)
|
|
52
55
|
const showAssigneePicker = state(false)
|
|
53
56
|
const showNewLabelComposer = state(false)
|
|
@@ -83,6 +86,9 @@
|
|
|
83
86
|
descDraft.set(c ? (c.description || '') : '')
|
|
84
87
|
dueDateDraft.set(c?.dueDate ? c.dueDate.slice(0, 10) : '')
|
|
85
88
|
commentDraft.set('')
|
|
89
|
+
editingCommentId.set(null)
|
|
90
|
+
commentEditDraft.set('')
|
|
91
|
+
savingCommentId.set(null)
|
|
86
92
|
}
|
|
87
93
|
// Sync drafts when the open card changes (open / close / refetch).
|
|
88
94
|
effect(() => { openCard(); Promise.resolve().then(syncDraftsFromCard) })
|
|
@@ -134,6 +140,40 @@
|
|
|
134
140
|
if (ok) commentDraft.set('')
|
|
135
141
|
}
|
|
136
142
|
|
|
143
|
+
function startCommentEdit(id, body) {
|
|
144
|
+
editingCommentId.set(id)
|
|
145
|
+
commentEditDraft.set(body)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function cancelCommentEdit() {
|
|
149
|
+
editingCommentId.set(null)
|
|
150
|
+
commentEditDraft.set('')
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function saveCommentEdit(id) {
|
|
154
|
+
const body = commentEditDraft().trim()
|
|
155
|
+
if (!body || savingCommentId()) return
|
|
156
|
+
savingCommentId.set(id)
|
|
157
|
+
try {
|
|
158
|
+
if (await kanban.updateComment(id, body))
|
|
159
|
+
cancelCommentEdit()
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
savingCommentId.set(null)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function handleCommentEditKeydown(event, id) {
|
|
167
|
+
if (event.key === 'Escape') {
|
|
168
|
+
event.preventDefault()
|
|
169
|
+
cancelCommentEdit()
|
|
170
|
+
}
|
|
171
|
+
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) {
|
|
172
|
+
event.preventDefault()
|
|
173
|
+
void saveCommentEdit(id)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
137
177
|
function requestDelete(kind: DeleteKind, id: number, hasCards = false): void {
|
|
138
178
|
pendingDelete.set({ kind, id, hasCards })
|
|
139
179
|
}
|
|
@@ -219,7 +259,7 @@
|
|
|
219
259
|
}
|
|
220
260
|
|
|
221
261
|
// Pull the id from the URL through the navigation composable.
|
|
222
|
-
// (stacksjs/stacks#1838
|
|
262
|
+
// (stacksjs/stacks#1838 - no vanilla DOM in dashboard pages).
|
|
223
263
|
const id = useNavigation().urlIdParam()
|
|
224
264
|
if (id !== null)
|
|
225
265
|
kanban.loadBoard(id)
|
|
@@ -647,7 +687,7 @@
|
|
|
647
687
|
</div>
|
|
648
688
|
</div>
|
|
649
689
|
|
|
650
|
-
<!--
|
|
690
|
+
<!-- Card detail modal -->
|
|
651
691
|
<!-- Opens on card click. Fixed positioned with a backdrop; closes
|
|
652
692
|
on backdrop click + Escape. Sections (top → bottom):
|
|
653
693
|
· Title (inline-editable, blurs to commit)
|
|
@@ -739,9 +779,26 @@
|
|
|
739
779
|
<div class="flex gap-2 items-baseline">
|
|
740
780
|
<span class="font-medium text-xs text-zinc-700 dark:text-zinc-200" :text="cm.authorName || cm.authorEmail || 'Unknown'"></span>
|
|
741
781
|
<span class="text-[10px] text-zinc-400" :text="formatTimestamp(cm.createdAt)"></span>
|
|
742
|
-
<
|
|
782
|
+
<template :if="editingCommentId() !== cm.id">
|
|
783
|
+
<Button variant="ghost" size="xs" @click="startCommentEdit(cm.id, cm.body)">Edit</Button>
|
|
784
|
+
<Button variant="ghost" size="xs" @click="requestDelete('comment', cm.id)"><span class="text-red-500">Delete</span></Button>
|
|
785
|
+
</template>
|
|
786
|
+
</div>
|
|
787
|
+
<p :if="editingCommentId() !== cm.id" class="mt-0.5 text-xs text-zinc-700 whitespace-pre-wrap dark:text-zinc-300" :text="cm.body"></p>
|
|
788
|
+
<div :if="editingCommentId() === cm.id" class="mt-2">
|
|
789
|
+
<textarea
|
|
790
|
+
x-model="commentEditDraft"
|
|
791
|
+
@keydown="handleCommentEditKeydown($event, cm.id)"
|
|
792
|
+
rows="3"
|
|
793
|
+
maxlength="10000"
|
|
794
|
+
:aria-label="'Edit comment by ' + (cm.authorName || cm.authorEmail || 'Unknown')"
|
|
795
|
+
class="px-3 py-2 w-full text-sm text-zinc-800 dark:text-zinc-200 bg-white dark:bg-zinc-950 border border-zinc-200 rounded dark:border-zinc-800 focus:border-violet-400 focus:outline-none dark:focus:border-violet-600 resize-y"
|
|
796
|
+
></textarea>
|
|
797
|
+
<div class="flex gap-2 justify-end mt-2">
|
|
798
|
+
<Button variant="secondary" size="xs" :disabled="savingCommentId() === cm.id" @click="cancelCommentEdit()">Cancel</Button>
|
|
799
|
+
<Button size="xs" :loading="savingCommentId() === cm.id" :disabled="!commentEditDraft().trim()" @click="saveCommentEdit(cm.id)">Save comment</Button>
|
|
800
|
+
</div>
|
|
743
801
|
</div>
|
|
744
|
-
<p class="mt-0.5 text-xs text-zinc-700 whitespace-pre-wrap dark:text-zinc-300" :text="cm.body"></p>
|
|
745
802
|
</div>
|
|
746
803
|
</li>
|
|
747
804
|
</ul>
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
<script client>
|
|
2
|
-
// Kanban — Boards index (stacksjs/stacks#1846, Phase 1).
|
|
3
|
-
//
|
|
4
2
|
// Lists every non-archived board with a card-count badge. Clicking a
|
|
5
|
-
// board navigates to
|
|
6
|
-
// drag-and-drop board view at that URL.
|
|
3
|
+
// board navigates to its complete drag-and-drop view.
|
|
7
4
|
//
|
|
8
5
|
// Follows #1838 conventions: no <script server>, no vanilla DOM
|
|
9
|
-
// reads, all state via the store. Role gate via useRole()
|
|
6
|
+
// reads, all state via the store. Role gate via useRole() - the
|
|
10
7
|
// surface is dev-mode-only (admins + devs see it, clients don't).
|
|
11
8
|
import { useRole } from '~/storage/framework/defaults/views/dashboard/composables/useRole'
|
|
12
9
|
import { kanbanStore } from '~/storage/framework/defaults/views/dashboard/stores/kanban'
|
|
@@ -175,7 +172,7 @@
|
|
|
175
172
|
<p class="mt-2 text-xs text-zinc-500">Most common: migrations haven't run yet. Try <code>./buddy migrate</code>.</p>
|
|
176
173
|
</div>
|
|
177
174
|
|
|
178
|
-
<!-- Empty state
|
|
175
|
+
<!-- Empty state - DB reachable but no boards yet -->
|
|
179
176
|
<div :if="!loading && !error && !hasBoards" class="px-6 py-12 text-center border border-dashed border-zinc-300 rounded-lg dark:border-zinc-700">
|
|
180
177
|
<span class="block mb-3 mx-auto h-10 w-10 text-zinc-400 i-hugeicons-layout-3-column"></span>
|
|
181
178
|
<p class="font-medium text-sm text-zinc-700 dark:text-zinc-300">No boards yet</p>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// and the role/permission matrix against the live `user_roles` /
|
|
6
6
|
// `role_permissions` / `user_permissions` pivots. All persistence
|
|
7
7
|
// flows through the dashboard RBAC endpoints, which delegate
|
|
8
|
-
// to `@stacksjs/auth`'s `Rbac` facade
|
|
8
|
+
// to `@stacksjs/auth`'s `Rbac` facade - no direct DB writes from the
|
|
9
9
|
// page.
|
|
10
10
|
//
|
|
11
11
|
// Follows #1838 conventions:
|
|
@@ -13,13 +13,13 @@
|
|
|
13
13
|
// - All state in the rbacStore; the page is pure presentation.
|
|
14
14
|
// - Mutations are optimistic with rollback on failure (store handles
|
|
15
15
|
// the snapshot/rollback dance).
|
|
16
|
-
import { useStore } from '@stacksjs/stx'
|
|
17
16
|
import { useRole } from '~/storage/framework/defaults/views/dashboard/composables/useRole'
|
|
17
|
+
import { rbacStore } from '~/storage/framework/defaults/views/dashboard/stores/rbac'
|
|
18
18
|
|
|
19
|
-
const rbac =
|
|
19
|
+
const rbac = rbacStore
|
|
20
20
|
// Admin-only surface. Per `useRole.ts`, `isAdmin()` returns true for
|
|
21
21
|
// any user with the `admin` role *or* an unauthenticated viewer on
|
|
22
|
-
// the localhost dev dashboard
|
|
22
|
+
// the localhost dev dashboard - same permissive-dev defaults the rest
|
|
23
23
|
// of the management surface uses.
|
|
24
24
|
const { isAdmin } = useRole()
|
|
25
25
|
const canManage = derived(() => isAdmin())
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// works the same on a table with 10 rows and one with a million. Rows and
|
|
8
8
|
// writes go through the model, so scopes, casts and observers apply.
|
|
9
9
|
import { useNavigation } from '~/storage/framework/defaults/views/dashboard/composables/useNavigation'
|
|
10
|
-
import { dashboardApi } from '~/storage/framework/defaults/functions/dashboard-api'
|
|
10
|
+
import { DashboardApiError, dashboardApi } from '~/storage/framework/defaults/functions/dashboard-api'
|
|
11
11
|
import { pushToast } from '~/storage/framework/defaults/functions/toasts'
|
|
12
12
|
|
|
13
13
|
interface ColumnMeta {
|
|
@@ -46,7 +46,6 @@ interface ResponseShape {
|
|
|
46
46
|
writable: boolean
|
|
47
47
|
writeCapabilities: WriteCapabilities
|
|
48
48
|
createFields: CreateField[]
|
|
49
|
-
source: 'orm' | 'sqlite-fallback'
|
|
50
49
|
error: string | null
|
|
51
50
|
}
|
|
52
51
|
|
|
@@ -67,7 +66,6 @@ const search = state(navigation.urlQuery('q') || '')
|
|
|
67
66
|
const filters = state<Record<string, string>>({})
|
|
68
67
|
const writeCapabilities = state<WriteCapabilities>({ create: false, update: false, destroy: false })
|
|
69
68
|
const createFields = state<CreateField[]>([])
|
|
70
|
-
const source = state<'orm' | 'sqlite-fallback'>('orm')
|
|
71
69
|
const error = state<string | null>(null)
|
|
72
70
|
const loading = state(true)
|
|
73
71
|
const showFilters = state(false)
|
|
@@ -86,14 +84,6 @@ const createErrors = state<Record<string, string[]>>({})
|
|
|
86
84
|
|
|
87
85
|
const slug = derived(() => navigation.urlSegment(-1) || '')
|
|
88
86
|
|
|
89
|
-
const sourceLabel = derived(() => {
|
|
90
|
-
if (source() === 'orm')
|
|
91
|
-
return 'via Stacks ORM'
|
|
92
|
-
return Object.values(writeCapabilities()).some(Boolean)
|
|
93
|
-
? 'SQLite query, ORM writes'
|
|
94
|
-
: 'raw SQLite, read-only'
|
|
95
|
-
})
|
|
96
|
-
|
|
97
87
|
const recordLabel = derived(() => {
|
|
98
88
|
const n = totalCount()
|
|
99
89
|
return `${n.toLocaleString()} ${n === 1 ? 'record' : 'records'}`
|
|
@@ -223,7 +213,6 @@ async function load() {
|
|
|
223
213
|
sort.set(data.sort ?? '')
|
|
224
214
|
writeCapabilities.set(data.writeCapabilities ?? { create: false, update: false, destroy: false })
|
|
225
215
|
createFields.set(data.createFields ?? [])
|
|
226
|
-
source.set(data.source ?? 'orm')
|
|
227
216
|
error.set(data.error ?? null)
|
|
228
217
|
}
|
|
229
218
|
catch (e) {
|
|
@@ -319,6 +308,11 @@ async function createRecord(fields: Record<string, unknown>): Promise<void> {
|
|
|
319
308
|
}
|
|
320
309
|
catch (reason) {
|
|
321
310
|
createError.set(reason instanceof Error ? reason.message : String(reason))
|
|
311
|
+
if (reason instanceof DashboardApiError && reason.fields) {
|
|
312
|
+
createErrors.set(Object.fromEntries(
|
|
313
|
+
Object.entries(reason.fields).map(([field, message]) => [field, [message]]),
|
|
314
|
+
))
|
|
315
|
+
}
|
|
322
316
|
}
|
|
323
317
|
finally {
|
|
324
318
|
creating.set(false)
|
|
@@ -402,7 +396,7 @@ void load()
|
|
|
402
396
|
<div class="flex flex-wrap gap-4 items-start justify-between">
|
|
403
397
|
<div class="min-w-0">
|
|
404
398
|
<h1 class="font-semibold text-2xl text-neutral-900 truncate dark:text-white" :text="modelName()"></h1>
|
|
405
|
-
<p class="mt-1 text-[13px] text-neutral-500 dark:text-neutral-400" :text="recordLabel() + ' · ' + tableName() + ' ·
|
|
399
|
+
<p class="mt-1 text-[13px] text-neutral-500 dark:text-neutral-400" :text="recordLabel() + ' · ' + tableName() + ' · via Stacks ORM'"></p>
|
|
406
400
|
</div>
|
|
407
401
|
<div class="flex gap-2 items-center">
|
|
408
402
|
<Button
|
|
@@ -129,7 +129,7 @@ onMount(() => {
|
|
|
129
129
|
|
|
130
130
|
<div :if="!loadError() && unavailableModels() > 0" role="status" class="flex gap-3 items-start px-4 py-3 text-amber-800 text-sm dark:text-amber-200 bg-amber-50 dark:bg-amber-950/40 border border-amber-200 rounded-lg dark:border-amber-900">
|
|
131
131
|
<span class="flex-none mt-0.5 h-4 w-4 i-hugeicons-alert-02"></span>
|
|
132
|
-
<span>{{ unavailableModels() }} model {{ unavailableModels() === 1 ? 'count is' : 'counts are' }} unavailable. Affected cards
|
|
132
|
+
<span>{{ unavailableModels() }} model {{ unavailableModels() === 1 ? 'count is' : 'counts are' }} unavailable. Affected cards remain unavailable instead of reporting zero records.</span>
|
|
133
133
|
</div>
|
|
134
134
|
|
|
135
135
|
<Card padding="none">
|
|
@@ -11,19 +11,16 @@
|
|
|
11
11
|
Sorry, we couldn't find the page you're looking for. The page might have been moved or deleted.
|
|
12
12
|
</p>
|
|
13
13
|
<div class="flex gap-x-6 items-center justify-center mt-10">
|
|
14
|
-
<Button tag="a" href="/
|
|
14
|
+
<Button tag="a" href="/">
|
|
15
15
|
Return to dashboard
|
|
16
16
|
</Button>
|
|
17
|
-
<Button tag="a" href="/" variant="ghost">
|
|
18
|
-
Go to home <span aria-hidden="true">→</span>
|
|
19
|
-
</Button>
|
|
20
17
|
</div>
|
|
21
18
|
</div>
|
|
22
19
|
|
|
23
20
|
<div>
|
|
24
21
|
<h2 class="font-semibold leading-6 text-base text-center text-gray-900 dark:text-white">You might be looking for</h2>
|
|
25
22
|
<div class="grid gap-5 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 mt-6">
|
|
26
|
-
<StxLink to="/
|
|
23
|
+
<StxLink to="/" class="overflow-hidden bg-white dark:bg-neutral-800 rounded-lg shadow hover:shadow-md duration-200 transition-shadow">
|
|
27
24
|
<div class="p-5">
|
|
28
25
|
<div class="flex items-center">
|
|
29
26
|
<div class="flex-shrink-0 p-3 bg-blue-500 rounded-md">
|
|
@@ -97,6 +97,10 @@ function formatDate(value: string): string {
|
|
|
97
97
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString()
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
function queryDetailsPath(id: DashboardQueryLog['id']): string {
|
|
101
|
+
return `/queries/${encodeURIComponent(String(id))}`
|
|
102
|
+
}
|
|
103
|
+
|
|
100
104
|
function typeClass(value: DashboardQueryLog['type']): string {
|
|
101
105
|
const classes: Record<DashboardQueryLog['type'], string> = {
|
|
102
106
|
SELECT: 'text-blue-700 dark:text-blue-300 bg-blue-50 dark:bg-blue-950/40',
|
|
@@ -248,9 +252,9 @@ onMount(() => {
|
|
|
248
252
|
</td>
|
|
249
253
|
<td class="px-4 py-3.5 text-neutral-500 text-right text-xs whitespace-nowrap dark:text-neutral-400">{{ formatDate(query.executedAt) }}</td>
|
|
250
254
|
<td class="pr-5 py-3.5 text-right">
|
|
251
|
-
<
|
|
255
|
+
<a :href="queryDetailsPath(query.id)" :aria-label="'View query ' + query.id" class="inline-flex p-1.5 text-neutral-500 dark:text-neutral-400 hover:text-blue-600 dark:hover:text-blue-400 rounded-md">
|
|
252
256
|
<span class="h-4 w-4 i-hugeicons-arrow-right-01"></span>
|
|
253
|
-
</
|
|
257
|
+
</a>
|
|
254
258
|
</td>
|
|
255
259
|
</tr>
|
|
256
260
|
</template>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script server>
|
|
2
2
|
// The navigation the toggles below control. Read from the same builder the
|
|
3
|
-
// layout uses, so the list can never drift from what the sidebar renders
|
|
3
|
+
// layout uses, so the list can never drift from what the sidebar renders -
|
|
4
4
|
// including sections a project has switched off in config/dashboard.ts, which
|
|
5
5
|
// simply are not here to toggle.
|
|
6
6
|
import { buildWebSidebarSections } from '~/storage/framework/defaults/resources/functions/dashboard/sidebar'
|
|
@@ -29,7 +29,7 @@ const appearance = useAppearance()
|
|
|
29
29
|
// see until you navigate away is the thing this is avoiding.
|
|
30
30
|
const styleOptions = SIDEBAR_STYLE_OPTIONS
|
|
31
31
|
const colorModes = COLOR_MODE_OPTIONS
|
|
32
|
-
const sections = navSections
|
|
32
|
+
const sections = {{ navSections }}
|
|
33
33
|
|
|
34
34
|
const sidebarStyle = appearance.sidebarStyle
|
|
35
35
|
const colorMode = appearance.colorMode
|
|
@@ -58,9 +58,9 @@ function resetAll() {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
// Selected state travels as a data attribute rather than as a swapped class
|
|
61
|
-
// string. It began as a workaround
|
|
61
|
+
// string. It began as a workaround - Crosswind could not see class names that
|
|
62
62
|
// existed only inside a client-script expression, so those utilities were never
|
|
63
|
-
// emitted and the control rendered unstyled
|
|
63
|
+
// emitted and the control rendered unstyled - and that gap is now closed
|
|
64
64
|
// upstream. It stays because it is the better shape regardless: one selector
|
|
65
65
|
// per state in the block below, rather than two class strings kept in sync by
|
|
66
66
|
// hand inside a ternary.
|
|
@@ -19,9 +19,9 @@ const actionHref = useReactiveProp('actionHref', '')
|
|
|
19
19
|
<p :if="description()" class="mb-6 max-w-sm text-neutral-500 text-sm dark:text-neutral-400" :text="description()"></p>
|
|
20
20
|
|
|
21
21
|
<Button
|
|
22
|
-
:if="actionLabel()"
|
|
22
|
+
:if="actionLabel() && actionHref()"
|
|
23
23
|
tag="a"
|
|
24
|
-
:href="actionHref()
|
|
24
|
+
:href="actionHref()"
|
|
25
25
|
>
|
|
26
26
|
<span :if="actionIcon()" :class="actionIcon() + ' h-4 w-4'"></span>
|
|
27
27
|
<span :text="actionLabel()"></span>
|
|
@@ -3,11 +3,11 @@ interface PageLayoutProps {
|
|
|
3
3
|
/**
|
|
4
4
|
* Visual variant.
|
|
5
5
|
*
|
|
6
|
-
* - `default`
|
|
6
|
+
* - `default` - standard centered column with `mx-auto` + max-width
|
|
7
7
|
* gutter. Use for almost every page.
|
|
8
|
-
* - `centered`
|
|
8
|
+
* - `centered` - vertically centered hero block on a min-height
|
|
9
9
|
* container. Use for splash / 404 / empty-route pages.
|
|
10
|
-
* - `bare`
|
|
10
|
+
* - `bare` - no container at all. Use when the page wants to own
|
|
11
11
|
* its own gutter (e.g. a settings page with side-by-side panels
|
|
12
12
|
* that already pad themselves).
|
|
13
13
|
*/
|
|
@@ -428,11 +428,11 @@ export function buildNavSections(
|
|
|
428
428
|
// The layout's role map hides the row from client-role viewers.
|
|
429
429
|
{ to: '/kanban', icon: 'table', text: 'Kanban', roles: ['admin', 'dev'] },
|
|
430
430
|
]
|
|
431
|
-
// CI tracking (stacksjs/stacks#1844)
|
|
432
|
-
// config/dashboard.ts.
|
|
433
|
-
// (
|
|
431
|
+
// CI tracking (stacksjs/stacks#1844) - opt-in via `ci.enabled` in
|
|
432
|
+
// config/dashboard.ts. Keep the row gate aligned with the page's
|
|
433
|
+
// `useRole().isDev()` check so client-only viewers never get a dead link.
|
|
434
434
|
if (toggles.ci)
|
|
435
|
-
managementItems.push({ to: '/ci', icon: 'check-circle', text: 'CI' })
|
|
435
|
+
managementItems.push({ to: '/ci', icon: 'check-circle', text: 'CI', roles: ['dev'] })
|
|
436
436
|
sections.push(['management', 'management', managementItems])
|
|
437
437
|
}
|
|
438
438
|
|
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,23 @@
|
|
|
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
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"id": "email-idempotency",
|
|
190
|
+
"name": "EmailIdempotency",
|
|
191
|
+
"icon": "tablecells.fill",
|
|
192
|
+
"hasDedicatedPage": false,
|
|
193
|
+
"category": "data",
|
|
194
|
+
"dashboard": {
|
|
195
|
+
"enabled": false
|
|
196
|
+
}
|
|
197
|
+
},
|
|
171
198
|
{
|
|
172
199
|
"id": "email-list",
|
|
173
200
|
"name": "EmailList",
|
|
@@ -182,6 +209,26 @@
|
|
|
182
209
|
"hasDedicatedPage": false,
|
|
183
210
|
"category": "data"
|
|
184
211
|
},
|
|
212
|
+
{
|
|
213
|
+
"id": "email-suppression",
|
|
214
|
+
"name": "EmailSuppression",
|
|
215
|
+
"icon": "tablecells.fill",
|
|
216
|
+
"hasDedicatedPage": false,
|
|
217
|
+
"category": "data",
|
|
218
|
+
"dashboard": {
|
|
219
|
+
"enabled": false
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
"id": "email-webhook-event",
|
|
224
|
+
"name": "EmailWebhookEvent",
|
|
225
|
+
"icon": "tablecells.fill",
|
|
226
|
+
"hasDedicatedPage": false,
|
|
227
|
+
"category": "data",
|
|
228
|
+
"dashboard": {
|
|
229
|
+
"enabled": false
|
|
230
|
+
}
|
|
231
|
+
},
|
|
185
232
|
{
|
|
186
233
|
"id": "gift-card",
|
|
187
234
|
"name": "GiftCard",
|
|
@@ -456,6 +503,13 @@
|
|
|
456
503
|
"highlight": true
|
|
457
504
|
}
|
|
458
505
|
},
|
|
506
|
+
{
|
|
507
|
+
"id": "social-account",
|
|
508
|
+
"name": "SocialAccount",
|
|
509
|
+
"icon": "tablecells.fill",
|
|
510
|
+
"hasDedicatedPage": false,
|
|
511
|
+
"category": "data"
|
|
512
|
+
},
|
|
459
513
|
{
|
|
460
514
|
"id": "social-post",
|
|
461
515
|
"name": "SocialPost",
|