Skip to content

Commit 06344a1

Browse files
revert(dashboards): restore dashboards as a separate workspace resource
Reverts "store dashboards as workspace files" and "move authoring guidance to Mothership like Sim Pages", restoring the dashboard resource: its page, folders, APIs, resource tags, Mothership dashboards/dashboard_folders tools, per-turn availability, the create-dashboard built-in skill, and file discovery. Removes the write-time diagnostics that only applied to plain files. Keeps the chart renderer fixes, readable parse errors, panel query-mode handling, the create-response revision, and the test cleanups. The authoring skill keeps its simplified form with the default-formatting note. Migrations are regenerated on top of staging in the next commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S6aTRnkiu7PxYZPNPXYEMV
1 parent 68e8448 commit 06344a1

98 files changed

Lines changed: 2699 additions & 387 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/docs/openapi-v2-files-audit.json‎

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4119,13 +4119,6 @@
41194119
"revision": {
41204120
"description": "Opaque token for the content this write produced. Send it back as `expectedRevision` on the next write. Absent for a file with no recorded content version.",
41214121
"type": "string"
4122-
},
4123-
"diagnostics": {
4124-
"description": "For a dashboard file, the YAML parse errors in the written content. Empty when it parses; the file is saved either way. Absent for other file types.",
4125-
"type": "array",
4126-
"items": {
4127-
"type": "string"
4128-
}
41294122
}
41304123
},
41314124
"required": [
@@ -4143,7 +4136,7 @@
41434136
],
41444137
"additionalProperties": false,
41454138
"title": "Created file",
4146-
"description": "A newly created workspace file, with the revision it produced and any dashboard parse errors."
4139+
"description": "A newly created workspace file, with the revision it produced."
41474140
},
41484141
"V2CreatedFileResponse": {
41494142
"type": "object",
@@ -4156,7 +4149,7 @@
41564149
"required": ["data"],
41574150
"additionalProperties": false,
41584151
"title": "Created file response",
4159-
"description": "A newly created workspace file, with the revision it produced and any dashboard parse errors.",
4152+
"description": "A newly created workspace file, with the revision it produced.",
41604153
"examples": [
41614154
{
41624155
"data": {
@@ -5963,13 +5956,6 @@
59635956
"revision": {
59645957
"description": "Opaque token for the content this write produced. Send it back as `expectedRevision` on the next write. Absent for a file with no recorded content version.",
59655958
"type": "string"
5966-
},
5967-
"diagnostics": {
5968-
"description": "For a dashboard file, the YAML parse errors in the written content. Empty when it parses; the file is saved either way. Absent for other file types.",
5969-
"type": "array",
5970-
"items": {
5971-
"type": "string"
5972-
}
59735959
}
59745960
},
59755961
"required": ["file", "lineCount"],
@@ -6372,13 +6358,6 @@
63726358
"revision": {
63736359
"description": "Opaque token for the content this write produced. Send it back as `expectedRevision` on the next write. Absent for a file with no recorded content version.",
63746360
"type": "string"
6375-
},
6376-
"diagnostics": {
6377-
"description": "For a dashboard file, the YAML parse errors in the written content. Empty when it parses; the file is saved either way. Absent for other file types.",
6378-
"type": "array",
6379-
"items": {
6380-
"type": "string"
6381-
}
63826361
}
63836362
},
63846363
"required": [

‎apps/sim/app/api/files/uploads/finalizers.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
import { getWorkspaceFileSize, type StorageContext } from '@/lib/uploads/shared/types'
2323
import { UploadSessionError, type UploadSessionRecord } from '@/lib/uploads/upload-session/service'
2424
import { readWorkspaceFileUploadProvenance } from '@/lib/uploads/upload-session/workspace-file-provenance'
25+
import { defaultFileDiscovery } from '@/lib/workspace-files/discovery'
2526
import { toV2File } from '@/app/api/v2/files/utils'
2627

2728
export interface UploadActor {
@@ -376,6 +377,7 @@ async function insertOrLoadFileMetadata(
376377
userId: input.userId,
377378
workspaceId: input.workspaceId,
378379
context: input.context,
380+
discovery: defaultFileDiscovery(input.context),
379381
originalName: input.originalName,
380382
displayName: input.originalName,
381383
contentType: input.contentType,

‎apps/sim/app/api/v2/files/[fileId]/content/route.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,10 @@ export const PUT = defineV2JsonRoute({
4040
expectedRevision: body.expectedRevision,
4141
}),
4242
useCase: updateWorkspaceFileContent,
43-
present: async ({ file, diagnostics }) => ({
43+
present: async ({ file }) => ({
4444
data: {
4545
...(await toV2File(file)),
4646
...workspaceFileRevisionField(file),
47-
...(diagnostics ? { diagnostics } : {}),
4847
},
4948
}),
5049
})
@@ -81,12 +80,11 @@ export const PATCH = defineV2JsonRoute({
8180
expectedRevision: body.expectedRevision,
8281
}),
8382
useCase: editWorkspaceFileContent,
84-
present: async ({ file, lineCount, diagnostics }) => ({
83+
present: async ({ file, lineCount }) => ({
8584
data: {
8685
file: await toV2File(file),
8786
lineCount,
8887
...workspaceFileRevisionField(file),
89-
...(diagnostics ? { diagnostics } : {}),
9088
},
9189
}),
9290
})

‎apps/sim/app/api/v2/files/route.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,10 @@ export const POST = defineV2JsonRoute({
9494
exactName: true,
9595
}),
9696
useCase: createWorkspaceFile,
97-
present: async ({ file, diagnostics }) => ({
97+
present: async ({ file }) => ({
9898
data: {
9999
...(await toV2File(file)),
100100
...workspaceFileRevisionField(file),
101-
...(diagnostics ? { diagnostics } : {}),
102101
},
103102
}),
104103
})
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {
2+
deleteDashboardContract,
3+
moveDashboardContract,
4+
readDashboardContract,
5+
updateDashboardContract,
6+
} from '@/lib/api/contracts/dashboards'
7+
import {
8+
defineInternalJsonRoute,
9+
internalOrchestrationErrorPolicy,
10+
internalRateLimits,
11+
internalSessionAuth,
12+
} from '@/lib/api/server/routes'
13+
import {
14+
deleteDashboard,
15+
moveDashboard,
16+
readDashboard,
17+
updateDashboard,
18+
} from '@/lib/dashboards/application/dashboards'
19+
import { dashboardOperations } from '@/lib/dashboards/application/operations'
20+
21+
export const GET = defineInternalJsonRoute({
22+
contract: readDashboardContract,
23+
auth: internalSessionAuth,
24+
operation: dashboardOperations.read,
25+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
26+
errorPolicy: internalOrchestrationErrorPolicy,
27+
mapInput: ({ params }) => ({ workspaceId: params.id, dashboardId: params.dashboardId }),
28+
useCase: readDashboard,
29+
})
30+
31+
export const PUT = defineInternalJsonRoute({
32+
contract: updateDashboardContract,
33+
auth: internalSessionAuth,
34+
operation: dashboardOperations.update,
35+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
36+
errorPolicy: internalOrchestrationErrorPolicy,
37+
mapInput: ({ params, body }) => ({
38+
workspaceId: params.id,
39+
dashboardId: params.dashboardId,
40+
...body,
41+
}),
42+
useCase: updateDashboard,
43+
})
44+
45+
export const PATCH = defineInternalJsonRoute({
46+
contract: moveDashboardContract,
47+
auth: internalSessionAuth,
48+
operation: dashboardOperations.move,
49+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
50+
errorPolicy: internalOrchestrationErrorPolicy,
51+
mapInput: ({ params, body }) => ({
52+
workspaceId: params.id,
53+
dashboardId: params.dashboardId,
54+
...body,
55+
}),
56+
useCase: moveDashboard,
57+
})
58+
59+
export const DELETE = defineInternalJsonRoute({
60+
contract: deleteDashboardContract,
61+
auth: internalSessionAuth,
62+
operation: dashboardOperations.delete,
63+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
64+
errorPolicy: internalOrchestrationErrorPolicy,
65+
mapInput: ({ params }) => ({ workspaceId: params.id, dashboardId: params.dashboardId }),
66+
useCase: deleteDashboard,
67+
})
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import {
2+
createDashboardFolderContract,
3+
deleteDashboardFolderContract,
4+
listDashboardFoldersContract,
5+
moveDashboardFolderContract,
6+
} from '@/lib/api/contracts/dashboards'
7+
import {
8+
defineInternalJsonRoute,
9+
internalOrchestrationErrorPolicy,
10+
internalRateLimits,
11+
internalSessionAuth,
12+
} from '@/lib/api/server/routes'
13+
import {
14+
createDashboardFolder,
15+
deleteDashboardFolder,
16+
listDashboardFolders,
17+
moveDashboardFolder,
18+
} from '@/lib/dashboards/application/folders'
19+
import { dashboardOperations } from '@/lib/dashboards/application/operations'
20+
21+
export const GET = defineInternalJsonRoute({
22+
contract: listDashboardFoldersContract,
23+
auth: internalSessionAuth,
24+
operation: dashboardOperations.folders,
25+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
26+
errorPolicy: internalOrchestrationErrorPolicy,
27+
mapInput: ({ params }) => ({ workspaceId: params.id }),
28+
useCase: listDashboardFolders,
29+
})
30+
31+
export const POST = defineInternalJsonRoute({
32+
contract: createDashboardFolderContract,
33+
auth: internalSessionAuth,
34+
operation: dashboardOperations.createFolder,
35+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
36+
errorPolicy: internalOrchestrationErrorPolicy,
37+
mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }),
38+
useCase: createDashboardFolder,
39+
})
40+
41+
export const PATCH = defineInternalJsonRoute({
42+
contract: moveDashboardFolderContract,
43+
auth: internalSessionAuth,
44+
operation: dashboardOperations.moveFolder,
45+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
46+
errorPolicy: internalOrchestrationErrorPolicy,
47+
mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }),
48+
useCase: moveDashboardFolder,
49+
})
50+
51+
export const DELETE = defineInternalJsonRoute({
52+
contract: deleteDashboardFolderContract,
53+
auth: internalSessionAuth,
54+
operation: dashboardOperations.deleteFolder,
55+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
56+
errorPolicy: internalOrchestrationErrorPolicy,
57+
mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }),
58+
useCase: deleteDashboardFolder,
59+
})
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { createDashboardContract, listDashboardsContract } from '@/lib/api/contracts/dashboards'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
internalSessionAuth,
7+
} from '@/lib/api/server/routes'
8+
import { createDashboard, listDashboards } from '@/lib/dashboards/application/dashboards'
9+
import { dashboardOperations } from '@/lib/dashboards/application/operations'
10+
11+
export const GET = defineInternalJsonRoute({
12+
contract: listDashboardsContract,
13+
auth: internalSessionAuth,
14+
operation: dashboardOperations.list,
15+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
16+
errorPolicy: internalOrchestrationErrorPolicy,
17+
mapInput: ({ params, query }) => ({ workspaceId: params.id, ...query }),
18+
useCase: listDashboards,
19+
})
20+
21+
export const POST = defineInternalJsonRoute({
22+
contract: createDashboardContract,
23+
auth: internalSessionAuth,
24+
operation: dashboardOperations.create,
25+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
26+
errorPolicy: internalOrchestrationErrorPolicy,
27+
mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }),
28+
useCase: createDashboard,
29+
})

