mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 07:39:07 +03:00
Refactor telemetry event into RQ hook, and update to support PH
This commit is contained in:
1 parent
57d8f92176
commit
cc420c18ca
26 files changed
+553
-493
No files matched your search
@@ -1,11 +1,9 @@
|
||||
import { ExternalLink, Eye, EyeOff, FlaskConical } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useAppStateSnapshot } from 'state/app-state'
|
||||
import { Button, Modal, ScrollArea, cn } from 'ui'
|
||||
import APISidePanelPreview from './APISidePanelPreview'
|
||||
@@ -29,10 +27,9 @@ const FeaturePreviewModal = () => {
|
||||
},
|
||||
]
|
||||
|
||||
const router = useRouter()
|
||||
const snap = useAppStateSnapshot()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const featurePreviewContext = useFeaturePreviewContext()
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const selectedFeaturePreview =
|
||||
snap.selectedFeaturePreview === '' ? FEATURE_PREVIEWS[0].key : snap.selectedFeaturePreview
|
||||
@@ -53,15 +50,11 @@ const FeaturePreviewModal = () => {
|
||||
|
||||
const toggleFeature = () => {
|
||||
onUpdateFlag(selectedFeatureKey, !isSelectedFeatureEnabled)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'ui_feature_previews',
|
||||
action: isSelectedFeatureEnabled ? 'disabled' : 'enabled',
|
||||
label: selectedFeatureKey,
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'ui_feature_previews',
|
||||
action: isSelectedFeatureEnabled ? 'disabled' : 'enabled',
|
||||
label: selectedFeatureKey,
|
||||
})
|
||||
}
|
||||
|
||||
function handleCloseFeaturePreviewModal() {
|
||||
|
||||
+10
-16
@@ -1,13 +1,11 @@
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { compact, last } from 'lodash'
|
||||
import { ChevronsUpDown, Lightbulb } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { SchemaComboBox } from 'components/ui/SchemaComboBox'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi'
|
||||
import { useSchemasForAi } from 'hooks/misc/useSchemasForAi'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
@@ -44,13 +42,11 @@ export const AIPolicyChat = ({
|
||||
onDiff,
|
||||
clearHistory,
|
||||
}: AIPolicyChatProps) => {
|
||||
const router = useRouter()
|
||||
const { profile } = useProfile()
|
||||
const project = useSelectedProject()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const { profile } = useProfile()
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
const [selectedSchemas, setSelectedSchemas] = useSchemasForAi(project?.ref!)
|
||||
const [value, setValue] = useState<string>('')
|
||||
const [hasSuggested, setHasSuggested] = useState(false)
|
||||
@@ -62,6 +58,8 @@ export const AIPolicyChat = ({
|
||||
const name = compact([profile?.first_name, profile?.last_name]).join(' ')
|
||||
const pendingReply = loading && last(messages)?.role === 'user'
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading) {
|
||||
setValue('')
|
||||
@@ -203,15 +201,11 @@ export const AIPolicyChat = ({
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault()
|
||||
onSubmit(value)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_asked',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_asked',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
+13
-18
@@ -6,6 +6,7 @@ import { Button, CodeBlock, cn } from 'ui'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
|
||||
interface AAIPolicyPreProps {
|
||||
onDiff: (s: string) => void
|
||||
@@ -18,6 +19,8 @@ export const AIPolicyPre = ({ onDiff, children, className }: AAIPolicyPreProps)
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) return
|
||||
const timer = setTimeout(() => setCopied(false), 2000)
|
||||
@@ -73,15 +76,11 @@ export const AIPolicyPre = ({ onDiff, children, className }: AAIPolicyPreProps)
|
||||
className={cn('text-foreground-lighter hover:text-foreground', 'transition')}
|
||||
onClick={() => {
|
||||
onDiff(formatted)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_diffed',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_diffed',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
<FileDiff className={cn('h-4 w-4')} />
|
||||
@@ -109,15 +108,11 @@ export const AIPolicyPre = ({ onDiff, children, className }: AAIPolicyPreProps)
|
||||
className={cn('text-foreground-lighter hover:text-foreground', 'transition')}
|
||||
onClick={() => {
|
||||
handleCopy(formatted)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_copied',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_copied',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{copied ? (
|
||||
|
||||
+12
-18
@@ -1,14 +1,12 @@
|
||||
import styles from '@ui/layout/ai-icon-animation/ai-icon-animation-style.module.css'
|
||||
import { initial, last } from 'lodash'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import styles from '@ui/layout/ai-icon-animation/ai-icon-animation-style.module.css'
|
||||
import { subscriptionHasHipaaAddon } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import { QueryResponseError } from 'data/sql/execute-sql-mutation'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useRouter } from 'next/router'
|
||||
import {
|
||||
AlertTitle_Shadcn_,
|
||||
Alert_Shadcn_,
|
||||
@@ -30,15 +28,15 @@ const QueryError = ({
|
||||
setOpen: Dispatch<SetStateAction<boolean>>
|
||||
onSelectDebug: () => void
|
||||
}) => {
|
||||
const formattedError =
|
||||
(error?.formattedError?.split('\n') ?? [])?.filter((x: string) => x.length > 0) ?? []
|
||||
|
||||
// Customers on HIPAA plans should not have access to Supabase AI
|
||||
const organization = useSelectedOrganization()
|
||||
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug })
|
||||
const hasHipaaAddon = subscriptionHasHipaaAddon(subscription)
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const formattedError =
|
||||
(error?.formattedError?.split('\n') ?? [])?.filter((x: string) => x.length > 0) ?? []
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-3 px-5">
|
||||
@@ -84,15 +82,11 @@ const QueryError = ({
|
||||
)}
|
||||
onClick={() => {
|
||||
onSelectDebug()
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_debugger_requested',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_debugger_requested',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
Fix with Assistant
|
||||
|
||||
@@ -1,15 +1,41 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { Monaco } from '@monaco-editor/react'
|
||||
import type { PostgresPolicy } from '@supabase/postgres-meta'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useChat } from 'ai/react'
|
||||
import { IS_PLATFORM, useParams, useTelemetryProps } from 'common'
|
||||
import { isEqual, uniqBy } from 'lodash'
|
||||
import { FileDiff } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { toast } from 'sonner'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { useChat } from 'ai/react'
|
||||
import { IS_PLATFORM, useParams } from 'common'
|
||||
import { subscriptionHasHipaaAddon } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import {
|
||||
IStandaloneCodeEditor,
|
||||
IStandaloneDiffEditor,
|
||||
} from 'components/interfaces/SQLEditor/SQLEditor.types'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useSqlDebugMutation } from 'data/ai/sql-debug-mutation'
|
||||
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
|
||||
import { useDatabasePolicyUpdateMutation } from 'data/database-policies/database-policy-update-mutation'
|
||||
import { databasePoliciesKeys } from 'data/database-policies/keys'
|
||||
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
|
||||
import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi'
|
||||
import { useSchemasForAi } from 'hooks/misc/useSchemasForAi'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import {
|
||||
Button,
|
||||
Checkbox_Shadcn_,
|
||||
@@ -25,33 +51,6 @@ import {
|
||||
Tabs_Shadcn_,
|
||||
cn,
|
||||
} from 'ui'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { Monaco } from '@monaco-editor/react'
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { subscriptionHasHipaaAddon } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import {
|
||||
IStandaloneCodeEditor,
|
||||
IStandaloneDiffEditor,
|
||||
} from 'components/interfaces/SQLEditor/SQLEditor.types'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useSqlDebugMutation } from 'data/ai/sql-debug-mutation'
|
||||
import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query'
|
||||
import { useDatabasePolicyUpdateMutation } from 'data/database-policies/database-policy-update-mutation'
|
||||
import { databasePoliciesKeys } from 'data/database-policies/keys'
|
||||
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
|
||||
import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi'
|
||||
import { useSchemasForAi } from 'hooks/misc/useSchemasForAi'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { AIPolicyChat } from './AIPolicyChat'
|
||||
import {
|
||||
@@ -96,13 +95,11 @@ export const AIPolicyEditorPanel = memo(function ({
|
||||
onSelectCancel,
|
||||
authContext,
|
||||
}: AIPolicyEditorPanelProps) {
|
||||
const router = useRouter()
|
||||
const { ref } = useParams()
|
||||
const queryClient = useQueryClient()
|
||||
const selectedProject = useSelectedProject()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const canUpdatePolicies = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
|
||||
|
||||
const [editView, setEditView] = useQueryState('view', { defaultValue: 'templates' as const })
|
||||
@@ -234,6 +231,8 @@ export const AIPolicyEditorPanel = memo(function ({
|
||||
)
|
||||
}, [chatMessages, debugThread])
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const { mutate: executeMutation, isLoading: isExecuting } = useExecuteSqlMutation({
|
||||
onSuccess: async () => {
|
||||
// refresh all policies
|
||||
@@ -511,15 +510,11 @@ export const AIPolicyEditorPanel = memo(function ({
|
||||
onClick={() => {
|
||||
setIncomingChange(undefined)
|
||||
setSelectedDiff(undefined)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_discarded',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_discarded',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
Discard
|
||||
@@ -529,15 +524,11 @@ export const AIPolicyEditorPanel = memo(function ({
|
||||
onClick={() => {
|
||||
acceptChange()
|
||||
setSelectedDiff(undefined)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_accepted',
|
||||
label: 'rls-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'rls_editor',
|
||||
action: 'ai_suggestion_accepted',
|
||||
label: 'rls-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
Accept
|
||||
|
||||
+9
-15
@@ -1,12 +1,10 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ChevronDown, ExternalLink } from 'lucide-react'
|
||||
import { Dispatch, SetStateAction, useState } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import * as z from 'zod'
|
||||
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { ChevronDown, ExternalLink } from 'lucide-react'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import {
|
||||
Button,
|
||||
FormControl_Shadcn_,
|
||||
@@ -32,8 +30,8 @@ const FormSchema = z.object({ channel: z.string(), isPrivate: z.boolean() })
|
||||
|
||||
export const ChooseChannelPopover = ({ config, onChangeConfig }: ChooseChannelPopoverProps) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
mode: 'onBlur',
|
||||
@@ -52,15 +50,11 @@ export const ChooseChannelPopover = ({ config, onChangeConfig }: ChooseChannelPo
|
||||
|
||||
const onSubmit = () => {
|
||||
setOpen(false)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'started_listening_to_channel_in_input_channel_popover',
|
||||
label: 'realtime_inspector_config',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'started_listening_to_channel_in_input_channel_popover',
|
||||
label: 'realtime_inspector_config',
|
||||
})
|
||||
onChangeConfig({
|
||||
...config,
|
||||
channelName: form.getValues('channel'),
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { PlayCircle, StopCircle } from 'lucide-react'
|
||||
import { Dispatch, SetStateAction } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { Button } from 'ui'
|
||||
import { ChooseChannelPopover } from './ChooseChannelPopover'
|
||||
import { RealtimeFilterPopover } from './RealtimeFilterPopover'
|
||||
import { RealtimeTokensPopover } from './RealtimeTokensPopover'
|
||||
@@ -17,8 +15,7 @@ interface HeaderProps {
|
||||
}
|
||||
|
||||
export const Header = ({ config, onChangeConfig }: HeaderProps) => {
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
return (
|
||||
<div className="flex flex-row h-14 gap-2.5 items-center px-4">
|
||||
@@ -37,15 +34,11 @@ export const Header = ({ config, onChangeConfig }: HeaderProps) => {
|
||||
onChangeConfig({ ...config, enabled: !config.enabled })
|
||||
if (!config.enabled) {
|
||||
// the user has clicked to start listening
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'started_listening',
|
||||
label: 'realtime_inspector_config',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'started_listening',
|
||||
label: 'realtime_inspector_config',
|
||||
})
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { X } from 'lucide-react'
|
||||
import { useMemo } from 'react'
|
||||
import { Button, cn } from 'ui'
|
||||
|
||||
import CopyButton from 'components/ui/CopyButton'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { Button, cn } from 'ui'
|
||||
import type { LogData } from './Messages.types'
|
||||
import { SelectedRealtimeMessagePanel } from './SelectedRealtimeMessagePanel'
|
||||
import { X } from 'lucide-react'
|
||||
|
||||
export interface MessageSelectionProps {
|
||||
log: LogData | null
|
||||
@@ -15,13 +13,12 @@ export interface MessageSelectionProps {
|
||||
}
|
||||
|
||||
const MessageSelection = ({ log, onClose }: MessageSelectionProps) => {
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
|
||||
const selectionText = useMemo(() => {
|
||||
return JSON.stringify(log, null, 2)
|
||||
}, [log])
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -76,15 +73,11 @@ const MessageSelection = ({ log, onClose }: MessageSelectionProps) => {
|
||||
type="default"
|
||||
title="Copy log to clipboard"
|
||||
onClick={() => {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'copied_message',
|
||||
label: 'realtime_inspector_results',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'copied_message',
|
||||
label: 'realtime_inspector_results',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { isEqual } from 'lodash'
|
||||
import { ExternalLink, Loader2, Megaphone, MegaphoneIcon } from 'lucide-react'
|
||||
import { ExternalLink, Loader2, Megaphone } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useState } from 'react'
|
||||
import DataGrid, { Row } from 'react-data-grid'
|
||||
import { Button, IconBroadcast, IconDatabaseChanges, IconPresence, cn } from 'ui'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import ShimmerLine from 'components/ui/ShimmerLine'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { Button, IconBroadcast, IconDatabaseChanges, IconPresence, cn } from 'ui'
|
||||
import MessageSelection from './MessageSelection'
|
||||
import type { LogData } from './Messages.types'
|
||||
import NoChannelEmptyState from './NoChannelEmptyState'
|
||||
@@ -116,8 +115,8 @@ const MessagesTable = ({
|
||||
}: MessagesTableProps) => {
|
||||
const [focusedLog, setFocusedLog] = useState<LogData | null>(null)
|
||||
const stringData = JSON.stringify(data)
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!data) return
|
||||
@@ -185,15 +184,11 @@ const MessagesTable = ({
|
||||
isRowSelected={false}
|
||||
selectedCellIdx={undefined}
|
||||
onClick={() => {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'focused-specific-message',
|
||||
label: 'realtime_inspector_results',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'focused-specific-message',
|
||||
label: 'realtime_inspector_results',
|
||||
})
|
||||
setFocusedLog(row)
|
||||
}}
|
||||
/>
|
||||
|
||||
+9
-15
@@ -1,8 +1,8 @@
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { PlusCircle } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Dispatch, SetStateAction, useState } from 'react'
|
||||
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@@ -16,8 +16,6 @@ import {
|
||||
Toggle,
|
||||
cn,
|
||||
} from 'ui'
|
||||
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
|
||||
import { RealtimeConfig } from '../useRealtimeMessages'
|
||||
import { FilterSchema } from './FilterSchema'
|
||||
@@ -32,8 +30,8 @@ export const RealtimeFilterPopover = ({ config, onChangeConfig }: RealtimeFilter
|
||||
const [open, setOpen] = useState(false)
|
||||
const [applyConfigOpen, setApplyConfigOpen] = useState(false)
|
||||
const [tempConfig, setTempConfig] = useState(config)
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const onOpen = (v: boolean) => {
|
||||
// when opening, copy the outside config into the intermediate one
|
||||
@@ -202,15 +200,11 @@ export const RealtimeFilterPopover = ({ config, onChangeConfig }: RealtimeFilter
|
||||
visible={applyConfigOpen}
|
||||
onCancel={() => setApplyConfigOpen(false)}
|
||||
onConfirm={() => {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'applied_filters',
|
||||
label: 'realtime_inspector_config',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'applied_filters',
|
||||
label: 'realtime_inspector_config',
|
||||
})
|
||||
onChangeConfig(tempConfig)
|
||||
setApplyConfigOpen(false)
|
||||
setOpen(false)
|
||||
|
||||
+8
-14
@@ -1,14 +1,12 @@
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Dispatch, SetStateAction, useEffect, useRef } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector'
|
||||
import { useProjectApiQuery } from 'data/config/project-api-query'
|
||||
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { IS_PLATFORM } from 'lib/constants'
|
||||
import { getRoleImpersonationJWT } from 'lib/role-impersonation'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state'
|
||||
import { RealtimeConfig } from '../useRealtimeMessages'
|
||||
|
||||
@@ -19,8 +17,8 @@ interface RealtimeTokensPopoverProps {
|
||||
|
||||
export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokensPopoverProps) => {
|
||||
const { data: settings } = useProjectApiQuery({ projectRef: config.projectRef })
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const apiService = settings?.autoApiService
|
||||
const serviceRoleKey = apiService?.service_api_keys.find((x) => x.name === 'service_role key')
|
||||
@@ -44,15 +42,11 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens
|
||||
const isMounted = useRef(false)
|
||||
useEffect(() => {
|
||||
if (isMounted.current) {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'changed_database_role',
|
||||
label: 'realtime_inspector_config',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'changed_database_role',
|
||||
label: 'realtime_inspector_config',
|
||||
})
|
||||
}
|
||||
isMounted.current = true
|
||||
}, [snap.role])
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useParams } from 'common'
|
||||
import { useState } from 'react'
|
||||
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { Header } from './Header'
|
||||
import MessagesTable from './MessagesTable'
|
||||
import { SendMessageModal } from './SendMessageModal'
|
||||
@@ -13,8 +12,6 @@ import { RealtimeConfig, useRealtimeMessages } from './useRealtimeMessages'
|
||||
*/
|
||||
export const RealtimeInspector = () => {
|
||||
const { ref } = useParams()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const router = useRouter()
|
||||
const [sendMessageShown, setSendMessageShown] = useState(false)
|
||||
|
||||
const [realtimeConfig, setRealtimeConfig] = useState<RealtimeConfig>({
|
||||
@@ -33,6 +30,7 @@ export const RealtimeInspector = () => {
|
||||
enableBroadcast: true,
|
||||
})
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
const { logData, sendMessage } = useRealtimeMessages(realtimeConfig, setRealtimeConfig)
|
||||
|
||||
return (
|
||||
@@ -52,15 +50,11 @@ export const RealtimeInspector = () => {
|
||||
visible={sendMessageShown}
|
||||
onSelectCancel={() => setSendMessageShown(false)}
|
||||
onSelectConfirm={(v) => {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'realtime_inspector',
|
||||
action: 'send_broadcast_message',
|
||||
label: 'realtime_inspector_results',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'realtime_inspector',
|
||||
action: 'send_broadcast_message',
|
||||
label: 'realtime_inspector_results',
|
||||
})
|
||||
sendMessage(v.message, v.payload, () => setSendMessageShown(false))
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { DiffType } from '../SQLEditor.types'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
|
||||
interface AiMessagePreProps {
|
||||
onDiff: (type: DiffType, s: string) => void
|
||||
@@ -27,6 +28,8 @@ export const AiMessagePre = ({ onDiff, children, className }: AiMessagePreProps)
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) return
|
||||
const timer = setTimeout(() => setCopied(false), 2000)
|
||||
@@ -69,15 +72,11 @@ export const AiMessagePre = ({ onDiff, children, className }: AiMessagePreProps)
|
||||
size="tiny"
|
||||
onClick={() => {
|
||||
onDiff(DiffType.Addition, formatted)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_inserted',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_inserted',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
<InsertCode className="h-4 w-4 text-foreground-light" strokeWidth={1.5} />
|
||||
@@ -95,15 +94,11 @@ export const AiMessagePre = ({ onDiff, children, className }: AiMessagePreProps)
|
||||
size="tiny"
|
||||
onClick={() => {
|
||||
onDiff(DiffType.Modification, formatted)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_replaced',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_replaced',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
<ReplaceCode className="h-4 w-4 text-foreground-light" strokeWidth={1.5} />
|
||||
@@ -121,15 +116,11 @@ export const AiMessagePre = ({ onDiff, children, className }: AiMessagePreProps)
|
||||
size="tiny"
|
||||
onClick={() => {
|
||||
handleCopy(formatted)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_copied',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_copied',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
})
|
||||
}}
|
||||
>
|
||||
{copied ? (
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { Message as MessageType } from 'ai'
|
||||
import { useChat } from 'ai/react'
|
||||
import { compact, last } from 'lodash'
|
||||
import { Info } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { Message as MessageType } from 'ai'
|
||||
import { useChat } from 'ai/react'
|
||||
import { useParams } from 'common'
|
||||
import { SchemaComboBox } from 'components/ui/SchemaComboBox'
|
||||
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
|
||||
import { useOrganizationUpdateMutation } from 'data/organizations/organization-update-mutation'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
|
||||
import { useSchemasForAi } from 'hooks/misc/useSchemasForAi'
|
||||
@@ -18,7 +18,6 @@ import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { BASE_PATH, LOCAL_STORAGE_KEYS, OPT_IN_TAGS } from 'lib/constants'
|
||||
import { useProfile } from 'lib/profile'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import uuidv4 from 'lib/uuid'
|
||||
import {
|
||||
AiIconAnimation,
|
||||
@@ -55,13 +54,25 @@ export const AiAssistantPanel = ({
|
||||
onClose,
|
||||
includeSchemaMetadata,
|
||||
}: AiAssistantPanelProps) => {
|
||||
const project = useSelectedProject()
|
||||
const router = useRouter()
|
||||
const { ref } = useParams()
|
||||
const { profile } = useProfile()
|
||||
const project = useSelectedProject()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const [chatId, setChatId] = useState(uuidv4())
|
||||
const [value, setValue] = useState<string>('')
|
||||
const [isConfirmOptInModalOpen, setIsConfirmOptInModalOpen] = useState(false)
|
||||
const [selectedSchemas, setSelectedSchemas] = useSchemasForAi(project?.ref!)
|
||||
|
||||
const canUpdateOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
||||
const [showAiNotOptimizedWarningSetting, setShowAiNotOptimizedWarningSetting] =
|
||||
useLocalStorageQuery(LOCAL_STORAGE_KEYS.SHOW_AI_NOT_OPTIMIZED_WARNING(ref as string), true)
|
||||
const shouldShowNotOptimizedAlert =
|
||||
selectedOrganization && !includeSchemaMetadata && showAiNotOptimizedWarningSetting
|
||||
|
||||
const { data } = useEntityDefinitionsQuery(
|
||||
{
|
||||
schemas: selectedSchemas,
|
||||
@@ -74,7 +85,6 @@ export const AiAssistantPanel = ({
|
||||
const entityDefinitions = includeSchemaMetadata ? data?.map((def) => def.sql.trim()) : undefined
|
||||
|
||||
// Use chat id because useChat doesn't have a reset function to clear all messages
|
||||
const [chatId, setChatId] = useState(uuidv4())
|
||||
const {
|
||||
messages: chatMessages,
|
||||
append,
|
||||
@@ -97,38 +107,10 @@ export const AiAssistantPanel = ({
|
||||
)
|
||||
}, [chatMessages])
|
||||
|
||||
const bottomRef = useRef<HTMLDivElement>(null)
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const [value, setValue] = useState<string>('')
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
const name = compact([profile?.first_name, profile?.last_name]).join(' ')
|
||||
const pendingReply = isLoading && last(messages)?.role === 'user'
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
setValue('')
|
||||
if (inputRef.current) inputRef.current.focus()
|
||||
}
|
||||
|
||||
// Try to scroll on each rerender to the bottom
|
||||
setTimeout(
|
||||
() => {
|
||||
if (bottomRef.current) {
|
||||
bottomRef.current.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
isLoading ? 100 : 500
|
||||
)
|
||||
}, [isLoading])
|
||||
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
|
||||
const [isConfirmOptInModalOpen, setIsConfirmOptInModalOpen] = useState(false)
|
||||
|
||||
const canUpdateOrganization = useCheckPermissions(PermissionAction.UPDATE, 'organizations')
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
const { mutate: updateOrganization, isLoading: isUpdating } = useOrganizationUpdateMutation()
|
||||
|
||||
const confirmOptInToShareSchemaData = async () => {
|
||||
@@ -155,11 +137,22 @@ export const AiAssistantPanel = ({
|
||||
)
|
||||
}
|
||||
|
||||
const [showAiNotOptimizedWarningSetting, setShowAiNotOptimizedWarningSetting] =
|
||||
useLocalStorageQuery(LOCAL_STORAGE_KEYS.SHOW_AI_NOT_OPTIMIZED_WARNING(ref as string), true)
|
||||
useEffect(() => {
|
||||
if (!isLoading) {
|
||||
setValue('')
|
||||
if (inputRef.current) inputRef.current.focus()
|
||||
}
|
||||
|
||||
const shouldShowNotOptimizedAlert =
|
||||
selectedOrganization && !includeSchemaMetadata && showAiNotOptimizedWarningSetting
|
||||
// Try to scroll on each rerender to the bottom
|
||||
setTimeout(
|
||||
() => {
|
||||
if (bottomRef.current) {
|
||||
bottomRef.current.scrollIntoView({ behavior: 'smooth' })
|
||||
}
|
||||
},
|
||||
isLoading ? 100 : 500
|
||||
)
|
||||
}, [isLoading])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full border-l border-control">
|
||||
@@ -317,15 +310,11 @@ export const AiAssistantPanel = ({
|
||||
role: 'user',
|
||||
createdAt: new Date(),
|
||||
})
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_asked',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor_ai_assistant',
|
||||
action: 'ai_suggestion_asked',
|
||||
label: 'sql-editor-ai-assistant',
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -78,6 +78,7 @@ import {
|
||||
suffixWithLimit,
|
||||
} from './SQLEditor.utils'
|
||||
import UtilityPanel from './UtilityPanel/UtilityPanel'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
|
||||
// Load the monaco editor client-side only (does not behave well server-side)
|
||||
const MonacoEditor = dynamic(() => import('./MonacoEditor'), { ssr: false })
|
||||
@@ -164,6 +165,8 @@ const SQLEditor = () => {
|
||||
)
|
||||
const isLoading = urlId === 'new' ? false : snippetIsLoading
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const { mutate: execute, isLoading: isExecuting } = useExecuteSqlMutation({
|
||||
onSuccess(data, vars) {
|
||||
if (id) snapV2.addResult(id, data.result, vars.autoLimit)
|
||||
@@ -475,15 +478,11 @@ const SQLEditor = () => {
|
||||
}
|
||||
}
|
||||
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor',
|
||||
action: 'ai_suggestion_accepted',
|
||||
label: debugSolution ? 'debug_snippet' : 'edit_snippet',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor',
|
||||
action: 'ai_suggestion_accepted',
|
||||
label: debugSolution ? 'debug_snippet' : 'edit_snippet',
|
||||
})
|
||||
|
||||
setSelectedMessage(undefined)
|
||||
setSelectedDiffType(DiffType.Modification)
|
||||
@@ -507,15 +506,11 @@ const SQLEditor = () => {
|
||||
])
|
||||
|
||||
const discardAiHandler = useCallback(() => {
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'sql_editor',
|
||||
action: 'ai_suggestion_rejected',
|
||||
label: debugSolution ? 'debug_snippet' : 'edit_snippet',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'sql_editor',
|
||||
action: 'ai_suggestion_rejected',
|
||||
label: debugSolution ? 'debug_snippet' : 'edit_snippet',
|
||||
})
|
||||
|
||||
setSelectedMessage(undefined)
|
||||
setDebugSolution(undefined)
|
||||
|
||||
@@ -3,13 +3,13 @@ import { partition } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { useParams } from 'common'
|
||||
import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import { useProfile } from 'lib/profile'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
|
||||
import { createSqlSnippetSkeletonV2 } from '../SQLEditor.utils'
|
||||
import SQLCard from './SQLCard'
|
||||
@@ -22,13 +22,14 @@ const SQLQuickstarts = () => {
|
||||
const [, quickStart] = partition(SQL_TEMPLATES, { type: 'template' })
|
||||
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const handleNewQuery = async (sql: string, name: string) => {
|
||||
if (!ref) return console.error('Project ref is required')
|
||||
if (!project) return console.error('Project is required')
|
||||
@@ -74,15 +75,11 @@ const SQLQuickstarts = () => {
|
||||
sql={x.sql}
|
||||
onClick={(sql, title) => {
|
||||
handleNewQuery(sql, title)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'quickstart',
|
||||
action: 'quickstart_clicked',
|
||||
label: x.title,
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'quickstart',
|
||||
action: 'quickstart_clicked',
|
||||
label: x.title,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -3,13 +3,13 @@ import { partition } from 'lodash'
|
||||
import { useRouter } from 'next/router'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { useParams } from 'common'
|
||||
import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { uuidv4 } from 'lib/helpers'
|
||||
import { useProfile } from 'lib/profile'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
|
||||
import { createSqlSnippetSkeletonV2 } from '../SQLEditor.utils'
|
||||
import SQLCard from './SQLCard'
|
||||
@@ -22,13 +22,14 @@ const SQLTemplates = () => {
|
||||
const [sql] = partition(SQL_TEMPLATES, { type: 'template' })
|
||||
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const canCreateSQLSnippet = useCheckPermissions(PermissionAction.CREATE, 'user_content', {
|
||||
resource: { type: 'sql', owner_id: profile?.id },
|
||||
subject: { id: profile?.id },
|
||||
})
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const handleNewQuery = async (sql: string, name: string) => {
|
||||
if (!ref) return console.error('Project ref is required')
|
||||
if (!project) return console.error('Project is required')
|
||||
@@ -74,15 +75,11 @@ const SQLTemplates = () => {
|
||||
sql={x.sql}
|
||||
onClick={(sql, title) => {
|
||||
handleNewQuery(sql, title)
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'scripts',
|
||||
action: 'script_clicked',
|
||||
label: x.title,
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'scripts',
|
||||
action: 'script_clicked',
|
||||
label: x.title,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import { compact, isObject, isString, map } from 'lodash'
|
||||
import { ChevronDownIcon, Clipboard, Download } from 'lucide-react'
|
||||
import { markdownTable } from 'markdown-table'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { CSVLink } from 'react-csv'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { ChevronDownIcon, Clipboard, Download } from 'lucide-react'
|
||||
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
|
||||
import {
|
||||
Button,
|
||||
@@ -25,12 +23,12 @@ export type ResultsDropdownProps = {
|
||||
|
||||
const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
|
||||
const { project } = useProjectContext()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
|
||||
const result = snapV2.results?.[id]?.[0] ?? undefined
|
||||
const csvRef = useRef<CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }>(null)
|
||||
const router = useRouter()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const csvData = useMemo(() => {
|
||||
if (result?.rows) {
|
||||
@@ -68,11 +66,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
|
||||
|
||||
function onDownloadCSV() {
|
||||
csvRef.current?.link.click()
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'sql_editor', action: 'sql_download_csv', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'sql_editor', action: 'sql_download_csv', label: '' })
|
||||
}
|
||||
|
||||
function onCopyAsMarkdown() {
|
||||
@@ -92,11 +86,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
|
||||
|
||||
copyToClipboard(markdownData, () => {
|
||||
toast.success('Copied results to clipboard')
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' })
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -109,11 +99,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
|
||||
|
||||
copyToClipboard(JSON.stringify(result.rows, null, 2), () => {
|
||||
toast.success('Copied results to clipboard')
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'sql_editor', action: 'sql_copy_as_json', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'sql_editor', action: 'sql_copy_as_json', label: '' })
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+9
-14
@@ -1,8 +1,7 @@
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { ChevronDown, ExternalLink } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
@@ -12,9 +11,9 @@ import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
|
||||
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
import { pluckObjectFields } from 'lib/helpers'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import { useDatabaseSettingsStateSnapshot } from 'state/database-settings'
|
||||
import {
|
||||
@@ -58,8 +57,6 @@ interface DatabaseConnectionStringProps {
|
||||
}
|
||||
|
||||
export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStringProps) => {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const project = useSelectedProject()
|
||||
const { ref: projectRef, connectionString } = useParams()
|
||||
const snap = useDatabaseSettingsStateSnapshot()
|
||||
@@ -93,6 +90,8 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
|
||||
const { data: addons, isSuccess: isSuccessAddons } = useProjectAddonsQuery({ projectRef })
|
||||
const { ipv4: ipv4Addon } = getAddons(addons?.selected_addons ?? [])
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const DB_FIELDS = ['db_host', 'db_name', 'db_port', 'db_user', 'inserted_at']
|
||||
const emptyState = { db_user: '', db_host: '', db_port: '', db_name: '' }
|
||||
const connectionInfo = pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS)
|
||||
@@ -103,15 +102,11 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin
|
||||
|
||||
const handleCopy = (id: string) => {
|
||||
const labelValue = CONNECTION_TYPES.find((type) => type.id === id)?.label
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'settings',
|
||||
action: 'copy_connection_string',
|
||||
label: labelValue ? labelValue : '',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'settings',
|
||||
action: 'copy_connection_string',
|
||||
label: labelValue ? labelValue : '',
|
||||
})
|
||||
}
|
||||
|
||||
const connectionStrings =
|
||||
|
||||
+10
-16
@@ -1,7 +1,6 @@
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useParams, useTelemetryProps } from 'common'
|
||||
import { useParams } from 'common'
|
||||
import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import DatabaseSelector from 'components/ui/DatabaseSelector'
|
||||
@@ -10,12 +9,11 @@ import ShimmeringLoader from 'components/ui/ShimmeringLoader'
|
||||
import { usePoolingConfigurationQuery } from 'data/database/pooling-configuration-query'
|
||||
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
|
||||
import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { pluckObjectFields } from 'lib/helpers'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
|
||||
import { useDatabaseSettingsStateSnapshot } from 'state/database-settings'
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Input } from 'ui'
|
||||
import { WarningIcon } from 'ui'
|
||||
import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Input, WarningIcon } from 'ui'
|
||||
import {
|
||||
DefaultSessionModeNotice,
|
||||
IPv4AddonDirectConnectionNotice,
|
||||
@@ -25,9 +23,7 @@ import { UsePoolerCheckbox } from '../UsePoolerCheckbox'
|
||||
import ResetDbPassword from './ResetDbPassword'
|
||||
|
||||
const DatabaseSettings = () => {
|
||||
const router = useRouter()
|
||||
const { ref: projectRef, connectionString } = useParams()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const snap = useDatabaseSettingsStateSnapshot()
|
||||
const state = useDatabaseSelectorStateSnapshot()
|
||||
|
||||
@@ -57,6 +53,8 @@ const DatabaseSettings = () => {
|
||||
isError: isErrorAddons,
|
||||
isSuccess: isSuccessAddons,
|
||||
} = useProjectAddonsQuery({ projectRef })
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const error = readReplicasError || poolingInfoError || addOnsError
|
||||
const isLoading = isLoadingReadReplicas || isLoadingPoolingInfo || isLoadingAddons
|
||||
const isError = isErrorReadReplicas || isErrorPoolingInfo || isErrorAddons
|
||||
@@ -84,15 +82,11 @@ const DatabaseSettings = () => {
|
||||
: dbConnectionInfo
|
||||
|
||||
const handleCopy = (labelValue?: string) =>
|
||||
Telemetry.sendEvent(
|
||||
{
|
||||
category: 'settings',
|
||||
action: 'copy_connection_string',
|
||||
label: labelValue ? labelValue : '',
|
||||
},
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({
|
||||
category: 'settings',
|
||||
action: 'copy_connection_string',
|
||||
label: labelValue ? labelValue : '',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (connectionString !== undefined && connectionStringsRef.current !== undefined) {
|
||||
|
||||
@@ -6,14 +6,13 @@ import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { object, string } from 'yup'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
|
||||
import { useMfaChallengeAndVerifyMutation } from 'data/profile/mfa-challenge-and-verify-mutation'
|
||||
import { useMfaListFactorsQuery } from 'data/profile/mfa-list-factors-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSignOut } from 'lib/auth'
|
||||
import { getReturnToPath } from 'lib/gotrue'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import { Button, Form, Input } from 'ui'
|
||||
|
||||
const signInSchema = object({
|
||||
@@ -24,9 +23,10 @@ const SignInMfaForm = () => {
|
||||
const router = useRouter()
|
||||
const signOut = useSignOut()
|
||||
const queryClient = useQueryClient()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const [selectedFactor, setSelectedFactor] = useState<Factor | null>(null)
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const {
|
||||
data: factors,
|
||||
error: factorsError,
|
||||
@@ -40,11 +40,7 @@ const SignInMfaForm = () => {
|
||||
isSuccess,
|
||||
} = useMfaChallengeAndVerifyMutation({
|
||||
onSuccess: async () => {
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'account', action: 'sign_in', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'account', action: 'sign_in', label: '' })
|
||||
await queryClient.resetQueries()
|
||||
router.push(getReturnToPath())
|
||||
},
|
||||
@@ -149,9 +145,8 @@ const SignInMfaForm = () => {
|
||||
onClick={() =>
|
||||
setSelectedFactor(factors.totp.find((f) => f.id !== selectedFactor?.id)!)
|
||||
}
|
||||
>{`Authenticate using ${
|
||||
factors.totp.find((f) => f.id !== selectedFactor?.id)?.friendly_name
|
||||
}?`}</a>
|
||||
>{`Authenticate using ${factors.totp.find((f) => f.id !== selectedFactor?.id)
|
||||
?.friendly_name}?`}</a>
|
||||
</li>
|
||||
)}
|
||||
<li>
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
import { useMutation, UseMutationOptions } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { isBrowser } from 'common'
|
||||
import { handleError, post } from 'data/fetchers'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { useRouter } from 'next/router'
|
||||
import type { ResponseError } from 'types'
|
||||
|
||||
type SendEventGA = {
|
||||
action: string
|
||||
category: string
|
||||
label: string
|
||||
value: string
|
||||
page_referrer: string
|
||||
page_title: string
|
||||
page_location: string
|
||||
ga: {
|
||||
screen_resolution: string
|
||||
language: string
|
||||
}
|
||||
}
|
||||
|
||||
type SendEventPH = {
|
||||
action: string
|
||||
page_url: string
|
||||
page_title: string
|
||||
pathname: string
|
||||
ph: {
|
||||
language: string
|
||||
referrer: string
|
||||
userAgent: string
|
||||
search: string
|
||||
viewport_height: number
|
||||
viewport_width: number
|
||||
}
|
||||
custom_properties: { [key: string]: string }
|
||||
}
|
||||
|
||||
export type SendEventVariables = {
|
||||
action: string
|
||||
category: string
|
||||
label: string
|
||||
value?: string
|
||||
}
|
||||
|
||||
type SendEventPayload = any
|
||||
|
||||
export async function sendEvent(type: 'GA' | 'PH', body: SendEventPayload) {
|
||||
const headers = type === 'PH' ? { Version: '2' } : undefined
|
||||
const { data, error } = await post(`/platform/telemetry/event`, { body, headers })
|
||||
if (error) handleError(error)
|
||||
return data
|
||||
}
|
||||
|
||||
type SendEventData = Awaited<ReturnType<typeof sendEvent>>
|
||||
|
||||
export const useSendEventMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseMutationOptions<SendEventData, ResponseError, SendEventVariables>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
const router = useRouter()
|
||||
const usePostHogParameters = useFlag('enablePosthogChanges')
|
||||
|
||||
const payload = usePostHogParameters
|
||||
? ({
|
||||
page_url: window.location.href,
|
||||
page_title: document?.title,
|
||||
pathname: router.pathname,
|
||||
ph: {
|
||||
language: router?.locale ?? 'en-US',
|
||||
referrer: document?.referrer,
|
||||
userAgent: navigator.userAgent,
|
||||
search: window.location.search,
|
||||
viewport_height: isBrowser ? window.innerHeight : 0,
|
||||
viewport_width: isBrowser ? window.innerWidth : 0,
|
||||
},
|
||||
custom_properties: {},
|
||||
} as SendEventPH)
|
||||
: ({
|
||||
page_referrer: document?.referrer,
|
||||
page_title: document?.title,
|
||||
page_location: router.asPath.split('#')[0],
|
||||
ga: {
|
||||
screen_resolution: isBrowser ? `${window.innerWidth}x${window.innerHeight}` : undefined,
|
||||
language: router?.locale ?? 'en-US',
|
||||
},
|
||||
} as SendEventGA)
|
||||
|
||||
return useMutation<SendEventData, ResponseError, SendEventVariables>(
|
||||
(vars) => {
|
||||
const { action, ...otherVars } = vars
|
||||
const type = usePostHogParameters ? 'PH' : 'GA'
|
||||
const body = usePostHogParameters
|
||||
? ({
|
||||
action,
|
||||
...(payload as Omit<SendEventPH, 'action'>),
|
||||
custom_properties: otherVars,
|
||||
} as SendEventPH)
|
||||
: ({ ...vars, ...payload } as SendEventGA)
|
||||
return sendEvent(type, body)
|
||||
},
|
||||
{
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to send Telemetry event: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useIsLoggedIn, useTelemetryProps } from 'common'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, createContext, useContext, useMemo } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
@@ -7,6 +6,7 @@ import { usePermissionsQuery } from 'data/permissions/permissions-query'
|
||||
import { useProfileCreateMutation } from 'data/profile/profile-create-mutation'
|
||||
import { useProfileQuery } from 'data/profile/profile-query'
|
||||
import type { Profile } from 'data/profile/types'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import type { ResponseError } from 'types'
|
||||
|
||||
@@ -27,18 +27,14 @@ export const ProfileContext = createContext<ProfileContextType>({
|
||||
})
|
||||
|
||||
export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const router = useRouter()
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
|
||||
const isLoggedIn = useIsLoggedIn()
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const { mutate: createProfile, isLoading: isCreatingProfile } = useProfileCreateMutation({
|
||||
async onSuccess() {
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'conversion', action: 'sign_up', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'conversion', action: 'sign_up', label: '' })
|
||||
},
|
||||
onError() {
|
||||
toast.error('Failed to create your profile. Please refresh to try again.')
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Sha256 } from '@aws-crypto/sha256-browser'
|
||||
import type { NextRouter } from 'next/router'
|
||||
|
||||
import { post } from 'lib/common/fetch'
|
||||
import { API_URL, IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants'
|
||||
@@ -10,49 +9,6 @@ export interface TelemetryProps {
|
||||
language: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a telemetry event to Logflare for tracking by the product team.
|
||||
*/
|
||||
const sendEvent = (
|
||||
event: {
|
||||
category: string
|
||||
action: string
|
||||
label: string
|
||||
value?: string
|
||||
},
|
||||
gaProps: TelemetryProps,
|
||||
router: NextRouter
|
||||
) => {
|
||||
if (!IS_PLATFORM) return
|
||||
|
||||
const consent =
|
||||
typeof window !== 'undefined'
|
||||
? localStorage.getItem(LOCAL_STORAGE_KEYS.TELEMETRY_CONSENT)
|
||||
: null
|
||||
if (consent !== 'true') return
|
||||
|
||||
const { category, action, label, value } = event
|
||||
|
||||
// remove # section from router.asPath as it
|
||||
// often includes sensitive information
|
||||
// such as access/refresh tokens
|
||||
const page_location = router.asPath.split('#')[0]
|
||||
|
||||
return post(`${API_URL}/telemetry/event`, {
|
||||
action: action,
|
||||
category: category,
|
||||
label: label,
|
||||
value: value,
|
||||
page_referrer: document?.referrer,
|
||||
page_title: document?.title,
|
||||
page_location,
|
||||
ga: {
|
||||
screen_resolution: gaProps?.screenResolution,
|
||||
language: gaProps?.language,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: GA4 doesn't have identify method.
|
||||
* We may or may not need gaClientId here. Confirm later
|
||||
@@ -88,7 +44,6 @@ export const getAnonId = async (id: string) => {
|
||||
}
|
||||
|
||||
const Telemetry = {
|
||||
sendEvent,
|
||||
sendIdentify,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,19 +3,18 @@ import { useRouter } from 'next/router'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useTelemetryProps } from 'common'
|
||||
import SignInMfaForm from 'components/interfaces/SignIn/SignInMfaForm'
|
||||
import SignInLayout from 'components/layouts/SignInLayout/SignInLayout'
|
||||
import { Loading } from 'components/ui/Loading'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { auth, buildPathWithParams, getAccessToken, getReturnToPath } from 'lib/gotrue'
|
||||
import Telemetry from 'lib/telemetry'
|
||||
import type { NextPageWithLayout } from 'types'
|
||||
|
||||
const SignInMfaPage: NextPageWithLayout = () => {
|
||||
const router = useRouter()
|
||||
const telemetryProps = useTelemetryProps()
|
||||
const queryClient = useQueryClient()
|
||||
const [loading, setLoading] = useState(true)
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
// This useEffect redirects the user to MFA if they're already halfway signed in
|
||||
useEffect(() => {
|
||||
@@ -42,15 +41,9 @@ const SignInMfaPage: NextPageWithLayout = () => {
|
||||
}
|
||||
|
||||
if (data.currentLevel === data.nextLevel) {
|
||||
Telemetry.sendEvent(
|
||||
{ category: 'account', action: 'sign_in', label: '' },
|
||||
telemetryProps,
|
||||
router
|
||||
)
|
||||
sendEvent({ category: 'account', action: 'sign_in', label: '' })
|
||||
await queryClient.resetQueries()
|
||||
|
||||
router.push(getReturnToPath())
|
||||
|
||||
return
|
||||
}
|
||||
if (data.currentLevel !== data.nextLevel) {
|
||||
|
||||
Vendored
+159
-27
@@ -95,10 +95,6 @@ export interface paths {
|
||||
/** Return quotes for different billing plans for a specific product */
|
||||
get: operations['ProductsController_listResources']
|
||||
}
|
||||
'/partners/vercel/webhooks': {
|
||||
/** Webhooks endpoint */
|
||||
post: operations['WebhooksController_processWebhook']
|
||||
}
|
||||
'/platform/auth/{ref}/config': {
|
||||
/** Gets GoTrue config */
|
||||
get: operations['GoTrueConfigController_getGoTrueConfig']
|
||||
@@ -1077,15 +1073,31 @@ export interface paths {
|
||||
}
|
||||
'/platform/telemetry/event': {
|
||||
/** Sends analytics server event */
|
||||
post: operations['TelemetryEventController_sendServerEvent']
|
||||
post: operations['TelemetryEventController_sendServerEventV2']
|
||||
}
|
||||
'/platform/telemetry/groups/identify': {
|
||||
/** Send analytics group identify event */
|
||||
post: operations['TelemetryGroupsController_groupIdentify']
|
||||
}
|
||||
'/platform/telemetry/groups/reset': {
|
||||
/** Send analytics group reset event */
|
||||
post: operations['TelemetryGroupsController_groupReset']
|
||||
}
|
||||
'/platform/telemetry/identify': {
|
||||
/** Send analytics identify event */
|
||||
post: operations['TelemetryIdentifyController_identify']
|
||||
post: operations['TelemetryIdentifyController_identifyV2']
|
||||
}
|
||||
'/platform/telemetry/page': {
|
||||
/** Send server page event */
|
||||
post: operations['TelemetryPageController_sendServerPage']
|
||||
post: operations['TelemetryPageController_sendServerPageV2']
|
||||
}
|
||||
'/platform/telemetry/page-leave': {
|
||||
/** Send analytics page leave event */
|
||||
post: operations['TelemetryPageLeaveController_pageLeave']
|
||||
}
|
||||
'/platform/telemetry/reset': {
|
||||
/** Reset analytics */
|
||||
post: operations['TelemetryResetController_reset']
|
||||
}
|
||||
'/platform/tos/fly': {
|
||||
/** Redirects to Fly sso flow */
|
||||
@@ -1959,6 +1971,8 @@ export interface paths {
|
||||
post: operations['v1-create-a-project']
|
||||
}
|
||||
'/v1/projects/{ref}': {
|
||||
/** Gets a specific project that belongs to the authenticated user */
|
||||
get: operations['v1-get-project']
|
||||
/** Deletes the given project */
|
||||
delete: operations['v1-delete-a-project']
|
||||
}
|
||||
@@ -5569,16 +5583,64 @@ export interface components {
|
||||
page_title?: string
|
||||
value?: string
|
||||
}
|
||||
TelemetryEventBodyV2: {
|
||||
action: string
|
||||
custom_properties: Record<string, never>
|
||||
page_title: string
|
||||
page_url: string
|
||||
pathname: string
|
||||
ph: components['schemas']['TelemetryEventPostHog']
|
||||
}
|
||||
TelemetryEventPostHog: {
|
||||
language: string
|
||||
referrer: string
|
||||
search: string
|
||||
userAgent: string
|
||||
viewport_height: number
|
||||
viewport_width: number
|
||||
}
|
||||
TelemetryGroupsIdentityBody: {
|
||||
organization_slug?: string
|
||||
project_ref?: string
|
||||
}
|
||||
TelemetryGroupsResetBody: {
|
||||
reset_organization?: boolean
|
||||
reset_project?: boolean
|
||||
}
|
||||
TelemetryIdentifyBody: {
|
||||
ga?: components['schemas']['GoogleAnalyticBody']
|
||||
user: components['schemas']['IdentifyUserBody']
|
||||
}
|
||||
TelemetryIdentifyBodyV2: {
|
||||
organization_slug?: string
|
||||
project_ref?: string
|
||||
user_id: string
|
||||
}
|
||||
TelemetryPageBody: {
|
||||
ga?: components['schemas']['GoogleAnalyticBody']
|
||||
referrer: string
|
||||
route?: string
|
||||
title: string
|
||||
}
|
||||
TelemetryPageBodyV2: {
|
||||
page_title: string
|
||||
page_url: string
|
||||
pathname: string
|
||||
ph: components['schemas']['TelemetryPagePostHog']
|
||||
}
|
||||
TelemetryPageLeaveBody: {
|
||||
page_title: string
|
||||
page_url: string
|
||||
pathname: string
|
||||
}
|
||||
TelemetryPagePostHog: {
|
||||
language: string
|
||||
referrer: string
|
||||
search: string
|
||||
userAgent: string
|
||||
viewport_height: number
|
||||
viewport_width: number
|
||||
}
|
||||
ThirdPartyAuth: {
|
||||
custom_jwks?: unknown
|
||||
id: string
|
||||
@@ -6959,19 +7021,6 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Webhooks endpoint */
|
||||
WebhooksController_processWebhook: {
|
||||
parameters: {
|
||||
header: {
|
||||
'x-vercel-signature': string
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
201: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Gets GoTrue config */
|
||||
GoTrueConfigController_getGoTrueConfig: {
|
||||
parameters: {
|
||||
@@ -13603,10 +13652,10 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
/** Sends analytics server event */
|
||||
TelemetryEventController_sendServerEvent: {
|
||||
TelemetryEventController_sendServerEventV2: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryEventBody']
|
||||
'application/json': components['schemas']['TelemetryEventBodyV2']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
@@ -13619,11 +13668,45 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Send analytics identify event */
|
||||
TelemetryIdentifyController_identify: {
|
||||
/** Send analytics group identify event */
|
||||
TelemetryGroupsController_groupIdentify: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryIdentifyBody']
|
||||
'application/json': components['schemas']['TelemetryGroupsIdentityBody']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
201: {
|
||||
content: never
|
||||
}
|
||||
/** @description Failed to send analytics group identify event */
|
||||
500: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Send analytics group reset event */
|
||||
TelemetryGroupsController_groupReset: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryGroupsResetBody']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
201: {
|
||||
content: never
|
||||
}
|
||||
/** @description Failed to send analytics group reset event */
|
||||
500: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Send analytics identify event */
|
||||
TelemetryIdentifyController_identifyV2: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryIdentifyBodyV2']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
@@ -13637,10 +13720,10 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
/** Send server page event */
|
||||
TelemetryPageController_sendServerPage: {
|
||||
TelemetryPageController_sendServerPageV2: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryPageBody']
|
||||
'application/json': components['schemas']['TelemetryPageBodyV2']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
@@ -13653,6 +13736,35 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Send analytics page leave event */
|
||||
TelemetryPageLeaveController_pageLeave: {
|
||||
requestBody: {
|
||||
content: {
|
||||
'application/json': components['schemas']['TelemetryPageLeaveBody']
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
201: {
|
||||
content: never
|
||||
}
|
||||
/** @description Failed to send analytics page leave event */
|
||||
500: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Reset analytics */
|
||||
TelemetryResetController_reset: {
|
||||
responses: {
|
||||
201: {
|
||||
content: never
|
||||
}
|
||||
/** @description Failed to reset analytics */
|
||||
500: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Redirects to Fly sso flow */
|
||||
TermsOfServiceController_flyTosAccepted: {
|
||||
parameters: {
|
||||
@@ -14865,6 +14977,26 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Gets a specific project that belongs to the authenticated user */
|
||||
'v1-get-project': {
|
||||
parameters: {
|
||||
path: {
|
||||
/** @description Project ref */
|
||||
ref: string
|
||||
}
|
||||
}
|
||||
responses: {
|
||||
200: {
|
||||
content: {
|
||||
'application/json': components['schemas']['V1ProjectResponse']
|
||||
}
|
||||
}
|
||||
/** @description Failed to retrieve project */
|
||||
500: {
|
||||
content: never
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Deletes the given project */
|
||||
'v1-delete-a-project': {
|
||||
parameters: {
|
||||
|
||||
Reference in new issue
Block a user