AccountLayout.tsx147 lines · main
1import { LOCAL_STORAGE_KEYS } from 'common'
2import Head from 'next/head'
3import { useRouter } from 'next/router'
4import type { PropsWithChildren } from 'react'
5import { useEffect, useLayoutEffect, useMemo } from 'react'
6import { cn } from 'ui'
7
8import { useMobileSheet } from '../Navigation/NavigationBar/MobileSheetContext'
9import { AccountMenuContent } from './AccountMenuContent'
10import { WithSidebar } from './WithSidebar'
11import { useCustomContent } from '@/hooks/custom-content/useCustomContent'
12import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
13import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage'
14import { withAuth } from '@/hooks/misc/withAuth'
15import { IS_PLATFORM } from '@/lib/constants'
16import { buildStudioPageTitle } from '@/lib/page-title'
17import { useAppStateSnapshot } from '@/state/app-state'
18
19export interface AccountLayoutProps {
20 title: string
21}
22
23const AccountLayout = ({ children, title }: PropsWithChildren<AccountLayoutProps>) => {
24 const router = useRouter()
25 const appSnap = useAppStateSnapshot()
26 const { setContent: setMobileSheetContent, registerOpenMenu } = useMobileSheet()
27 const currentPath = router.pathname
28
29 const showSecuritySettings = useIsFeatureEnabled('account:show_security_settings')
30
31 const { appTitle } = useCustomContent(['app:title'])
32 const brandTitle = appTitle || 'Briven'
33 const surfaceLabel = IS_PLATFORM ? 'Account' : 'Preferences'
34
35 const [lastVisitedOrganization] = useLocalStorageQuery(
36 LOCAL_STORAGE_KEYS.LAST_VISITED_ORGANIZATION,
37 ''
38 )
39
40 const backToDashboardURL =
41 appSnap.lastRouteBeforeVisitingAccountPage.length > 0
42 ? appSnap.lastRouteBeforeVisitingAccountPage
43 : IS_PLATFORM && !!lastVisitedOrganization
44 ? `/org/${lastVisitedOrganization}`
45 : IS_PLATFORM
46 ? '/organizations'
47 : '/project/default'
48
49 const pageTitle = buildStudioPageTitle({
50 section: title,
51 surface: surfaceLabel,
52 brand: brandTitle,
53 })
54
55 const sections = useMemo(
56 () =>
57 !IS_PLATFORM
58 ? [
59 {
60 key: 'preferences',
61 links: [
62 {
63 key: 'preferences',
64 label: 'Preferences',
65 href: '/account/me',
66 isActive: currentPath === '/account/me',
67 },
68 ],
69 },
70 ]
71 : [
72 {
73 key: 'account-settings',
74 heading: 'Account Settings',
75 links: [
76 {
77 key: 'preferences',
78 label: 'Preferences',
79 href: '/account/me',
80 isActive: currentPath === '/account/me',
81 },
82 {
83 key: 'access-tokens',
84 label: 'Access Tokens',
85 href: '/account/tokens',
86 isActive:
87 currentPath === '/account/tokens' || currentPath === '/account/tokens/scoped',
88 },
89 ...(showSecuritySettings
90 ? [
91 {
92 key: 'security',
93 label: 'Security',
94 href: '/account/security',
95 isActive: currentPath === '/account/security',
96 },
97 ]
98 : []),
99 ],
100 },
101 {
102 key: 'logs',
103 heading: 'Logs',
104 links: [
105 {
106 key: 'audit-logs',
107 label: 'Audit Logs',
108 href: '/account/audit',
109 isActive: currentPath === '/account/audit',
110 },
111 ],
112 },
113 ],
114 [currentPath, showSecuritySettings]
115 )
116
117 useLayoutEffect(() => {
118 const unregister = registerOpenMenu(() => {
119 setMobileSheetContent(
120 <AccountMenuContent sections={sections} onCloseSheet={() => setMobileSheetContent(null)} />
121 )
122 })
123 return unregister
124 }, [registerOpenMenu, setMobileSheetContent, sections])
125
126 useEffect(() => {
127 if (!IS_PLATFORM && currentPath !== '/account/me') {
128 router.push('/project/default')
129 }
130 }, [currentPath, router])
131
132 return (
133 <>
134 <Head>
135 <title>{pageTitle}</title>
136 <meta name="description" content="Briven Studio" />
137 </Head>
138 <div className={cn('flex flex-col w-screen h-[calc(100vh-48px)]')}>
139 <WithSidebar backToDashboardURL={backToDashboardURL} sections={sections}>
140 {children}
141 </WithSidebar>
142 </div>
143 </>
144 )
145}
146
147export default withAuth(AccountLayout)