mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Feat/add studio events (#33148)
* add tracking for help, feedback, import data, and example clicked * clarify www pricing plan and add run sql query event * add pricing plan cta tracking * Add one more event to Import data from CSV * Consistent copy * Clean up and refactors * Some fixes --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
b6aa2af832
commit
b6cefe5c25
15 files changed
+358
-64
No files matched your search
@@ -4,6 +4,7 @@ import { forwardRef, useRef } from 'react'
|
||||
import DataGrid, { DataGridHandle, RowsChangeData } from 'react-data-grid'
|
||||
import { memo } from 'react-tracked'
|
||||
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { formatClipboardValue } from 'components/grid/utils/common'
|
||||
import { TableGridInnerLoadingState } from 'components/interfaces/TableGridEditor/LoadingState'
|
||||
import { formatForeignKeys } from 'components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.utils'
|
||||
@@ -11,6 +12,8 @@ import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/S
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { useForeignKeyConstraintsQuery } from 'data/database/foreign-key-constraints-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useUrlState } from 'hooks/ui/useUrlState'
|
||||
import { copyToClipboard } from 'lib/helpers'
|
||||
import { Button, cn } from 'ui'
|
||||
@@ -133,6 +136,8 @@ export const Grid = memo(
|
||||
|
||||
const table = state.table
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
const org = useSelectedOrganization()
|
||||
const { project } = useProjectContext()
|
||||
const { data } = useForeignKeyConstraintsQuery({
|
||||
projectRef: project?.ref,
|
||||
@@ -214,8 +219,21 @@ export const Grid = memo(
|
||||
</p>
|
||||
<div className="flex items-center space-x-2 mt-4">
|
||||
{onAddRow !== undefined && onImportData !== undefined && (
|
||||
<Button type="default" onClick={onImportData}>
|
||||
Import data via CSV
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => {
|
||||
onImportData()
|
||||
sendEvent({
|
||||
action: TelemetryActions.IMPORT_DATA_BUTTON_CLICKED,
|
||||
properties: { tableType: 'Existing Table' },
|
||||
groups: {
|
||||
project: project?.ref ?? 'Unknown',
|
||||
organization: org?.slug ?? 'Unknown',
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Import data from CSV
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import saveAs from 'file-saver'
|
||||
import { ArrowUp, ChevronDown, FileText, Trash } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import Papa from 'papaparse'
|
||||
import { ReactNode, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { useDispatch, useTrackedState } from 'components/grid/store/Store'
|
||||
import type { Filter, Sort, SupaTable } from 'components/grid/types'
|
||||
import { formatTableRowsToSQL } from 'components/interfaces/TableGridEditor/TableEntity.utils'
|
||||
@@ -12,7 +15,9 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query'
|
||||
import { fetchAllTableRows, useTableRowsQuery } from 'data/table-rows/table-rows-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useUrlState } from 'hooks/ui/useUrlState'
|
||||
import {
|
||||
useRoleImpersonationStateSnapshot,
|
||||
@@ -30,7 +35,6 @@ import {
|
||||
} from 'ui'
|
||||
import FilterPopover from './filter/FilterPopover'
|
||||
import { SortPopover } from './sort'
|
||||
import Link from 'next/link'
|
||||
|
||||
// [Joshen] CSV exports require this guard as a fail-safe if the table is
|
||||
// just too large for a browser to keep all the rows in memory before
|
||||
@@ -105,6 +109,9 @@ type DefaultHeaderProps = {
|
||||
onImportData?: () => void
|
||||
}
|
||||
const DefaultHeader = ({ table, onAddColumn, onAddRow, onImportData }: DefaultHeaderProps) => {
|
||||
const { ref } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const canAddNew = onAddRow !== undefined || onAddColumn !== undefined
|
||||
|
||||
// [Joshen] Using this logic to block both column and row creation/update/delete
|
||||
@@ -114,6 +121,8 @@ const DefaultHeader = ({ table, onAddColumn, onAddRow, onImportData }: DefaultHe
|
||||
arrayKeys: ['sort', 'filter'],
|
||||
})
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
return (
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -195,7 +204,17 @@ const DefaultHeader = ({ table, onAddColumn, onAddRow, onImportData }: DefaultHe
|
||||
<DropdownMenuItem
|
||||
key="import-data"
|
||||
className="group space-x-2"
|
||||
onClick={onImportData}
|
||||
onClick={() => {
|
||||
onImportData()
|
||||
sendEvent({
|
||||
action: TelemetryActions.IMPORT_DATA_BUTTON_CLICKED,
|
||||
properties: { tableType: 'Existing Table' },
|
||||
groups: {
|
||||
project: ref ?? 'Unknown',
|
||||
organization: org?.slug ?? 'Unknown',
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div className="relative -mt-2">
|
||||
<FileText
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { BASE_PATH } from 'lib/constants'
|
||||
|
||||
interface ExampleProjectProps {
|
||||
framework: string
|
||||
title: string
|
||||
@@ -12,9 +17,24 @@ interface ExampleProjectProps {
|
||||
|
||||
const ExampleProject = ({ framework, title, description, url }: ExampleProjectProps) => {
|
||||
const { resolvedTheme } = useTheme()
|
||||
const { ref: projectRef } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
return (
|
||||
<Link href={url} target="_blank" rel="noreferrer">
|
||||
<Link
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
onClick={() =>
|
||||
sendEvent({
|
||||
action: TelemetryActions.EXAMPLE_PROJECT_CARD_CLICKED,
|
||||
properties: { cardTitle: title },
|
||||
groups: { project: projectRef ?? 'Unknown', organization: org?.slug ?? 'Unknown' },
|
||||
})
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
'group relative',
|
||||
|
||||
+44
-11
@@ -1,5 +1,9 @@
|
||||
import { Check } from 'lucide-react'
|
||||
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { PricingInformation } from 'shared-data'
|
||||
import { pickFeatures } from 'shared-data/plans'
|
||||
import { Button, cn } from 'ui'
|
||||
@@ -10,8 +14,15 @@ export interface EnterpriseCardProps {
|
||||
billingPartner: 'fly' | 'aws' | 'vercel_marketplace' | undefined
|
||||
}
|
||||
|
||||
const EnterpriseCard = ({ plan, isCurrentPlan, billingPartner }: EnterpriseCardProps) => {
|
||||
export const EnterpriseCard = ({ plan, isCurrentPlan, billingPartner }: EnterpriseCardProps) => {
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const orgSlug = selectedOrganization?.slug
|
||||
|
||||
const features = pickFeatures(plan, billingPartner)
|
||||
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug })
|
||||
const currentPlan = subscription?.plan.name
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -37,11 +48,23 @@ const EnterpriseCard = ({ plan, isCurrentPlan, billingPartner }: EnterpriseCardP
|
||||
|
||||
<p className="text-sm mt-2 mb-4">{plan.description}</p>
|
||||
|
||||
<a href={plan.href} className="hidden md:block" target="_blank">
|
||||
<Button block type="default" size="tiny">
|
||||
<Button
|
||||
block
|
||||
asChild
|
||||
type="default"
|
||||
size="tiny"
|
||||
onClick={() =>
|
||||
sendEvent({
|
||||
action: TelemetryActions.STUDIO_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: { selectedPlan: 'Enterprise', currentPlan },
|
||||
groups: { organization: orgSlug ?? 'Unknown' },
|
||||
})
|
||||
}
|
||||
>
|
||||
<a href={plan.href} className="hidden md:block" target="_blank">
|
||||
{plan.cta}
|
||||
</Button>
|
||||
</a>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-center col-span-2 px-4 md:px-0">
|
||||
@@ -62,14 +85,24 @@ const EnterpriseCard = ({ plan, isCurrentPlan, billingPartner }: EnterpriseCardP
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<a href={plan.href} className="visible md:hidden mt-8" target="_blank">
|
||||
<Button block type="default" size="tiny">
|
||||
<Button
|
||||
block
|
||||
asChild
|
||||
type="default"
|
||||
size="tiny"
|
||||
onClick={() =>
|
||||
sendEvent({
|
||||
action: TelemetryActions.STUDIO_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: { selectedPlan: 'Enterprise', currentPlan },
|
||||
groups: { organization: orgSlug ?? 'Unknown' },
|
||||
})
|
||||
}
|
||||
>
|
||||
<a href={plan.href} className="visible md:hidden mt-8" target="_blank">
|
||||
{plan.cta}
|
||||
</Button>
|
||||
</a>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default EnterpriseCard
|
||||
+15
-2
@@ -7,6 +7,7 @@ import { useRouter } from 'next/router'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { billingPartnerLabel } from 'components/interfaces/Billing/Subscription/Subscription.utils'
|
||||
import Table from 'components/to-be-cleaned/Table'
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
@@ -22,6 +23,7 @@ import { useOrgPlansQuery } from 'data/subscriptions/org-plans-query'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
|
||||
import type { OrgPlan, SubscriptionTier } from 'data/subscriptions/types'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
@@ -31,7 +33,7 @@ import { pickFeatures, pickFooter, plans as subscriptionsPlans } from 'shared-da
|
||||
import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings'
|
||||
import { Button, Modal, SidePanel, cn } from 'ui'
|
||||
import DowngradeModal from './DowngradeModal'
|
||||
import EnterpriseCard from './EnterpriseCard'
|
||||
import { EnterpriseCard } from './EnterpriseCard'
|
||||
import ExitSurveyModal from './ExitSurveyModal'
|
||||
import MembersExceedLimitModal from './MembersExceedLimitModal'
|
||||
import PaymentMethodSelection from './PaymentMethodSelection'
|
||||
@@ -41,6 +43,7 @@ const PlanUpdateSidePanel = () => {
|
||||
const router = useRouter()
|
||||
const selectedOrganization = useSelectedOrganization()
|
||||
const slug = selectedOrganization?.slug
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const queryClient = useQueryClient()
|
||||
const originalPlanRef = useRef<string>()
|
||||
@@ -260,7 +263,17 @@ const PlanUpdateSidePanel = () => {
|
||||
hasOrioleProjects ||
|
||||
!canUpdateSubscription
|
||||
}
|
||||
onClick={() => setSelectedTier(plan.id as any)}
|
||||
onClick={() => {
|
||||
setSelectedTier(plan.id as any)
|
||||
sendEvent({
|
||||
action: TelemetryActions.STUDIO_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
selectedPlan: plan.name,
|
||||
currentPlan: subscription?.plan?.name,
|
||||
},
|
||||
groups: { organization: slug ?? 'Unknown' },
|
||||
})
|
||||
}}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
|
||||
@@ -316,6 +316,11 @@ export const SQLEditor = () => {
|
||||
throw error
|
||||
},
|
||||
})
|
||||
|
||||
sendEvent({
|
||||
action: TelemetryActions.SQL_EDITOR_QUERY_RUN_BUTTON_CLICKED,
|
||||
groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' },
|
||||
})
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
@@ -13,7 +13,9 @@ import { toast } from 'sonner'
|
||||
import { useParams } from 'common'
|
||||
import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector'
|
||||
import DatabaseSelector from 'components/ui/DatabaseSelector'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants'
|
||||
import { detectOS } from 'lib/helpers'
|
||||
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
|
||||
@@ -51,6 +53,9 @@ const UtilityActions = ({
|
||||
const os = detectOS()
|
||||
const { ref } = useParams()
|
||||
const snapV2 = useSqlEditorV2StateSnapshot()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const [isAiOpen] = useLocalStorageQuery(LOCAL_STORAGE_KEYS.SQL_EDITOR_AI_OPEN, true)
|
||||
const [intellisenseEnabled, setIntellisenseEnabled] = useLocalStorageQuery(
|
||||
@@ -191,7 +196,7 @@ const UtilityActions = ({
|
||||
/>
|
||||
<RoleImpersonationPopover serviceRoleLabel="postgres" variant="connected-on-both" />
|
||||
<Button
|
||||
onClick={() => executeQuery()}
|
||||
onClick={executeQuery}
|
||||
disabled={isDisabled || isExecuting}
|
||||
type="primary"
|
||||
size="tiny"
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import { FileText, Loader } from 'lucide-react'
|
||||
import { DragEvent, useRef, useState } from 'react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import SparkBar from 'components/ui/SparkBar'
|
||||
import { FileText, Loader } from 'lucide-react'
|
||||
import { Button } from 'ui'
|
||||
|
||||
interface SpreadSheetFileUploadProps {
|
||||
parseProgress: number
|
||||
|
||||
+16
-5
@@ -5,6 +5,10 @@ import Link from 'next/link'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { Button, SidePanel, Tabs } from 'ui'
|
||||
import ActionBar from '../ActionBar'
|
||||
import type { ImportContent } from '../TableEditor/TableEditor.types'
|
||||
@@ -43,11 +47,8 @@ const SpreadsheetImport = ({
|
||||
closePanel,
|
||||
updateEditorDirty = noop,
|
||||
}: SpreadsheetImportProps) => {
|
||||
useEffect(() => {
|
||||
if (visible && headers.length === 0) {
|
||||
resetSpreadsheetImport()
|
||||
}
|
||||
}, [visible])
|
||||
const { ref: projectRef } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const [tab, setTab] = useState<'fileUpload' | 'pasteText'>('fileUpload')
|
||||
const [input, setInput] = useState<string>('')
|
||||
@@ -62,6 +63,8 @@ const SpreadsheetImport = ({
|
||||
const [errors, setErrors] = useState<any>([])
|
||||
const [selectedHeaders, setSelectedHeaders] = useState<string[]>([])
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const selectedTableColumns = (selectedTable?.columns ?? []).map((column) => column.name)
|
||||
const incompatibleHeaders = selectedHeaders.filter(
|
||||
(header) => !selectedTableColumns.includes(header)
|
||||
@@ -168,9 +171,17 @@ const SpreadsheetImport = ({
|
||||
resolve()
|
||||
} else {
|
||||
saveContent({ file: uploadedFile, ...spreadsheetData, selectedHeaders, resolve })
|
||||
sendEvent({
|
||||
action: TelemetryActions.IMPORT_DATA_ADDED,
|
||||
groups: { project: projectRef ?? 'Unknown', organization: org?.slug ?? 'Unknown' },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && headers.length === 0) resetSpreadsheetImport()
|
||||
}, [visible])
|
||||
|
||||
return (
|
||||
<SidePanel
|
||||
size="large"
|
||||
|
||||
+24
-2
@@ -9,8 +9,12 @@ import {
|
||||
DroppableProvided,
|
||||
} from 'react-beautiful-dnd'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import InformationBox from 'components/ui/InformationBox'
|
||||
import type { EnumeratedType } from 'data/enumerated-types/enumerated-types-query'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import {
|
||||
AlertDescription_Shadcn_,
|
||||
AlertTitle_Shadcn_,
|
||||
@@ -54,10 +58,15 @@ const ColumnManagement = ({
|
||||
onClearImportContent = noop,
|
||||
onUpdateFkRelations,
|
||||
}: ColumnManagementProps) => {
|
||||
const { ref: projectRef } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const [selectedColumn, setSelectedColumn] = useState<ColumnField>()
|
||||
const [selectedFk, setSelectedFk] = useState<ForeignKey>()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const hasImportContent = !isEmpty(importContent)
|
||||
const [primaryKeyColumns, otherColumns] = partition(
|
||||
columns,
|
||||
@@ -156,8 +165,21 @@ const ColumnManagement = ({
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button type="default" onClick={onSelectImportData}>
|
||||
Import data via spreadsheet
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => {
|
||||
onSelectImportData()
|
||||
sendEvent({
|
||||
action: TelemetryActions.IMPORT_DATA_BUTTON_CLICKED,
|
||||
properties: { tableType: 'New Table' },
|
||||
groups: {
|
||||
project: projectRef ?? 'Unknown',
|
||||
organization: org?.slug ?? 'Unknown',
|
||||
},
|
||||
})
|
||||
}}
|
||||
>
|
||||
Import data from CSV
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
|
||||
+21
-5
@@ -4,6 +4,13 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { ChangeEvent, useEffect, useRef, useState } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { useSendFeedbackMutation } from 'data/feedback/feedback-send'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { timeout } from 'lib/helpers'
|
||||
import {
|
||||
Button,
|
||||
DropdownMenu,
|
||||
@@ -12,10 +19,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
Input,
|
||||
} from 'ui'
|
||||
|
||||
import { useParams } from 'common'
|
||||
import { useSendFeedbackMutation } from 'data/feedback/feedback-send'
|
||||
import { timeout } from 'lib/helpers'
|
||||
import { convertB64toBlob, uploadAttachment } from './FeedbackDropdown.utils'
|
||||
|
||||
interface FeedbackWidgetProps {
|
||||
@@ -38,11 +41,14 @@ const FeedbackWidget = ({
|
||||
|
||||
const router = useRouter()
|
||||
const { ref, slug } = useParams()
|
||||
const org = useSelectedOrganization()
|
||||
const uploadButtonRef = useRef(null)
|
||||
|
||||
const [isSending, setSending] = useState(false)
|
||||
const [isSavingScreenshot, setIsSavingScreenshot] = useState(false)
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const { mutate: submitFeedback } = useSendFeedbackMutation({
|
||||
onSuccess: () => {
|
||||
setFeedback('')
|
||||
@@ -261,7 +267,17 @@ const FeedbackWidget = ({
|
||||
accept="image/png"
|
||||
onChange={onFilesUpload}
|
||||
/>
|
||||
<Button disabled={isSending} loading={isSending} onClick={sendFeedback}>
|
||||
<Button
|
||||
disabled={feedback.length === 0 || isSending}
|
||||
loading={isSending}
|
||||
onClick={() => {
|
||||
sendFeedback()
|
||||
sendEvent({
|
||||
action: TelemetryActions.SEND_FEEDBACK_BUTTON_CLICKED,
|
||||
groups: { project: ref, organization: org?.slug },
|
||||
})
|
||||
}}
|
||||
>
|
||||
Send feedback
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,10 @@ import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import SVG from 'react-inlinesvg'
|
||||
|
||||
import { TelemetryActions } from 'common/telemetry-constants'
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
@@ -17,6 +20,10 @@ import { useProjectContext } from '../ProjectContext'
|
||||
const HelpPopover = () => {
|
||||
const router = useRouter()
|
||||
const { project } = useProjectContext()
|
||||
const org = useSelectedOrganization()
|
||||
|
||||
const { mutate: sendEvent } = useSendEventMutation()
|
||||
|
||||
const projectRef = project?.parent_project_ref ?? router.query.ref
|
||||
const supportUrl = `/support/new${projectRef ? `?ref=${projectRef}` : ''}`
|
||||
|
||||
@@ -29,6 +36,12 @@ const HelpPopover = () => {
|
||||
className="px-1"
|
||||
icon={<HelpCircle size={16} strokeWidth={1.5} className="text-foreground-light" />}
|
||||
tooltip={{ content: { side: 'bottom', text: 'Help' } }}
|
||||
onClick={() => {
|
||||
sendEvent({
|
||||
action: TelemetryActions.HELP_BUTTON_CLICKED,
|
||||
groups: { project: project?.ref, organization: org?.slug },
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</PopoverTrigger_Shadcn_>
|
||||
<PopoverContent_Shadcn_ className="w-[400px] space-y-4 p-0 py-5" align="end" side="bottom">
|
||||
|
||||
@@ -57,7 +57,7 @@ const MobileHeader = ({
|
||||
organizations={organizations}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED,
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
plan,
|
||||
showUpgradeText: true,
|
||||
@@ -74,7 +74,7 @@ const MobileHeader = ({
|
||||
href={selectedPlan.href}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED,
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
plan,
|
||||
showUpgradeText: false,
|
||||
@@ -403,7 +403,7 @@ const PricingComparisonTable = ({
|
||||
organizations={organizations}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED,
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
plan: plan.name,
|
||||
showUpgradeText: true,
|
||||
@@ -425,7 +425,7 @@ const PricingComparisonTable = ({
|
||||
href={plan.href}
|
||||
onClick={() =>
|
||||
sendTelemetryEvent({
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED,
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
plan: plan.name,
|
||||
showUpgradeText: false,
|
||||
|
||||
@@ -29,7 +29,7 @@ const PricingPlans = ({ organizations, hasExistingOrganizations }: PricingPlansP
|
||||
|
||||
const sendPricingEvent = () => {
|
||||
sendTelemetryEvent({
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED,
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED,
|
||||
properties: {
|
||||
plan: plan.name,
|
||||
showUpgradeText: isUpgradablePlan && hasExistingOrganizations ? true : false,
|
||||
|
||||
@@ -50,8 +50,7 @@ export enum TelemetryActions {
|
||||
HOMEPAGE_HERO_REQUEST_DEMO_CLICKED = 'homepage_hero_request_demo_clicked',
|
||||
HOMEPAGE_FRAMEWORK_QUICKSTART_CLICKED = 'homepage_framework_quickstart_clicked',
|
||||
HOMEPAGE_PRODUCT_CARD_CLICKED = 'homepage_product_card_clicked',
|
||||
PRICING_PLAN_CTA_CLICKED = 'pricing_plan_cta_clicked',
|
||||
PRICING_COMPARISON_PLAN_CLICKED = 'pricing_comparison_plan_clicked',
|
||||
WWW_PRICING_PLAN_CTA_CLICKED = 'www_pricing_plan_cta_clicked',
|
||||
EVENT_PAGE_CTA_CLICKED = 'event_page_cta_clicked',
|
||||
HOMEPAGE_GITHUB_BUTTON_CLICKED = 'homepage_github_button_clicked',
|
||||
HOMEPAGE_GITHUB_DISCUSSIONS_BUTTON_CLICKED = 'homepage_github_discussions_button_clicked',
|
||||
@@ -63,6 +62,14 @@ export enum TelemetryActions {
|
||||
SEE_DOCUMENTATION_BUTTON_CLICKED = 'see_documentation_button_clicked',
|
||||
REQUEST_DEMO_BUTTON_CLICKED = 'request_demo_button_clicked',
|
||||
SIGN_IN_BUTTON_CLICKED = 'sign_in_button_clicked',
|
||||
|
||||
HELP_BUTTON_CLICKED = 'help_button_clicked',
|
||||
SEND_FEEDBACK_BUTTON_CLICKED = 'send_feedback_button_clicked',
|
||||
EXAMPLE_PROJECT_CARD_CLICKED = 'example_project_card_clicked',
|
||||
IMPORT_DATA_BUTTON_CLICKED = 'import_data_button_clicked',
|
||||
IMPORT_DATA_ADDED = 'import_data_added',
|
||||
SQL_EDITOR_QUERY_RUN_BUTTON_CLICKED = 'sql_editor_query_run_button_clicked',
|
||||
STUDIO_PRICING_PLAN_CTA_CLICKED = 'studio_pricing_plan_cta_clicked',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -734,14 +741,14 @@ export interface HomepageProductCardClickedEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the green button on a plan in the pricing page.
|
||||
* User clicked on the CTA button on a plan in the pricing page.
|
||||
*
|
||||
* @group Events
|
||||
* @source www
|
||||
* @page /pricing
|
||||
*/
|
||||
export interface PricingPlanCtaClickedEvent {
|
||||
action: TelemetryActions.PRICING_PLAN_CTA_CLICKED
|
||||
export interface WwwPricingPlanCtaClickedEvent {
|
||||
action: TelemetryActions.WWW_PRICING_PLAN_CTA_CLICKED
|
||||
properties: {
|
||||
/**
|
||||
* The plan type that was clicked.
|
||||
@@ -761,24 +768,6 @@ export interface PricingPlanCtaClickedEvent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the plan in the pricing comparison section.
|
||||
*
|
||||
* @group Events
|
||||
* @source www
|
||||
* @page /pricing
|
||||
*/
|
||||
export interface PricingComparisonPlanClickedEvent {
|
||||
action: TelemetryActions.PRICING_COMPARISON_PLAN_CLICKED
|
||||
properties: {
|
||||
category: 'pricing_comparison'
|
||||
/**
|
||||
* The plan type that was clicked.
|
||||
*/
|
||||
plan: 'free' | 'pro' | 'team' | 'enterprise'
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the main CTA button in an event page.
|
||||
*
|
||||
@@ -946,6 +935,130 @@ export interface SignInButtonClickedEvent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the "Help" button in the top right corner of the page header.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
*/
|
||||
export interface HelpButtonClickedEvent {
|
||||
action: TelemetryActions.HELP_BUTTON_CLICKED
|
||||
groups: {
|
||||
project?: string
|
||||
organization?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the "Send Feedback" button in the top right corner of the page header.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
*/
|
||||
export interface SendFeedbackButtonClickedEvent {
|
||||
action: TelemetryActions.SEND_FEEDBACK_BUTTON_CLICKED
|
||||
groups: {
|
||||
project?: string
|
||||
organization?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked on an example project card.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /project/{ref}
|
||||
*/
|
||||
export interface ExampleProjectCardClickedEvent {
|
||||
action: TelemetryActions.EXAMPLE_PROJECT_CARD_CLICKED
|
||||
properties: {
|
||||
/**
|
||||
* The title of the example project card clicked.
|
||||
*/
|
||||
cardTitle: string
|
||||
}
|
||||
groups: {
|
||||
project: string
|
||||
organization: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the "Import Data" button.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /dashboard/project/{ref}/editor
|
||||
*/
|
||||
export interface ImportDataButtonClickedEvent {
|
||||
action: TelemetryActions.IMPORT_DATA_BUTTON_CLICKED
|
||||
properties: {
|
||||
/**
|
||||
* The type of table the data is imported to.
|
||||
* New Table means added when creating new table by clicking from New table sidebar,
|
||||
* Existing Table means added to an existing table by going to the table and clicking from the green Insert button..
|
||||
*/
|
||||
tableType: 'New Table' | 'Existing Table'
|
||||
}
|
||||
groups: {
|
||||
project: string
|
||||
organization: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User added data from the import data via CSV/spreadsheet successfully.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /dashboard/project/{ref}/editor
|
||||
*/
|
||||
export interface ImportDataAddedEvent {
|
||||
action: TelemetryActions.IMPORT_DATA_ADDED
|
||||
groups: {
|
||||
project: string
|
||||
organization: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked the run query button in the SQL editor.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /dashboard/project/{ref}/sql/{id}
|
||||
*/
|
||||
export interface SqlEditorQueryRunButtonClickedEvent {
|
||||
action: TelemetryActions.SQL_EDITOR_QUERY_RUN_BUTTON_CLICKED
|
||||
groups: {
|
||||
project: string
|
||||
organization: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User clicked on the CTA button on a plan in the pricing side panel in studio.
|
||||
*
|
||||
* @group Events
|
||||
* @source studio
|
||||
* @page /billing?panel=subscriptionPlan
|
||||
*/
|
||||
export interface StudioPricingPlanCtaClickedEvent {
|
||||
action: TelemetryActions.STUDIO_PRICING_PLAN_CTA_CLICKED
|
||||
properties: {
|
||||
/**
|
||||
* The plan type that was clicked.
|
||||
*/
|
||||
selectedPlan: string
|
||||
/**
|
||||
* The plan type the org is currently on.
|
||||
*/
|
||||
currentPlan?: string
|
||||
}
|
||||
groups: { organization: string }
|
||||
}
|
||||
|
||||
export type TelemetryEvent =
|
||||
| SignUpEvent
|
||||
| SignInEvent
|
||||
@@ -985,8 +1098,7 @@ export type TelemetryEvent =
|
||||
| HomepageHeroRequestDemoClickedEvent
|
||||
| HomepageFrameworkQuickstartClickedEvent
|
||||
| HomepageProductCardClickedEvent
|
||||
| PricingPlanCtaClickedEvent
|
||||
| PricingComparisonPlanClickedEvent
|
||||
| WwwPricingPlanCtaClickedEvent
|
||||
| EventPageCtaClickedEvent
|
||||
| HomepageGitHubButtonClickedEvent
|
||||
| HomepageGitHubDiscussionsButtonClickedEvent
|
||||
@@ -1000,3 +1112,10 @@ export type TelemetryEvent =
|
||||
| SeeDocumentationButtonClickedEvent
|
||||
| RequestDemoButtonClickedEvent
|
||||
| SignInButtonClickedEvent
|
||||
| HelpButtonClickedEvent
|
||||
| ExampleProjectCardClickedEvent
|
||||
| ImportDataButtonClickedEvent
|
||||
| ImportDataAddedEvent
|
||||
| SendFeedbackButtonClickedEvent
|
||||
| SqlEditorQueryRunButtonClickedEvent
|
||||
| StudioPricingPlanCtaClickedEvent
|
||||
Reference in new issue
Block a user