Files
supabase/apps/studio/components/interfaces/Organization/Documents/DPA.tsx
Pamela Chia 0bed80b340 chore(telemetry): clean up frontend event catalog (#45964)
## Summary
Resolves 13 findings (2 HIGH, 5 MEDIUM, 6 LOW) from the frontend
telemetry audit: 1 action-string collision, 1 camelCase experiment name,
9 dead events removed, 4 missing org groups attached, 1 ambiguous
property renamed, 1 raw-string property narrowed, plus consolidations
and a structural tightening on TABLE_EVENT_ACTIONS.

## Changes
### HIGH
- Rename `EventPageCtaClickedEvent.action` to
`www_event_page_cta_clicked` so it no longer collides with the pricing
CTA event (which had a different schema sharing the same action string)
- Snake_case the header-upgrade experiment exposure name
(`headerUpgradeCta_experiment_exposed` →
`header_upgrade_cta_experiment_exposed`); PostHog flag key and
`?source=` URL param unchanged

### MEDIUM
- Remove 4 dead `ProjectCreation*Step*` events (referenced a v2 route
that doesn't exist; 0 emissions)
- Remove 4 dead experiment exposure events:
`ProjectCreationRlsOptionExperimentExposed`, `HomeNewExperimentExposed`,
`TableCreateGeneratePoliciesExperimentExposed`,
`TableCreateGeneratePoliciesExperimentConverted` (0 emissions)
- Attach org group to `dpa_request_button_clicked` (0% had `$group_0`
per Hex)
- Delete `RegisterStateOfStartups2025NewsletterClicked` (interface
naming outlier, 0 emissions, page renamed to 2026)
- Rename `AssistantSuggestionRunQueryClickedEvent.category` to
`mutationType` with tightened literal union (`'functions' |
'rls-policies' | 'unknown'`)
- Attach org group to `project_creation_default_privileges_exposed` on
Vercel surface via explicit `groupOverrides` (auto-injection misses
because `useSelectedOrganizationQuery` is undefined on that page)

### LOW
- Consolidate `IndexAdvisorBannerEnableButtonClickedEvent` +
`IndexAdvisorDialogEnableButtonClickedEvent` into one event with
`origin: 'banner' | 'dialog'`
- Rename `ImportDataFileDroppedEvent` → `ImportDataFileAddedEvent` so
the interface name matches the action and the verb is on the approved
list
- Rename `LogDrainConfirmButtonSubmittedEvent` → `LogDrainRemovedEvent`
and action to `log_drain_removed` (fires on delete-confirm modal,
matches `CronJobRemovedEvent` pattern)
- Add `type` property to `CronJobRemovedEvent` (parsed from the job's
command), matching the create/update event shape
- Tighten `TABLE_EVENT_ACTIONS` values with `satisfies` against the
event union so renames in the union fail typecheck here too
- Attach org group to `www_pricing_plan_cta_clicked` at 5 emission sites
when an org is available in the page context
- Narrow `unified_logs_row_clicked.logType` from raw `string` to the
5-literal `LOG_TYPES` union (zod already validates server values)

### Bundled refactor
Migrated 5 emission sites from deprecated `useSendEventMutation` to
`useTrack` while their containing files were being edited: `DPA.tsx`,
`DisplayBlockRenderer.tsx`, `Grid.tsx` (2 events), `DeleteCronJob.tsx`.
Full sweep of the remaining ~79 files is a separate follow-up.

## Testing

Mostly just renaming of events

## Linear
- fixes GROWTH-798


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Standardized telemetry to a unified tracking system for more
consistent analytics.
  * Simplified experiment exposure reporting for upgrade prompts.

* **New Features**
* More granular tracking for CSV import, cron job deletions, log drain
removals, DPA downloads/requests, and pricing CTAs.
  * Assistant now classifies mutation queries more precisely.

* **Bug Fixes**
* Improved default-privileges exposure logic on Vercel deployments
(skips when org missing).

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/45964)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-18 18:21:58 +08:00

104 lines
3.5 KiB
TypeScript

import { useState } from 'react'
import { toast } from 'sonner'
import { Button } from 'ui'
import {
ScaffoldSection,
ScaffoldSectionContent,
ScaffoldSectionDetail,
} from '@/components/layouts/Scaffold'
import { InlineLink } from '@/components/ui/InlineLink'
import { TextConfirmModal } from '@/components/ui/TextConfirmModalWrapper'
import { useDpaRequestMutation } from '@/data/documents/dpa-request-mutation'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
import { useProfile } from '@/lib/profile'
import { useTrack } from '@/lib/telemetry/track'
export const DPA = () => {
const { profile } = useProfile()
const { data: organization } = useSelectedOrganizationQuery()
const slug = organization?.slug
const [isOpen, setIsOpen] = useState(false)
const track = useTrack()
const { mutate: requestDpa, isPending: isRequesting } = useDpaRequestMutation({
onSuccess: () => {
toast.success('DPA request sent successfully')
setIsOpen(false)
},
})
const onConfirmRequest = async () => {
if (!slug) return toast.error('Organization not found.')
if (!profile?.primary_email) return toast.error('Profile email not found.')
requestDpa({ recipient_email: profile?.primary_email, slug: slug })
}
return (
<>
<ScaffoldSection className="py-12">
<ScaffoldSectionDetail>
<h4 className="mb-5">Data Processing Addendum (DPA)</h4>
<div className="space-y-2 text-sm text-foreground-light [&_p]:m-0">
<p>
All organizations can sign our Data Processing Addendum ("DPA") as part of their GDPR
compliance.
</p>
<p>
You can review a static PDF version of our latest DPA document{' '}
<InlineLink
href="https://supabase.com/downloads/docs/Supabase+DPA+260317.pdf"
onClick={() => track('dpa_pdf_opened', { source: 'studio' })}
>
here
</InlineLink>
.
</p>
</div>
</ScaffoldSectionDetail>
<ScaffoldSectionContent>
<div className="@lg:flex items-center justify-center h-full">
<Button
onClick={() => {
setIsOpen(true)
track('dpa_request_button_clicked')
}}
type="default"
>
Request DPA
</Button>
</div>
</ScaffoldSectionContent>
</ScaffoldSection>
<TextConfirmModal
visible={isOpen}
title="Request executable DPA to sign"
loading={isRequesting}
confirmPlaceholder="Enter your email address"
confirmString={profile?.primary_email ?? ''}
confirmLabel="Send DPA request"
errorMessage="Email must match your account email."
onCancel={() => setIsOpen(false)}
onConfirm={() => onConfirmRequest()}
>
<div className="space-y-2 text-sm">
<p>
To make the DPA legally binding, you need to sign and complete the details through a
PandaDoc document that we prepare.
</p>
<p>
Please enter your email address to request an executable version of the DPA. You will
receive a document link via PandaDoc in the next 24 hours.
</p>
<p>
Once signed, the DPA will be considered executed and you'll be notified of any future
updates via this email.
</p>
</div>
</TextConfirmModal>
</>
)
}