Skip to content

Commit a61927e

Browse files
feat: update dpa retrieval (supabase#37563)
* feat: update dpa retrieval * add tracking for other legal docs * ci: Autofix updates from GitHub workflow * update api types * update pdf * Refactor DPA with text confirm modal * Nit consistency with using a and buttons * Update TextConfirmModal --------- Co-authored-by: pamelachia <[email protected]> Co-authored-by: Joshen Lim <[email protected]>
1 parent d0972be commit a61927e

File tree

15 files changed

+857
-198
lines changed

15 files changed

+857
-198
lines changed

apps/studio/components/interfaces/Organization/Documents/DPA.tsx

Lines changed: 92 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,108 @@
1-
import { ExternalLink } from 'lucide-react'
1+
import { useState } from 'react'
2+
import { toast } from 'sonner'
23

34
import {
45
ScaffoldSection,
56
ScaffoldSectionContent,
67
ScaffoldSectionDetail,
78
} from 'components/layouts/Scaffold'
9+
import { InlineLink } from 'components/ui/InlineLink'
10+
import { useDpaRequestMutation } from 'data/documents/dpa-request-mutation'
11+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
12+
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
13+
import { useProfile } from 'lib/profile'
814
import { Button } from 'ui'
15+
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
16+
17+
export const DPA = () => {
18+
const { profile } = useProfile()
19+
const { data: organization } = useSelectedOrganizationQuery()
20+
const slug = organization?.slug
21+
22+
const [isOpen, setIsOpen] = useState(false)
23+
24+
const { mutate: sendEvent } = useSendEventMutation()
25+
const { mutate: requestDpa, isLoading: isRequesting } = useDpaRequestMutation({
26+
onSuccess: () => {
27+
toast.success('DPA request sent successfully')
28+
setIsOpen(false)
29+
},
30+
})
31+
32+
const onConfirmRequest = async () => {
33+
if (!slug) return toast.error('Organization not found.')
34+
if (!profile?.primary_email) return toast.error('Profile email not found.')
35+
requestDpa({ recipient_email: profile?.primary_email, slug: slug })
36+
}
937

10-
const DPA = () => {
1138
return (
12-
<ScaffoldSection>
13-
<ScaffoldSectionDetail className="sticky space-y-6 top-12">
14-
<p className="text-base m-0">Data Processing Addendum (DPA)</p>
15-
<div className="space-y-2 text-sm text-foreground-light m-0">
39+
<>
40+
<ScaffoldSection>
41+
<ScaffoldSectionDetail className="sticky space-y-6 top-12">
42+
<p className="text-base m-0">Data Processing Addendum (DPA)</p>
43+
<div className="space-y-2 text-sm text-foreground-light m-0">
44+
<p>
45+
All organizations can sign our Data Processing Addendum ("DPA") as part of their GDPR
46+
compliance.
47+
</p>
48+
<p>
49+
You can review a static PDF version of our latest DPA document{' '}
50+
<InlineLink
51+
href="https://supabase.com/downloads/docs/Supabase+DPA+250805.pdf"
52+
onClick={() =>
53+
sendEvent({
54+
action: 'dpa_pdf_opened',
55+
properties: { source: 'studio' },
56+
})
57+
}
58+
>
59+
here
60+
</InlineLink>
61+
.
62+
</p>
63+
</div>
64+
</ScaffoldSectionDetail>
65+
<ScaffoldSectionContent className="flex items-center justify-center h-full">
66+
<Button
67+
onClick={() => {
68+
setIsOpen(true)
69+
sendEvent({
70+
action: 'dpa_request_button_clicked',
71+
})
72+
}}
73+
type="default"
74+
>
75+
Request DPA
76+
</Button>
77+
</ScaffoldSectionContent>
78+
</ScaffoldSection>
79+
80+
<TextConfirmModal
81+
visible={isOpen}
82+
title="Request executable DPA to sign"
83+
loading={isRequesting}
84+
confirmPlaceholder="Enter your email address"
85+
confirmString={profile?.primary_email ?? ''}
86+
confirmLabel="Send DPA request"
87+
errorMessage="Email must match your account email."
88+
onCancel={() => setIsOpen(false)}
89+
onConfirm={() => onConfirmRequest()}
90+
>
91+
<div className="space-y-2 text-sm">
92+
<p>
93+
To make the DPA legally binding, you need to sign and complete the details through a
94+
PandaDoc document that we prepare.
95+
</p>
1696
<p>
17-
All organizations can access and use our DPA as part of their GDPR compliance. This is
18-
only to access the document. Please ignore this if you have sent the signed document to
19-
us.
97+
Please enter your email address to request an executable version of the DPA. You will
98+
receive a document link via PandaDoc in the next 24 hours.
2099
</p>
21100
<p>
22-
Please review these details and add data specific to user processing where required.
23-
Sign and return the signed DPA document to{' '}
24-
<a
25-
href="mailto:[email protected]"
26-
target="_blank"
27-
className="text-brand hover:text-brand"
28-
>
29-
30-
</a>{' '}
31-
for the document to be considered executed.
101+
Once signed, the DPA will be considered executed and you'll be notified of any future
102+
updates via this email.
32103
</p>
33104
</div>
34-
</ScaffoldSectionDetail>
35-
<ScaffoldSectionContent className="flex items-center justify-center h-full">
36-
<Button asChild type="default" iconRight={<ExternalLink />}>
37-
<a
38-
download={true}
39-
target="_blank"
40-
rel="noreferrer noopener"
41-
href="https://supabase.com/downloads/docs/Supabase+DPA+250314.pdf"
42-
>
43-
View DPA
44-
</a>
45-
</Button>
46-
</ScaffoldSectionContent>
47-
</ScaffoldSection>
105+
</TextConfirmModal>
106+
</>
48107
)
49108
}
50-
51-
export default DPA

apps/studio/components/interfaces/Organization/Documents/Documents.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { ScaffoldContainer, ScaffoldDivider, ScaffoldSection } from 'components/layouts/Scaffold'
2-
import DPA from './DPA'
3-
import TIA from './TIA'
4-
import SecurityQuestionnaire from './SecurityQuestionnaire'
5-
import SOC2 from './SOC2'
6-
import HIPAA from './HIPAA'
71
import Link from 'next/link'
82

3+
import { ScaffoldContainer, ScaffoldDivider, ScaffoldSection } from 'components/layouts/Scaffold'
4+
import { DPA } from './DPA'
5+
import { HIPAA } from './HIPAA'
6+
import { SecurityQuestionnaire } from './SecurityQuestionnaire'
7+
import { SOC2 } from './SOC2'
8+
import { TIA } from './TIA'
9+
910
const Documents = () => {
1011
return (
1112
<>

apps/studio/components/interfaces/Organization/Documents/HIPAA.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import { ExternalLink } from 'lucide-react'
2+
13
import {
24
ScaffoldSection,
35
ScaffoldSectionContent,
46
ScaffoldSectionDetail,
57
} from 'components/layouts/Scaffold'
6-
import { ExternalLink } from 'lucide-react'
8+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
9+
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
710
import { Button } from 'ui'
811

9-
const HIPAA = () => {
12+
export const HIPAA = () => {
13+
const { data: organization } = useSelectedOrganizationQuery()
14+
const { mutate: sendEvent } = useSendEventMutation()
15+
1016
return (
1117
<>
1218
<ScaffoldSection>
@@ -29,16 +35,24 @@ const HIPAA = () => {
2935
</ScaffoldSectionDetail>
3036
<ScaffoldSectionContent>
3137
<div className="flex items-center justify-center h-full">
32-
<a href="https://forms.supabase.com/hipaa2" target="_blank" rel="noreferrer noopener">
33-
<Button type="default" iconRight={<ExternalLink />}>
38+
<Button asChild type="default" iconRight={<ExternalLink />}>
39+
<a
40+
href="https://forms.supabase.com/hipaa2"
41+
target="_blank"
42+
rel="noreferrer noopener"
43+
onClick={() =>
44+
sendEvent({
45+
action: 'hipaa_request_button_clicked',
46+
groups: { organization: organization?.slug ?? 'Unknown' },
47+
})
48+
}
49+
>
3450
Request HIPAA
35-
</Button>
36-
</a>
51+
</a>
52+
</Button>
3753
</div>
3854
</ScaffoldSectionContent>
3955
</ScaffoldSection>
4056
</>
4157
)
4258
}
43-
44-
export default HIPAA

apps/studio/components/interfaces/Organization/Documents/SOC2.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@ import {
1111
} from 'components/layouts/Scaffold'
1212
import NoPermission from 'components/ui/NoPermission'
1313
import { getDocument } from 'data/documents/document-query'
14+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
1415
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1516
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
1617
import { Button } from 'ui'
1718
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
1819

19-
const SOC2 = () => {
20+
export const SOC2 = () => {
2021
const { data: organization } = useSelectedOrganizationQuery()
2122
const slug = organization?.slug
23+
const { mutate: sendEvent } = useSendEventMutation()
2224
const canReadSubscriptions = useCheckPermissions(
2325
PermissionAction.BILLING_READ,
2426
'stripe.subscriptions'
@@ -58,7 +60,18 @@ const SOC2 = () => {
5860
<Button type="default">Upgrade to Team</Button>
5961
</Link>
6062
) : (
61-
<Button type="default" icon={<Download />} onClick={() => setIsOpen(true)}>
63+
<Button
64+
type="default"
65+
icon={<Download />}
66+
onClick={() => {
67+
sendEvent({
68+
action: 'document_view_button_clicked',
69+
properties: { documentName: 'SOC2' },
70+
groups: { organization: organization?.slug ?? 'Unknown' },
71+
})
72+
setIsOpen(true)
73+
}}
74+
>
6275
Download SOC2 Type 2 Report
6376
</Button>
6477
)}
@@ -111,5 +124,3 @@ const SOC2 = () => {
111124
</ScaffoldSection>
112125
)
113126
}
114-
115-
export default SOC2

apps/studio/components/interfaces/Organization/Documents/SecurityQuestionnaire.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
22
import { Download } from 'lucide-react'
33
import Link from 'next/link'
44
import { toast } from 'sonner'
5-
import { Button } from 'ui'
65

76
import {
87
ScaffoldSection,
@@ -11,12 +10,15 @@ import {
1110
} from 'components/layouts/Scaffold'
1211
import NoPermission from 'components/ui/NoPermission'
1312
import { getDocument } from 'data/documents/document-query'
13+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
1414
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
1515
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
16+
import { Button } from 'ui'
1617

17-
const SecurityQuestionnaire = () => {
18+
export const SecurityQuestionnaire = () => {
1819
const { data: organization } = useSelectedOrganizationQuery()
1920
const slug = organization?.slug
21+
const { mutate: sendEvent } = useSendEventMutation()
2022
const canReadSubscriptions = useCheckPermissions(
2123
PermissionAction.BILLING_READ,
2224
'stripe.subscriptions'
@@ -65,6 +67,11 @@ const SecurityQuestionnaire = () => {
6567
type="default"
6668
icon={<Download />}
6769
onClick={() => {
70+
sendEvent({
71+
action: 'document_view_button_clicked',
72+
properties: { documentName: 'Standard Security Questionnaire' },
73+
groups: { organization: organization?.slug ?? 'Unknown' },
74+
})
6875
if (slug) fetchQuestionnaire(slug)
6976
}}
7077
>
@@ -79,5 +86,3 @@ const SecurityQuestionnaire = () => {
7986
</>
8087
)
8188
}
82-
83-
export default SecurityQuestionnaire

apps/studio/components/interfaces/Organization/Documents/TIA.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import { ExternalLink } from 'lucide-react'
2+
13
import {
24
ScaffoldSection,
35
ScaffoldSectionContent,
46
ScaffoldSectionDetail,
57
} from 'components/layouts/Scaffold'
6-
import { ExternalLink } from 'lucide-react'
8+
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
9+
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
710
import { Button } from 'ui'
811

9-
const TIA = () => {
12+
export const TIA = () => {
13+
const { data: organization } = useSelectedOrganizationQuery()
14+
const { mutate: sendEvent } = useSendEventMutation()
15+
1016
return (
1117
<ScaffoldSection>
1218
<ScaffoldSectionDetail className="sticky space-y-6 top-12">
@@ -19,19 +25,24 @@ const TIA = () => {
1925
</div>
2026
</ScaffoldSectionDetail>
2127
<ScaffoldSectionContent className="flex items-center justify-center h-full">
22-
<a
23-
href="https://supabase.com/downloads/docs/Supabase+TIA+250314.pdf"
24-
target="_blank"
25-
rel="noreferrer noopener"
26-
download={true}
27-
>
28-
<Button type="default" iconRight={<ExternalLink />}>
28+
<Button asChild type="default" iconRight={<ExternalLink />}>
29+
<a
30+
href="https://supabase.com/downloads/docs/Supabase+TIA+250314.pdf"
31+
target="_blank"
32+
rel="noreferrer noopener"
33+
download={true}
34+
onClick={() =>
35+
sendEvent({
36+
action: 'document_view_button_clicked',
37+
properties: { documentName: 'TIA' },
38+
groups: { organization: organization?.slug ?? 'Unknown' },
39+
})
40+
}
41+
>
2942
View TIA
30-
</Button>
31-
</a>
43+
</a>
44+
</Button>
3245
</ScaffoldSectionContent>
3346
</ScaffoldSection>
3447
)
3548
}
36-
37-
export default TIA

apps/studio/components/interfaces/Organization/SSO/SSOConfig.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,9 @@ export const SSOConfig = () => {
145145
metadataXmlUrl: ssoConfig.metadata_xml_url,
146146
metadataXmlFile: ssoConfig.metadata_xml_file,
147147
emailMapping: ssoConfig.email_mapping.map((email) => ({ value: email })),
148-
userNameMapping: ssoConfig.user_name_mapping.map((userName) => ({ value: userName })),
149-
firstNameMapping: ssoConfig.first_name_mapping.map((firstName) => ({ value: firstName })),
150-
lastNameMapping: ssoConfig.last_name_mapping.map((lastName) => ({ value: lastName })),
148+
userNameMapping: ssoConfig.user_name_mapping?.map((userName) => ({ value: userName })),
149+
firstNameMapping: ssoConfig.first_name_mapping?.map((firstName) => ({ value: firstName })),
150+
lastNameMapping: ssoConfig.last_name_mapping?.map((lastName) => ({ value: lastName })),
151151
joinOrgOnSignup: ssoConfig.join_org_on_signup_enabled,
152152
roleOnJoin: ssoConfig.join_org_on_signup_role,
153153
})

apps/studio/components/ui/InlineLink.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ interface InlineLinkProps {
77
className?: string
88
target?: string
99
rel?: string
10+
onClick?: () => void
1011
}
1112

1213
export const InlineLinkClassName =

0 commit comments

Comments
 (0)