Skip to content

Commit b45a6ba

Browse files
committed
feat(projects): checkpoint project UI and environment settings redesign
1 parent 63d595e commit b45a6ba

252 files changed

Lines changed: 67313 additions & 2278 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.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { listOrganizationProjectsContract } from '@/lib/api/contracts/projects'
2+
import {
3+
defineInternalJsonRoute,
4+
internalRateLimits,
5+
internalSessionAuth,
6+
} from '@/lib/api/server/routes'
7+
import { internalPermissionGroupErrorPolicy } from '@/lib/api/server/routes/permission-groups'
8+
import {
9+
listOrganizationProjects,
10+
listOrganizationProjectsOperation,
11+
} from '@/lib/projects/application/organization-projects'
12+
13+
export const GET = defineInternalJsonRoute({
14+
contract: listOrganizationProjectsContract,
15+
auth: internalSessionAuth,
16+
operation: listOrganizationProjectsOperation,
17+
rateLimit: internalRateLimits.none({
18+
reason:
19+
'Read-only organization settings inventory follows existing workspace inventory admission',
20+
}),
21+
errorPolicy: internalPermissionGroupErrorPolicy,
22+
mapInput: ({ params }) => ({ organizationId: params.id }),
23+
useCase: listOrganizationProjects,
24+
present: (result) => result,
25+
})

‎apps/sim/app/api/v1/admin/organizations/[id]/data-retention/route.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* `DATA_RETENTION_ENABLED` (or `ENTERPRISE_ENABLED`) when billing is off.
1010
*
1111
* Body: any subset of `logRetentionHours`, `softDeleteRetentionHours`,
12-
* `taskCleanupHours`, `fileVersionRetentionHours`, `piiRedaction`, `retentionOverrides`.
12+
* `taskCleanupHours`, `fileVersionRetentionHours`, `piiRedaction`, `retentionOverrides`, `projectOverrides`.
1313
* Omitted keys keep their current value; `null` means "forever" for an hours field.
1414
*
1515
* Response: AdminSingleResponse<{ success, organizationId }>
@@ -85,6 +85,7 @@ export const PATCH = withRouteHandler(
8585
merged.piiRedaction = body.piiRedaction
8686
}
8787

88+
if (body.projectOverrides !== undefined) merged.projectOverrides = body.projectOverrides
8889
if (body.retentionOverrides !== undefined) {
8990
merged.retentionOverrides = body.retentionOverrides
9091
}
@@ -97,6 +98,7 @@ export const PATCH = withRouteHandler(
9798
const foreignTargetsReason = await getForeignWorkspaceTargetsReason({
9899
organizationId,
99100
retentionOverrides: body.retentionOverrides,
101+
projectOverrides: body.projectOverrides,
100102
piiRedaction: body.piiRedaction,
101103
})
102104
if (foreignTargetsReason) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { readWorkspaceDashboardContract } from '@/lib/api/contracts/dashboards'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
internalSessionAuth,
7+
} from '@/lib/api/server/routes'
8+
import { readWorkspaceDashboard } from '@/lib/dashboards/application/dashboards'
9+
import { dashboardOperations } from '@/lib/dashboards/application/operations'
10+
11+
export const GET = defineInternalJsonRoute({
12+
contract: readWorkspaceDashboardContract,
13+
auth: internalSessionAuth,
14+
operation: dashboardOperations.read,
15+
rateLimit: internalRateLimits.user({ bucketName: 'dashboards' }),
16+
errorPolicy: internalOrchestrationErrorPolicy,
17+
mapInput: ({ params }) => ({ workspaceId: params.id }),
18+
useCase: readWorkspaceDashboard,
19+
})