‎apps/sim/app/o/[organizationId]/home/components/composer/composer.tsx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from '@/app/workspace/[workspaceId]/home/components/user-input/components/prompt-editor'
2626
import { organizationSkillOptions } from '@/app/workspace/[workspaceId]/home/components/user-input/components/skills-menu-dropdown/organization-skill-options'
2727
import type { ChatRequestMode } from '@/app/workspace/[workspaceId]/home/types'
28+
import { useFeatureFlag } from '@/app/workspace/[workspaceId]/providers/feature-flags-provider'
2829
import type { useFileAttachments } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/hooks/use-file-attachments'
2930
import { SKILL_CHIP_TRIGGER } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/utils'
3031
import { getSkillsQueryOptions } from '@/hooks/queries/skills'
@@ -75,6 +76,7 @@ export function Composer({
7576
attachedFilesRef.current = files.attachedFiles
7677
const imagesOnly = requestMode === 'assistant'
7778
const { organization } = useOrganizationContext()
79+
const dashboardsEnabled = useFeatureFlag('dashboards')
7880
const { data: allWorkspaces = [] } = useWorkspacesQuery(!imagesOnly)
7981
const workspaces = (imagesOnly ? [] : allWorkspaces).filter(
8082
(workspace) => workspace.organizationId === organization.id
@@ -88,7 +90,8 @@ export function Composer({
8890
workspaces.map((workspace, index) => ({
8991
...workspace,
9092
skills: skillQueries[index].isPlaceholderData ? [] : (skillQueries[index].data ?? []),
91-
}))
93+
})),
94+
dashboardsEnabled ? [] : ['builtin-create-dashboard']
9295
)
9396
const editor = usePromptEditor({
9497
workspaceId: '',

‎apps/sim/app/workspace/[workspaceId]/components/folders/foldered-resources.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ComponentType } from 'react'
2-
import { Database, File as FileIcon, Table as TableIcon } from '@sim/emcn/icons'
2+
import { ChartColumn, Database, File as FileIcon, Table as TableIcon } from '@sim/emcn/icons'
33
import type { FolderResourceType } from '@/lib/api/contracts/folders'
44
import { folderListHref } from '@/app/workspace/[workspaceId]/components/folders/search-params'
55

@@ -10,7 +10,7 @@ import { folderListHref } from '@/app/workspace/[workspaceId]/components/folders
1010
*/
1111
export type FolderedHeaderResourceType = Extract<
1212
FolderResourceType,
13-
'file' | 'knowledge_base' | 'table'
13+
'file' | 'knowledge_base' | 'table' | 'dashboard'
1414
>
1515

1616
export interface FolderedResourceHeaderMeta {
@@ -36,6 +36,7 @@ export const FOLDERED_RESOURCE_HEADERS: Record<
3636
file: { rootLabel: 'Files', rootIcon: FileIcon, listSegment: 'files' },
3737
knowledge_base: { rootLabel: 'Knowledge bases', rootIcon: Database, listSegment: 'knowledge' },
3838
table: { rootLabel: 'Tables', rootIcon: TableIcon, listSegment: 'tables' },
39+
dashboard: { rootLabel: 'Dashboards', rootIcon: ChartColumn, listSegment: 'dashboards' },
3940
}
4041

4142
/**
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { DashboardResource } from '@/components/dashboards/dashboard-resource'
2+
export default async function DashboardPage({
3+
params,
4+
}: {
5+
params: Promise<{ workspaceId: string; dashboardId: string }>
6+
}) {
7+
return <DashboardResource {...(await params)} />
8+
}

0 commit comments

Comments
 (0)