security.tsx91 lines · main
1import { Smartphone } from 'lucide-react'
2import { Badge, cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from 'ui'
3import { PageContainer } from 'ui-patterns/PageContainer'
4import {
5 PageHeader,
6 PageHeaderDescription,
7 PageHeaderMeta,
8 PageHeaderSummary,
9 PageHeaderTitle,
10} from 'ui-patterns/PageHeader'
11
12import { TOTPFactors } from '@/components/interfaces/Account/TOTPFactors'
13import AccountLayout from '@/components/layouts/AccountLayout/AccountLayout'
14import { AppLayout } from '@/components/layouts/AppLayout/AppLayout'
15import { DefaultLayout } from '@/components/layouts/DefaultLayout'
16import { UnknownInterface } from '@/components/ui/UnknownInterface'
17import { useMfaListFactorsQuery } from '@/data/profile/mfa-list-factors-query'
18import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
19import type { NextPageWithLayout } from '@/types'
20
21const collapsibleClasses = [
22 'bg-surface-100',
23 'hover:bg-surface-200',
24 'data-open:bg-surface-200',
25 'border-default',
26 'hover:border-strong data-open:border-strong',
27 'data-open:pb-px col-span-12 rounded-sm',
28 '-space-y-px overflow-hidden',
29 'border shadow-sm',
30 'transition',
31 'hover:z-50',
32]
33
34const Security: NextPageWithLayout = () => {
35 const showSecuritySettings = useIsFeatureEnabled('account:show_security_settings')
36
37 const { data } = useMfaListFactorsQuery({ enabled: showSecuritySettings })
38
39 if (!showSecuritySettings) {
40 return <UnknownInterface urlBack={`/account/me`} />
41 }
42
43 return (
44 <>
45 <PageHeader size="small">
46 <PageHeaderMeta>
47 <PageHeaderSummary>
48 <PageHeaderTitle>Security</PageHeaderTitle>
49 <PageHeaderDescription>
50 Manage your account security settings and authentication methods.
51 </PageHeaderDescription>
52 </PageHeaderSummary>
53 </PageHeaderMeta>
54 </PageHeader>
55 <PageContainer size="small">
56 <Collapsible className={cn('mt-8', collapsibleClasses)}>
57 <CollapsibleTrigger asChild>
58 <button
59 type="button"
60 className="group flex w-full items-center justify-between rounded-sm py-3 px-4 md:px-6 text-foreground"
61 >
62 <div className="flex flex-row gap-4 items-center py-1">
63 <Smartphone strokeWidth={1.5} />
64 <span className="text-sm">Authenticator app</span>
65 </div>
66
67 {data ? (
68 <Badge variant={data.totp.length === 0 ? 'default' : 'success'}>
69 {data.totp.length} app{data.totp.length === 1 ? '' : 's'} configured
70 </Badge>
71 ) : null}
72 </button>
73 </CollapsibleTrigger>
74 <CollapsibleContent className="group border-t border-default bg-surface-100 py-6 px-4 md:px-6 text-foreground">
75 <TOTPFactors />
76 </CollapsibleContent>
77 </Collapsible>
78 </PageContainer>
79 </>
80 )
81}
82
83Security.getLayout = (page) => (
84 <AppLayout>
85 <DefaultLayout headerTitle="Account">
86 <AccountLayout title="Security">{page}</AccountLayout>
87 </DefaultLayout>
88 </AppLayout>
89)
90
91export default Security