‎apps/sim/app/api/workspaces/[id]/fork/mapping/route.ts‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
createForkSecretMappingContract,
23
getForkMappingContract,
34
updateForkMappingContract,
45
} from '@/lib/api/contracts/workspace-fork'
@@ -8,6 +9,7 @@ import {
89
internalSessionAuth,
910
} from '@/lib/api/server/routes'
1011
import { internalForkErrorPolicy } from '@/ee/workspace-forking/api/route-policies'
12+
import { createWorkspaceForkSecretMapping } from '@/ee/workspace-forking/application/create-secret-mapping'
1113
import { getWorkspaceForkMappingDetails } from '@/ee/workspace-forking/application/mapping-details'
1214
import { forkOperations } from '@/ee/workspace-forking/application/operations'
1315
import { updateWorkspaceForkMappings } from '@/ee/workspace-forking/application/recovery-and-mappings'
@@ -38,3 +40,14 @@ export const PUT = defineInternalJsonRoute({
3840
useCase: updateWorkspaceForkMappings,
3941
present: (result) => ({ success: true as const, ...result }),
4042
})
43+
44+
export const POST = defineInternalJsonRoute({
45+
contract: createForkSecretMappingContract,
46+
auth: internalSessionAuth,
47+
operation: forkOperations.createSecretMapping,
48+
rateLimit: internalRateLimits.none({ reason: 'Preserve existing internal fork request policy' }),
49+
errorPolicy: internalForkErrorPolicy,
50+
mapInput: ({ params, body }) => ({ workspaceId: params.id, ...body }),
51+
useCase: createWorkspaceForkSecretMapping,
52+
present: ({ name }) => ({ name }),
53+
})
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { getWorkspaceSettingsNavigationContract } from '@/lib/api/contracts/workspace-settings'
2+
import {
3+
defineInternalJsonRoute,
4+
internalOrchestrationErrorPolicy,
5+
internalRateLimits,
6+
internalSessionAuth,
7+
} from '@/lib/api/server/routes'
8+
import {
9+
readWorkspaceSettingsNavigation,
10+
readWorkspaceSettingsNavigationOperation,
11+
} from '@/lib/settings/application/workspace-navigation'
12+
13+
export const GET = defineInternalJsonRoute({
14+
contract: getWorkspaceSettingsNavigationContract,
15+
auth: internalSessionAuth,
16+
operation: readWorkspaceSettingsNavigationOperation,
17+
rateLimit: internalRateLimits.none({
18+
reason: 'Read-only settings navigation, matching existing settings-page admission',
19+
}),
20+
errorPolicy: internalOrchestrationErrorPolicy,
21+
mapInput: ({ params }) => ({ workspaceId: params.id }),
22+
useCase: readWorkspaceSettingsNavigation,
23+
staticResponseHeaders: { 'Cache-Control': 'private, no-store' },
24+
})

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/chats-section/chats-section.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export function ChatsSection({
144144
<CollapsedChatFlyoutItem
145145
key={chat.id}
146146
chat={chat}
147-
isCurrentRoute={pathname === chat.href}
147+
isCurrentRoute={pathname === chat.href.split('?')[0]}
148148
isMenuOpen={menuOpenChatId === chat.id}
149149
isEditing={rename.editingId === chat.id}
150150
editValue={rename.value}
@@ -187,7 +187,7 @@ export function ChatsSection({
187187
key={chat.id}
188188
chat={chat}
189189
leading={leadingFor?.(chat)}
190-
isCurrentRoute={pathname === chat.href}
190+
isCurrentRoute={pathname === chat.href.split('?')[0]}
191191
isMenuOpen={menuOpenChatId === chat.id}
192192
onContextMenu={actions.onContextMenu}
193193
onMorePointerDown={actions.onMorePointerDown}

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/projects-section/index.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export { ProjectMarks } from './project-marks'
21
export { ProjectRow } from './project-row'
32
export { ProjectsSection } from './projects-section'
43
export { useProjectActions } from './use-project-actions'

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/projects-section/project-marks.tsx‎

Lines changed: 0 additions & 29 deletions
This file was deleted.

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/projects-section/project-row.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ import {
2323
SquareArrowUpRight,
2424
Trash,
2525
} from '@sim/emcn/icons'
26-
import Link from 'next/link'
2726
import { IdentityTile } from '@/components/identity-tile/identity-tile'
27+
import { SettingsGuardedLink as Link } from '@/components/settings/settings-guarded-link'
2828
import { useProjectActions } from '@/app/o/[organizationId]/components/organization-sidebar/components/projects-section/use-project-actions'
2929
import type { ProjectDragProps } from '@/app/o/[organizationId]/components/organization-sidebar/components/projects-section/use-project-order'
3030
import type { Project } from '@/app/o/[organizationId]/p/hooks/use-projects'

‎apps/sim/app/o/[organizationId]/components/organization-sidebar/components/projects-section/projects-section.tsx‎

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
11
'use client'
22

33
import { cn } from '@sim/emcn'
4+
import { useSearchParams } from 'next/navigation'
5+
import { useQueryStates } from 'nuqs'
46
import { organizationRoutes } from '@/lib/navigation/paths'
57
import { ProjectRow } from '@/app/o/[organizationId]/components/organization-sidebar/components/projects-section/project-row'
68
import {
79
type ProjectDragProps,
810
useProjectOrder,
911
} from '@/app/o/[organizationId]/components/organization-sidebar/components/projects-section/use-project-order'
12+
import { projectPaneParsers } from '@/app/o/[organizationId]/home/components/project-pane/search-params'
1013
import { type Project, useProjects } from '@/app/o/[organizationId]/p/hooks/use-projects'
1114
import { SidebarSection } from '@/app/workspace/[workspaceId]/w/components/sidebar/components'
1215
import { SIDEBAR_ITEM_GAP_CLASS } from '@/app/workspace/[workspaceId]/w/components/sidebar/constants'
1316

14-
/** Chats listed under an open project before "Show more". */
15-
const CHAT_PREVIEW = 3
16-
1717
interface ProjectsSectionProps {
1818
organizationId: string
1919
isCollapsed: boolean
2020
pathname: string | null
2121
}
2222

23-
/** The organization's projects, one per fork lineage, each with its chats underneath. */
23+
/** Projects select the pinned pane; chats remain independent organization conversations. */
2424
export function ProjectsSection({ organizationId, isCollapsed, pathname }: ProjectsSectionProps) {
2525
const { roots } = useProjects(organizationId)
2626
const { projects, dragProps, isAnyDragActive } = useProjectOrder(roots)
@@ -52,10 +52,6 @@ interface ProjectTreeProps {
5252
isAnyDragActive: boolean
5353
}
5454

55-
/**
56-
* A project row. Chats are organization-wide and can touch several projects, so they live in
57-
* the Chats section, each marked with the projects it worked in, rather than under one project.
58-
*/
5955
function ProjectTree({
6056
organizationId,
6157
project,
@@ -65,15 +61,23 @@ function ProjectTree({
6561
isAnyDragActive,
6662
}: ProjectTreeProps) {
6763
const routes = organizationRoutes(organizationId)
68-
const inProject = project.environments.some((environment) => {
69-
const base = `${routes.root}/p/${environment.workspaceId}`
70-
return pathname === base || pathname?.startsWith(`${base}/`)
71-
})
64+
const searchParams = useSearchParams()
65+
const [{ project: selectedWorkspace }] = useQueryStates(projectPaneParsers)
66+
const inProject = project.environments.some(
67+
(environment) => environment.workspaceId === selectedWorkspace
68+
)
69+
const query = new URLSearchParams(searchParams.toString())
70+
if (inProject) query.delete('project')
71+
else query.set('project', project.id)
72+
query.delete('section')
73+
query.set('pane', 'project')
74+
const chatPath = pathname?.startsWith(`/o/${organizationId}/chat/`) ? pathname : routes.home
75+
const href = `${chatPath}?${query}`
7276
return (
7377
<ProjectRow
7478
project={project}
75-
href={routes.project(project.id)}
76-
newChatHref={`${inProject && pathname ? pathname : routes.project(project.id)}?chat=new`}
79+
href={href}
80+
newChatHref={`${routes.home}?project=${encodeURIComponent(project.id)}&pane=project`}
7781
active={inProject}
7882
railCollapsed={railCollapsed}
7983
drag={drag}

0 commit comments

Comments
 (0)