OrganizationSettingsLayout.tsx238 lines · main
1import { useFlag, useParams } from 'common'
2import { PropsWithChildren } from 'react'
3
4import { useIsPlatformWebhooksEnabled } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext'
5import type { SidebarSection } from '@/components/layouts/AccountLayout/AccountLayout.types'
6import { WithSidebar } from '@/components/layouts/AccountLayout/WithSidebar'
7import { useCurrentPath } from '@/hooks/misc/useCurrentPath'
8import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
9
10interface OrganizationSettingsMenuItemsProps {
11 slug?: string
12 showSecuritySettings?: boolean
13 showSsoSettings?: boolean
14 showLegalDocuments?: boolean
15 showPlatformWebhooks?: boolean
16 showPrivateApps?: boolean
17}
18
19interface OrganizationSettingsSectionsProps extends OrganizationSettingsMenuItemsProps {
20 currentPath: string
21}
22
23export const normalizeOrganizationSettingsPath = (path: string) => path.split('#')[0]
24
25export const generateOrganizationSettingsMenuItems = ({
26 slug,
27 showSecuritySettings = true,
28 showSsoSettings = true,
29 showLegalDocuments = true,
30 showPlatformWebhooks = true,
31 showPrivateApps: _showPrivateApps = false,
32}: OrganizationSettingsMenuItemsProps) => [
33 {
34 key: 'general',
35 label: 'General',
36 href: `/org/${slug}/general`,
37 },
38 ...(showSecuritySettings
39 ? [
40 {
41 key: 'security',
42 label: 'Security',
43 href: `/org/${slug}/security`,
44 },
45 ]
46 : []),
47 {
48 key: 'apps',
49 label: 'OAuth Apps',
50 href: `/org/${slug}/apps`,
51 },
52 ...(showSsoSettings
53 ? [
54 {
55 key: 'sso',
56 label: 'SSO',
57 href: `/org/${slug}/sso`,
58 },
59 ]
60 : []),
61 ...(showPlatformWebhooks
62 ? [
63 {
64 key: 'webhooks',
65 label: 'Webhooks',
66 href: `/org/${slug}/webhooks`,
67 },
68 ]
69 : []),
70 {
71 key: 'audit',
72 label: 'Audit Logs',
73 href: `/org/${slug}/audit`,
74 },
75 ...(showLegalDocuments
76 ? [
77 {
78 key: 'documents',
79 label: 'Legal Documents',
80 href: `/org/${slug}/documents`,
81 },
82 ]
83 : []),
84]
85
86export const generateOrganizationSettingsSections = ({
87 currentPath,
88 slug,
89 showSecuritySettings = true,
90 showSsoSettings = true,
91 showLegalDocuments = true,
92 showPlatformWebhooks = true,
93 showPrivateApps = false,
94}: OrganizationSettingsSectionsProps): SidebarSection[] => {
95 const isLinkActive = (key: string, href: string) =>
96 key === 'webhooks'
97 ? currentPath === href || currentPath.startsWith(`${href}/`)
98 : currentPath === href
99
100 const configurationLinks = [
101 {
102 key: 'general',
103 label: 'General',
104 href: `/org/${slug}/general`,
105 },
106 ...(showSecuritySettings
107 ? [
108 {
109 key: 'security',
110 label: 'Security',
111 href: `/org/${slug}/security`,
112 },
113 ]
114 : []),
115 ...(showSsoSettings
116 ? [
117 {
118 key: 'sso',
119 label: 'SSO',
120 href: `/org/${slug}/sso`,
121 },
122 ]
123 : []),
124 ]
125
126 const connectionsLinks = [
127 {
128 key: 'apps',
129 label: 'OAuth Apps',
130 href: `/org/${slug}/apps`,
131 },
132 ...(showPrivateApps
133 ? [
134 {
135 key: 'private-apps',
136 label: 'Private Apps',
137 href: `/org/${slug}/private-apps`,
138 },
139 ]
140 : []),
141 ...(showPlatformWebhooks
142 ? [
143 {
144 key: 'webhooks',
145 label: 'Webhooks',
146 href: `/org/${slug}/webhooks`,
147 },
148 ]
149 : []),
150 ]
151
152 const complianceLinks = [
153 {
154 key: 'audit',
155 label: 'Audit Logs',
156 href: `/org/${slug}/audit`,
157 },
158 ...(showLegalDocuments
159 ? [
160 {
161 key: 'documents',
162 label: 'Legal Documents',
163 href: `/org/${slug}/documents`,
164 },
165 ]
166 : []),
167 ]
168
169 return [
170 {
171 key: 'configuration',
172 heading: 'Configuration',
173 links: configurationLinks.map((item) => ({
174 ...item,
175 isActive: isLinkActive(item.key, item.href),
176 })),
177 },
178 {
179 key: 'connections',
180 heading: 'Connections',
181 links: connectionsLinks.map((item) => ({
182 ...item,
183 isActive: isLinkActive(item.key, item.href),
184 })),
185 },
186 {
187 key: 'compliance',
188 heading: 'Compliance',
189 links: complianceLinks.map((item) => ({
190 ...item,
191 isActive: isLinkActive(item.key, item.href),
192 })),
193 },
194 ]
195}
196
197export function OrganizationSettingsLayout({ children }: PropsWithChildren) {
198 const { slug } = useParams()
199 const showPlatformWebhooks = useIsPlatformWebhooksEnabled()
200 const showPrivateApps = useFlag('privateApps')
201 const fullCurrentPath = useCurrentPath()
202 const currentPath = normalizeOrganizationSettingsPath(fullCurrentPath)
203
204 const {
205 organizationShowSsoSettings: showSsoSettings,
206 organizationShowSecuritySettings: showSecuritySettings,
207 organizationShowLegalDocuments: showLegalDocuments,
208 } = useIsFeatureEnabled([
209 'organization:show_sso_settings',
210 'organization:show_security_settings',
211 'organization:show_legal_documents',
212 ])
213
214 const sections = generateOrganizationSettingsSections({
215 currentPath,
216 slug,
217 showSecuritySettings,
218 showSsoSettings,
219 showLegalDocuments,
220 showPlatformWebhooks,
221 showPrivateApps,
222 })
223
224 // Browser titles for org settings routes are set by OrganizationLayout.
225 return (
226 <WithSidebar
227 title="Organization Settings"
228 sections={sections}
229 header={
230 <div className="border-default flex min-h-(--header-height) items-center border-b px-6">
231 <h4 className="text-lg">Settings</h4>
232 </div>
233 }
234 >
235 {children}
236 </WithSidebar>
237 )
238}