Files
supabase/apps/studio/hooks/ui/useCsvFileDrop.ts
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

72 lines
1.9 KiB
TypeScript

import { type ImportDataFileAddedEvent } from 'common/telemetry-constants'
import { useCallback, useState, type DragEvent } from 'react'
import { flagInvalidFileImport } from '@/components/interfaces/TableGridEditor/SidePanelEditor/SpreadsheetImport/SpreadsheetImport.utils'
interface UseCsvFileDropOptions {
enabled: boolean
onFileDropped: (file: File) => void
onTelemetryEvent?: (eventName: ImportDataFileAddedEvent['action']) => void
}
interface UseCsvFileDropReturn {
isDraggedOver: boolean
onDragOver: (event: DragEvent<HTMLDivElement>) => void
onFileDrop: (event: DragEvent<HTMLDivElement>) => void
}
export function useCsvFileDrop({
enabled,
onFileDropped,
onTelemetryEvent,
}: UseCsvFileDropOptions): UseCsvFileDropReturn {
const [isDraggedOver, setIsDraggedOver] = useState(false)
const onDragOver = useCallback(
(event: DragEvent<HTMLDivElement>) => {
if (!enabled) return
const [item] = event.dataTransfer.items
// ignore non files drop, like column headers
if (item && item.kind !== 'file') return
if (event.type === 'dragover' && !isDraggedOver) {
setIsDraggedOver(true)
} else if (event.type === 'dragleave' || event.type === 'drop') {
setIsDraggedOver(false)
}
event.stopPropagation()
event.preventDefault()
},
[enabled, isDraggedOver]
)
const onFileDrop = useCallback(
(event: DragEvent<HTMLDivElement>) => {
if (!enabled) return
onDragOver(event)
const [file] = event.dataTransfer.files
const [item] = event.dataTransfer.items
// ignore non files drop, like column headers
if (item && item.kind !== 'file') return
if (flagInvalidFileImport(file)) return
onFileDropped(file)
onTelemetryEvent?.('import_data_dropzone_file_added')
},
[enabled, onDragOver, onFileDropped, onTelemetryEvent]
)
return {
isDraggedOver,
onDragOver,
onFileDrop,
}
}