Replace ui setnotification with toast part 2 (#21872)

* Replace ui setnotification with toast part 2

* Prettier lint
This commit is contained in:
Joshen Lim authored and GitHub committed 2024-03-08 18:28:21 +08:00
1 parent 827b01c37a
commit 540049992d
50 files changed
+388 -706

No files matched your search

@@ -1,23 +1,23 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { codeBlock } from 'common-tags'
import { PropsWithChildren, useMemo } from 'react'
import toast from 'react-hot-toast'
import { CommandMenuProvider } from 'ui'
import { codeBlock } from 'common-tags'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useProjectApiQuery } from 'data/config/project-api-query'
import type { SqlSnippet } from 'data/content/sql-snippets-query'
import { useEntityDefinitionsQuery } from 'data/database/entity-definitions-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { OPT_IN_TAGS } from 'lib/constants'
import { uuidv4 } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { createSqlSnippetSkeleton } from '../SQLEditor/SQLEditor.utils'
import { OPT_IN_TAGS } from 'lib/constants'
const CommandMenuWrapper = ({ children }: PropsWithChildren<{}>) => {
const { ref } = useParams()
const { ui } = useStore()
const selectedOrganization = useSelectedOrganization()
const { project: selectedProject } = useProjectContext()
const opt_in_tags = selectedOrganization?.opt_in_tags
@@ -56,10 +56,7 @@ const CommandMenuWrapper = ({ children }: PropsWithChildren<{}>) => {
const onSaveGeneratedSQL = async (answer: string, title: string) => {
if (!ref) return console.error('Project ref is required')
if (!canCreateSQLSnippet) {
ui.setNotification({
category: 'info',
message: 'Unable to save query as you do not have sufficient permissions for this project',
})
toast('Unable to save query as you do not have sufficient permissions for this project')
return
}
@@ -84,15 +81,9 @@ const CommandMenuWrapper = ({ children }: PropsWithChildren<{}>) => {
})
snap.addSnippet(snippet as SqlSnippet, ref)
ui.setNotification({
category: 'success',
message: `Successfully saved snippet!`,
})
toast.success(`Successfully saved snippet!`)
} catch (error: any) {
ui.setNotification({
category: 'error',
message: `Failed to create new query: ${error.message}`,
})
toast.error(`Failed to create new query: ${error.message}`)
}
}
@@ -1,17 +1,17 @@
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
import { PropsWithChildren, useEffect } from 'react'
import toast from 'react-hot-toast'
import { useIsLoggedIn, useParams } from 'common'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useFlag, useStore, useLatest } from 'hooks'
import { useFlag, useLatest } from 'hooks'
import { DEFAULT_HOME, IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
// Ideally these could all be within a _middleware when we use Next 12
const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
const { ui } = useStore()
const router = useRouter()
const { ref, slug, id } = useParams()
const navLayoutV2 = useFlag('navigationLayoutV2')
@@ -55,7 +55,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
const isValidOrg = organizations.some((org) => org.slug === slug)
if (!isValidOrg) {
ui.setNotification({ category: 'error', message: 'This organization does not exist' })
toast.error('This organization does not exist')
router.push(navLayoutV2 ? `/org/${organizations[0].slug}` : DEFAULT_HOME)
return
}
@@ -80,7 +80,7 @@ const RouteValidationWrapper = ({ children }: PropsWithChildren<{}>) => {
: true
if (!isValidProject && !isValidBranch) {
ui.setNotification({ category: 'error', message: 'This project does not exist' })
toast.error('This project does not exist')
router.push(navLayoutV2 ? `/org/${organizations?.[0].slug}` : DEFAULT_HOME)
return
}
@@ -1,6 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -28,7 +29,7 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { IS_PLATFORM } from 'lib/constants'
import FormField from '../AuthProvidersForm/FormField'
@@ -74,7 +75,6 @@ function HoursOrNeverText({ value }: { value: number }) {
const formId = 'auth-config-basic-settings'
const BasicAuthSettingsForm = () => {
const { ui } = useStore()
const { ref: projectRef } = useParams()
const {
data: authConfig,
@@ -127,16 +127,10 @@ const BasicAuthSettingsForm = () => {
{ projectRef: projectRef!, config: payload },
{
onError: (error) => {
ui.setNotification({
category: 'error',
message: `Failed to update settings: ${error?.message}`,
})
toast.error(`Failed to update settings: ${error?.message}`)
},
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully updated settings`,
})
toast.success(`Successfully updated settings`)
resetForm({ values: values, initialValues: values })
},
}
@@ -1,6 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { useEffect } from 'react'
import toast from 'react-hot-toast'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -23,7 +24,7 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import SchemaFunctionSelector from './SchemaFunctionSelector'
const schema = object({
@@ -36,7 +37,6 @@ const schema = object({
const FORM_ID = 'enterprise-hooks-config'
const EnterpriseHooksConfig = () => {
const { ui } = useStore()
const { ref: projectRef } = useParams()
const {
data: authConfig,
@@ -79,16 +79,10 @@ const EnterpriseHooksConfig = () => {
{ projectRef: projectRef!, config: payload },
{
onError: () => {
ui.setNotification({
category: 'error',
message: `Failed to update settings`,
})
toast.error(`Failed to update settings`)
},
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully updated settings`,
})
toast.success(`Successfully updated settings`)
resetForm({ values: values, initialValues: values })
},
}
@@ -1,11 +1,11 @@
import { isEmpty, noop } from 'lodash'
import { useEffect, useState } from 'react'
import { Modal } from 'ui'
import toast from 'react-hot-toast'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useStore } from 'hooks'
import { LOCAL_STORAGE_KEYS } from 'lib/constants'
import { useAppStateSnapshot } from 'state/app-state'
import { Modal } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { POLICY_MODAL_VIEWS } from '../Policies.constants'
import {
PolicyFormField,
@@ -48,7 +48,6 @@ const PolicyEditorModal = ({
onUpdatePolicy,
onSaveSuccess = noop,
}: PolicyEditorModalProps) => {
const { ui } = useStore()
const snap = useAppStateSnapshot()
const newPolicyTemplate: PolicyFormField = {
@@ -124,36 +123,21 @@ const PolicyEditorModal = ({
const { name, definition, check, command } = policyFormFields
if (name.length === 0) {
return ui.setNotification({ category: 'error', message: 'Do give your policy a name' })
return toast.error('Please provide a name for your policy')
}
if (!command) {
return ui.setNotification({
category: 'error',
message: 'You will need to allow at one operation in your policy',
duration: 4000,
})
return toast.error('Please select an operation for your policy')
}
if (['SELECT', 'DELETE'].includes(command) && !definition) {
return ui.setNotification({
category: 'error',
message: 'Did you forget to provide a USING expression for your policy?',
duration: 4000,
})
return toast.error('Please provide a USING expression for your policy')
}
if (command === 'INSERT' && !check) {
return ui.setNotification({
category: 'error',
message: 'Did you forget to provide a WITH CHECK expression for your policy?',
duration: 4000,
})
return toast.error('Please provide a WITH CHECK expression for your policy')
}
if (command === 'UPDATE' && !definition && !check) {
return ui.setNotification({
category: 'error',
message:
'You will need to provide either a USING, or WITH CHECK expression, or both for your policy',
duration: 4000,
})
return toast.error(
'Please provide either a USING, or WITH CHECK expression, or both for your policy'
)
}
const policySQLStatement = createSQLPolicy(policyFormFields, selectedPolicyToEdit)
setPolicyStatementForReview(policySQLStatement)
@@ -1,15 +1,14 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { useParams } from 'common'
import { useState } from 'react'
import toast from 'react-hot-toast'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useUserDeleteMFAFactorsMutation } from 'data/auth/user-delete-mfa-factors-mutation'
import { useUserDeleteMutation } from 'data/auth/user-delete-mutation'
import { useUserResetPasswordMutation } from 'data/auth/user-reset-password-mutation'
import { useUserSendMagicLinkMutation } from 'data/auth/user-send-magic-link-mutation'
import { useUserSendOTPMutation } from 'data/auth/user-send-otp-mutation'
import type { User } from 'data/auth/users-query'
import { useStore } from 'hooks'
import { timeout } from 'lib/helpers'
import {
Button,
@@ -26,6 +25,7 @@ import {
IconUser,
Modal,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
interface UserDropdownProps {
user: User
@@ -42,31 +42,21 @@ const UserDropdown = ({
setSelectedUser,
setUserSidePanelOpen,
}: UserDropdownProps) => {
const { ui } = useStore()
const { ref } = useParams()
const { mutate: resetPassword, isLoading: isResetting } = useUserResetPasswordMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Sent password recovery to ${user.email}`,
})
toast.success(`Sent password recovery to ${user.email}`)
},
})
const { mutate: sendMagicLink, isLoading: isSendingLink } = useUserSendMagicLinkMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Sent magic link to ${user.email}`,
})
toast.success(`Sent magic link to ${user.email}`)
},
})
const { mutate: sendOTP, isLoading: isSendingOTP } = useUserSendOTPMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Sent OTP to ${user.phone}`,
})
toast.success(`Sent OTP to ${user.phone}`)
},
})
const { mutateAsync: deleteUser, isLoading: isDeleting } = useUserDeleteMutation()
@@ -97,13 +87,10 @@ const UserDropdown = ({
if (!ref) return console.error('Project ref is required')
try {
await deleteUser({ projectRef: ref, user })
ui.setNotification({ category: 'success', message: `Successfully deleted ${user.email}` })
toast.success(`Successfully deleted ${user.email}`)
setIsDeleteModalOpen(false)
} catch (error: any) {
ui.setNotification({
category: 'error',
message: error?.message ?? 'Something went wrong while trying to delete user',
})
toast.error(error?.message ?? 'Something went wrong while trying to delete user')
}
}
@@ -117,16 +104,10 @@ const UserDropdown = ({
try {
await deleteUserMFAFactors({ projectRef: ref, userId: user.id })
ui.setNotification({
category: 'success',
message: "Successfully deleted the user's factors",
})
toast.success("Successfully deleted the user's factors")
setIsDeleteFactorsModalOpen(false)
} catch (error: any) {
ui.setNotification({
category: 'error',
message: error?.message ?? "Something went wrong while trying to delete user's factors",
})
toast.error(error?.message ?? "Something went wrong while trying to delete user's factors")
} finally {
}
}
@@ -4,6 +4,16 @@ import { MessageCircle } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { useBranchDeleteMutation } from 'data/branches/branch-delete-mutation'
import { useBranchesDisableMutation } from 'data/branches/branches-disable-mutation'
import { Branch, useBranchesQuery } from 'data/branches/branches-query'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
import { useGitHubPullRequestsQuery } from 'data/integrations/github-pull-requests-query'
import { useSelectedOrganization, useSelectedProject } from 'hooks'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -16,15 +26,6 @@ import {
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import AlertError from 'components/ui/AlertError'
import { useBranchDeleteMutation } from 'data/branches/branch-delete-mutation'
import { useBranchesDisableMutation } from 'data/branches/branches-disable-mutation'
import { Branch, useBranchesQuery } from 'data/branches/branches-query'
import { useGitHubConnectionsQuery } from 'data/integrations/github-connections-query'
import { useGitHubPullRequestsQuery } from 'data/integrations/github-pull-requests-query'
import { useSelectedOrganization, useSelectedProject, useStore } from 'hooks'
import { BranchLoader, BranchManagementSection, BranchRow } from './BranchPanels'
import CreateBranchModal from './CreateBranchModal'
import {
@@ -35,7 +36,6 @@ import {
import Overview from './Overview'
const BranchManagement = () => {
const { ui } = useStore()
const router = useRouter()
const { ref } = useParams()
const project = useSelectedProject()
@@ -119,14 +119,12 @@ const BranchManagement = () => {
const { mutate: deleteBranch, isLoading: isDeleting } = useBranchDeleteMutation({
onSuccess: () => {
if (selectedBranchToDelete?.project_ref === ref) {
ui.setNotification({
category: 'success',
message:
'Successfully deleted branch. You are now currently on the main branch of your project.',
})
toast.success(
'Successfully deleted branch. You are now currently on the main branch of your project.'
)
router.push(`/project/${projectRef}/branches`)
} else {
ui.setNotification({ category: 'success', message: 'Successfully deleted branch' })
toast.success('Successfully deleted branch')
}
setSelectedBranchToDelete(undefined)
},
@@ -134,10 +132,7 @@ const BranchManagement = () => {
const { mutate: disableBranching, isLoading: isDisabling } = useBranchesDisableMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: 'Successfully disabled branching for project',
})
toast.success('Successfully disabled branching for project')
setShowDisableBranching(false)
},
})
@@ -1,9 +1,9 @@
import type { PostgresTrigger } from '@supabase/postgres-meta'
import toast from 'react-hot-toast'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal'
import { useDatabaseTriggerDeleteMutation } from 'data/database-triggers/database-trigger-delete-mutation'
import { useStore } from 'hooks'
interface DeleteHookModalProps {
visible: boolean
@@ -12,14 +12,13 @@ interface DeleteHookModalProps {
}
const DeleteHookModal = ({ selectedHook, visible, onClose }: DeleteHookModalProps) => {
const { ui } = useStore()
const { id, name, schema } = selectedHook ?? {}
const { project } = useProjectContext()
const { mutate: deleteDatabaseTrigger, isLoading: isDeleting } = useDatabaseTriggerDeleteMutation(
{
onSuccess: () => {
ui.setNotification({ category: 'success', message: `Successfully deleted ${name}` })
toast.success(`Successfully deleted ${name}`)
onClose()
},
}
@@ -30,7 +29,7 @@ const DeleteHookModal = ({ selectedHook, visible, onClose }: DeleteHookModalProp
return console.error('Project ref is required')
}
if (!id) {
return ui.setNotification({ category: 'error', message: 'Unable find selected hook' })
return toast.error('Unable find selected hook')
}
deleteDatabaseTrigger({
@@ -1,10 +1,10 @@
import type { PostgresTable, PostgresTrigger } from '@supabase/postgres-meta'
import Image from 'next/legacy/image'
import { MutableRefObject, useEffect, useMemo, useRef, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common/hooks'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms'
import { useDatabaseTriggerCreateMutation } from 'data/database-triggers/database-trigger-create-mutation'
import { useDatabaseTriggerUpdateMutation } from 'data/database-triggers/database-trigger-update-transaction-mutation'
@@ -14,11 +14,11 @@ import {
} from 'data/edge-functions/edge-functions-query'
import { getTable } from 'data/tables/table-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useStore } from 'hooks'
import { isValidHttpUrl, tryParseJson, uuidv4 } from 'lib/helpers'
import { Button, Checkbox, Form, Input, Listbox, Modal, Radio, SidePanel } from 'ui'
import { AVAILABLE_WEBHOOK_TYPES, HOOK_EVENTS } from './Hooks.constants'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import HTTPRequestFields from './HTTPRequestFields'
import { AVAILABLE_WEBHOOK_TYPES, HOOK_EVENTS } from './Hooks.constants'
export interface EditHookPanelProps {
visible: boolean
@@ -30,7 +30,6 @@ export type HTTPArgument = { id: string; name: string; value: string }
const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) => {
const { ref } = useParams()
const { ui } = useStore()
const submitRef = useRef<any>(null)
const [isEdited, setIsEdited] = useState(false)
const [isClosingPanel, setIsClosingPanel] = useState(false)
@@ -53,38 +52,24 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
const [isSubmitting, setIsSubmitting] = useState(false)
const { mutate: createDatabaseTrigger } = useDatabaseTriggerCreateMutation({
onSuccess: (res) => {
ui.setNotification({
category: 'success',
message: `Successfully created new webhook "${res.name}"`,
})
toast.success(`Successfully created new webhook "${res.name}"`)
setIsSubmitting(false)
onClose()
},
onError: (error) => {
setIsSubmitting(false)
ui.setNotification({
error,
category: 'error',
message: `Failed to create webhook: ${error.message}`,
})
toast.error(`Failed to create webhook: ${error.message}`)
},
})
const { mutate: updateDatabaseTrigger } = useDatabaseTriggerUpdateMutation({
onSuccess: (res) => {
setIsSubmitting(false)
ui.setNotification({
category: 'success',
message: `Successfully updated webhook "${res.name}"`,
})
toast.success(`Successfully updated webhook "${res.name}"`)
onClose()
},
onError: (error) => {
setIsSubmitting(false)
ui.setNotification({
error,
category: 'error',
message: `Failed to update webhook: ${error.message}`,
})
toast.error(`Failed to update webhook: ${error.message}`)
},
})
@@ -204,7 +189,7 @@ const EditHookPanel = ({ visible, selectedHook, onClose }: EditHookPanelProps) =
})
if (!selectedTable) {
setIsSubmitting(false)
return ui.setNotification({ category: 'error', message: 'Unable to find selected table' })
return toast.error('Unable to find selected table')
}
const headers = httpHeaders
@@ -1,17 +1,17 @@
import Link from 'next/link'
import { useEffect, useMemo, useState } from 'react'
import { Button, Input, Listbox, SidePanel } from 'ui'
import toast from 'react-hot-toast'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { CodeEditor } from 'components/ui/CodeEditor'
import MultiSelect, { MultiSelectOption } from 'ui-patterns/MultiSelect'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useIndexesQuery } from 'data/database/indexes-query'
import { useSchemasQuery } from 'data/database/schemas-query'
import { useTableColumnsQuery } from 'data/database/table-columns-query'
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
import { useExecuteSqlMutation } from 'data/sql/execute-sql-mutation'
import { useStore } from 'hooks'
import { Button, Input, Listbox, SidePanel } from 'ui'
import MultiSelect, { MultiSelectOption } from 'ui-patterns/MultiSelect'
import { INDEX_TYPES } from './Indexes.constants'
interface CreateIndexSidePanelProps {
@@ -20,7 +20,6 @@ interface CreateIndexSidePanelProps {
}
const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) => {
const { ui } = useStore()
const { project } = useProjectContext()
const [selectedSchema, setSelectedSchema] = useState('public')
const [selectedEntity, setSelectedEntity] = useState('---')
@@ -58,14 +57,10 @@ const CreateIndexSidePanel = ({ visible, onClose }: CreateIndexSidePanelProps) =
onSuccess: async () => {
await refetchIndexes()
onClose()
ui.setNotification({ category: 'success', message: `Successfully created index` })
toast.success(`Successfully created index`)
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to create index: ${error.message}`,
})
toast.error(`Failed to create index: ${error.message}`)
},
})
@@ -1,13 +1,14 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { noop } from 'lodash'
import { useState } from 'react'
import { Button, IconLoader } from 'ui'
import toast from 'react-hot-toast'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import AutoTextArea from 'components/to-be-cleaned/forms/AutoTextArea'
import { executeSql } from 'data/sql/execute-sql-query'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import { timeout } from 'lib/helpers'
import { Button, IconLoader } from 'ui'
// Removes some auto-generated Postgrest text
// Ideally PostgREST wouldn't add this if there is already a comment
@@ -30,8 +31,6 @@ interface DescrptionProps {
}
const Description = ({ content, metadata, onChange = noop }: DescrptionProps) => {
const { ui } = useStore()
const contentText = temp_removePostgrestText(content || '').trim()
const [value, setValue] = useState(contentText)
const [isUpdating, setIsUpdating] = useState(false)
@@ -62,20 +61,11 @@ const Description = ({ content, metadata, onChange = noop }: DescrptionProps) =>
connectionString: project?.connectionString,
sql: query,
})
// [Joshen] Temp fix, immediately refreshing the docs fetches stale state
await timeout(500)
ui.setNotification({
category: 'success',
message: `Successfully updated description`,
})
toast.success(`Successfully updated description`)
} catch (error: any) {
ui.setNotification({
error: error,
category: 'error',
message: `Failed to update description: ${error.message}`,
})
toast.error(`Failed to update description: ${error.message}`)
}
}
@@ -1,11 +1,11 @@
import Link from 'next/link'
import { useState } from 'react'
import { Button, IconDownload, IconExternalLink } from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common/hooks'
import { useParams } from 'common'
import CodeSnippet from 'components/interfaces/Docs/CodeSnippet'
import { generateTypes } from 'data/projects/project-type-generation-query'
import { useStore } from 'hooks'
import { Button, IconDownload, IconExternalLink } from 'ui'
interface Props {
selectedLang: 'bash' | 'js'
@@ -13,7 +13,6 @@ interface Props {
export default function GeneratingTypes({ selectedLang }: Props) {
const { ref } = useParams()
const { ui } = useStore()
const [isGeneratingTypes, setIsGeneratingTypes] = useState(false)
const onClickGenerateTypes = async () => {
@@ -27,16 +26,9 @@ export default function GeneratingTypes({ selectedLang }: Props) {
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
ui.setNotification({
category: 'success',
message: `Successfully generated types! File is being downloaded`,
})
toast.success(`Successfully generated types! File is being downloaded`)
} catch (error: any) {
ui.setNotification({
error,
category: 'error',
message: `Failed to generate types: ${error.message}`,
})
toast.error(`Failed to generate types: ${error.message}`)
} finally {
setIsGeneratingTypes(false)
}
@@ -6,18 +6,7 @@ import dayjs from 'dayjs'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import {
Alert,
Button,
Form,
IconExternalLink,
IconMaximize2,
IconMinimize2,
IconTerminal,
Input,
Modal,
Toggle,
} from 'ui'
import toast from 'react-hot-toast'
import {
FormActions,
@@ -33,13 +22,24 @@ import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
import { useEdgeFunctionDeleteMutation } from 'data/edge-functions/edge-functions-delete-mutation'
import { useEdgeFunctionUpdateMutation } from 'data/edge-functions/edge-functions-update-mutation'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import {
Alert,
Button,
Form,
IconExternalLink,
IconMaximize2,
IconMinimize2,
IconTerminal,
Input,
Modal,
Toggle,
} from 'ui'
import CommandRender from '../CommandRender'
import { generateCLICommands } from './EdgeFunctionDetails.utils'
const EdgeFunctionDetails = () => {
const router = useRouter()
const { ui } = useStore()
const { ref: projectRef, functionSlug } = useParams()
const [showDeleteModal, setShowDeleteModal] = useState(false)
const [showInstructions, setShowInstructions] = useState(false)
@@ -50,10 +50,7 @@ const EdgeFunctionDetails = () => {
const { mutateAsync: updateEdgeFunction, isLoading: isUpdating } = useEdgeFunctionUpdateMutation()
const { mutate: deleteEdgeFunction, isLoading: isDeleting } = useEdgeFunctionDeleteMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully deleted "${selectedFunction?.name}"`,
})
toast.success(`Successfully deleted "${selectedFunction?.name}"`)
router.push(`/project/${projectRef}/functions`)
},
})
@@ -90,7 +87,7 @@ const EdgeFunctionDetails = () => {
payload: values,
})
resetForm({ values, initialValues: values })
ui.setNotification({ category: 'success', message: `Successfully updated edge function` })
toast.success(`Successfully updated edge function`)
} catch (error) {}
}
@@ -1,6 +1,7 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { useEffect } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import {
@@ -11,11 +12,10 @@ import {
import { FormActions, FormPanel, FormSection, FormSectionContent } from 'components/ui/Forms'
import { useOrganizationUpdateMutation } from 'data/organizations/organization-update-mutation'
import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { Form, Input } from 'ui'
const BillingEmail = () => {
const { ui } = useStore()
const { slug } = useParams()
const queryClient = useQueryClient()
const selectedOrganization = useSelectedOrganization()
@@ -30,10 +30,7 @@ const BillingEmail = () => {
const onUpdateOrganizationEmail = async (values: any, { resetForm }: any) => {
if (!canUpdateOrganization) {
return ui.setNotification({
category: 'error',
message: 'You do not have the required permissions to update this organization',
})
return toast.error('You do not have the required permissions to update this organization')
}
if (!slug) return console.error('Slug is required')
if (!name) return console.error('Organization name is required')
@@ -46,10 +43,7 @@ const BillingEmail = () => {
})
resetForm({ values: { billing_email }, initialValues: { billing_email } })
invalidateOrganizationsQuery(queryClient)
ui.setNotification({
category: 'success',
message: 'Successfully saved settings',
})
toast.success('Successfully saved settings')
} finally {
}
}
@@ -1,20 +1,21 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import clsx from 'clsx'
import { useParams } from 'common'
import { useTheme } from 'next-themes'
import Table from 'components/to-be-cleaned/Table'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
import { useCheckPermissions, useStore } from 'hooks'
import { BASE_PATH, PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import Telemetry from 'lib/telemetry'
import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
import { useCheckPermissions } from 'hooks'
import { BASE_PATH, PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import Telemetry from 'lib/telemetry'
import { pricing } from 'shared-data/pricing'
import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings'
import { Alert, Button, Collapsible, IconChevronRight, IconExternalLink, SidePanel } from 'ui'
import Image from 'next/image'
import { Alert, Button, Collapsible, IconChevronRight, IconExternalLink, SidePanel, cn } from 'ui'
const SPEND_CAP_OPTIONS: {
name: string
@@ -37,7 +38,6 @@ const SPEND_CAP_OPTIONS: {
]
const SpendCapSidePanel = () => {
const { ui } = useStore()
const router = useRouter()
const { slug } = useParams()
const { resolvedTheme } = useTheme()
@@ -58,19 +58,11 @@ const SpendCapSidePanel = () => {
const { mutate: updateOrgSubscription, isLoading: isUpdating } = useOrgSubscriptionUpdateMutation(
{
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully ${isTurningOnCap ? 'enabled' : 'disabled'} spend cap`,
})
toast.success(`Successfully ${isTurningOnCap ? 'enabled' : 'disabled'} spend cap`)
onClose()
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to toggle spend cap: ${error.message}`,
})
toast.error(`Failed to toggle spend cap: ${error.message}`)
},
}
)
@@ -228,7 +220,7 @@ const SpendCapSidePanel = () => {
return (
<div
key={option.value}
className={clsx('col-span-4 group space-y-1', isFreePlan && 'opacity-75')}
className={cn('col-span-4 group space-y-1', isFreePlan && 'opacity-75')}
onClick={() => {
!isFreePlan && setSelectedOption(option.value)
Telemetry.sendActivity(
@@ -248,7 +240,7 @@ const SpendCapSidePanel = () => {
>
<Image
alt="Spend Cap"
className={clsx(
className={cn(
'relative rounded-xl transition border bg-no-repeat bg-center bg-cover w-[160px] h-[96px]',
isSelected
? 'border-foreground'
@@ -262,7 +254,7 @@ const SpendCapSidePanel = () => {
/>
<p
className={clsx(
className={cn(
'text-sm transition',
!isFreePlan && 'group-hover:text-foreground',
isSelected ? 'text-foreground' : 'text-foreground-light'
@@ -1,9 +1,10 @@
import toast from 'react-hot-toast'
import { useParams } from 'common'
import type { OrganizationPaymentMethod } from 'data/organizations/organization-payment-methods-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
import type { SubscriptionTier } from 'data/subscriptions/types'
import { useStore } from 'hooks'
import { Button, Modal } from 'ui'
export interface ChangePaymentMethodModalProps {
@@ -15,25 +16,20 @@ const ChangePaymentMethodModal = ({
selectedPaymentMethod,
onClose,
}: ChangePaymentMethodModalProps) => {
const { ui } = useStore()
const { slug } = useParams()
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: slug })
const { mutate: updateOrgSubscription, isLoading: isUpdating } = useOrgSubscriptionUpdateMutation(
{
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully changed payment method to the card ending with ${
toast.success(
`Successfully changed payment method to the card ending with ${
selectedPaymentMethod!.card.last4
}`,
})
}`
)
onClose()
},
onError: (error) => {
ui.setNotification({
category: 'error',
message: `Failed to change payment method: ${error.message}`,
})
toast.error(`Failed to change payment method: ${error.message}`)
},
}
)
@@ -1,15 +1,16 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { includes, without } from 'lodash'
import { useReducer, useRef, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useSendDowngradeFeedbackMutation } from 'data/feedback/exit-survey-send'
import type { OrgSubscription } from 'data/subscriptions/types'
import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation'
import { useFlag, useStore } from 'hooks'
import type { OrgSubscription } from 'data/subscriptions/types'
import { useFlag } from 'hooks'
import { Alert, Button, Input, Modal } from 'ui'
import ProjectUpdateDisabledTooltip from '../ProjectUpdateDisabledTooltip'
import { CANCELLATION_REASONS } from '../BillingSettings.constants'
import ProjectUpdateDisabledTooltip from '../ProjectUpdateDisabledTooltip'
export interface ExitSurveyModalProps {
visible: boolean
@@ -19,7 +20,6 @@ export interface ExitSurveyModalProps {
// [Joshen] For context - Exit survey is only when going to free plan from a paid plan
const ExitSurveyModal = ({ visible, subscription, onClose }: ExitSurveyModalProps) => {
const { ui } = useStore()
const { slug } = useParams()
const captchaRef = useRef<HCaptcha>(null)
@@ -33,10 +33,7 @@ const ExitSurveyModal = ({ visible, subscription, onClose }: ExitSurveyModalProp
const { mutateAsync: updateOrgSubscription, isLoading: isUpdating } =
useOrgSubscriptionUpdateMutation({
onError: (error) => {
ui.setNotification({
category: 'error',
message: `Failed to downgrade project: ${error.message}`,
})
toast.error(`Failed to downgrade project: ${error.message}`)
},
})
const isSubmitting = isUpdating || isSubmittingFeedback
@@ -62,10 +59,7 @@ const ExitSurveyModal = ({ visible, subscription, onClose }: ExitSurveyModalProp
const onSubmit = async () => {
if (selectedReasons.length === 0) {
return ui.setNotification({
category: 'error',
message: 'Please select at least one reason for canceling your subscription',
})
return toast.error('Please select at least one reason for canceling your subscription')
}
let token = captchaToken
@@ -98,13 +92,13 @@ const ExitSurveyModal = ({ visible, subscription, onClose }: ExitSurveyModalProp
} finally {
}
ui.setNotification({
category: 'success',
duration: hasComputeInstance ? 8000 : 4000,
message: hasComputeInstance
toast.success(
hasComputeInstance
? 'Your organization has been downgraded and your projects are currently restarting to update their compute instances'
: 'Successfully downgraded organization to the free plan',
})
{ duration: hasComputeInstance ? 8000 : 4000 }
)
onClose(true)
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
}
@@ -1,10 +1,10 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import clsx from 'clsx'
import { isArray } from 'lodash'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { plans as subscriptionsPlans } from 'shared-data/plans'
import toast from 'react-hot-toast'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
@@ -12,13 +12,16 @@ import InformationBox from 'components/ui/InformationBox'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query'
import { useOrganizationBillingSubscriptionPreview } from 'data/organizations/organization-billing-subscription-preview'
import { useProjectsQuery } from 'data/projects/projects-query'
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 { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import { formatCurrency } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { plans as subscriptionsPlans } from 'shared-data/plans'
import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings'
import {
Button,
@@ -28,18 +31,15 @@ import {
IconInfo,
Modal,
SidePanel,
cn,
} from 'ui'
import DowngradeModal from './DowngradeModal'
import EnterpriseCard from './EnterpriseCard'
import ExitSurveyModal from './ExitSurveyModal'
import MembersExceedLimitModal from './MembersExceedLimitModal'
import PaymentMethodSelection from './PaymentMethodSelection'
import { formatCurrency } from 'lib/helpers'
import { useProjectsQuery } from 'data/projects/projects-query'
import { isArray } from 'lodash'
const PlanUpdateSidePanel = () => {
const { ui } = useStore()
const router = useRouter()
const selectedOrganization = useSelectedOrganization()
const slug = selectedOrganization?.slug
@@ -75,20 +75,13 @@ const PlanUpdateSidePanel = () => {
const { mutate: updateOrgSubscription, isLoading: isUpdating } = useOrgSubscriptionUpdateMutation(
{
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully updated subscription to ${subscriptionPlanMeta?.name}!`,
})
toast.success(`Successfully updated subscription to ${subscriptionPlanMeta?.name}!`)
setSelectedTier(undefined)
onClose()
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Unable to update subscription: ${error.message}`,
})
toast.error(`Unable to update subscription: ${error.message}`)
},
}
)
@@ -147,7 +140,7 @@ const PlanUpdateSidePanel = () => {
if (!slug) return console.error('org slug is required')
if (!selectedTier) return console.error('Selected plan is required')
if (!selectedPaymentMethod && !paymentViaInvoice) {
return ui.setNotification({ category: 'error', message: 'Please select a payment method' })
return toast.error('Please select a payment method')
}
// If the user is downgrading from team, should have spend cap disabled by default
@@ -210,7 +203,7 @@ const PlanUpdateSidePanel = () => {
>
<div className="w-full">
<div className="flex items-center space-x-2">
<p className={clsx('text-brand text-sm uppercase')}>{plan.name}</p>
<p className={cn('text-brand text-sm uppercase')}>{plan.name}</p>
{isCurrentPlan ? (
<div className="text-xs bg-surface-300 text-foreground-light rounded px-2 py-0.5">
Current plan
@@ -394,7 +387,7 @@ const PlanUpdateSidePanel = () => {
className="!pl-0 !pr-1"
icon={
<IconChevronRight
className={clsx(
className={cn(
'transition',
usageFeesExpanded.includes(item.description) && 'rotate-90'
)}
@@ -1,5 +1,6 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import {
@@ -16,14 +17,13 @@ import {
TaxIdValue,
useOrganizationTaxIDsUpdateMutation,
} from 'data/organizations/organization-tax-ids-update-mutation'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import { uuidv4 } from 'lib/helpers'
import { Button, Form, IconPlus, IconX, Input, Listbox } from 'ui'
import { TAX_IDS } from './TaxID.constants'
import { checkTaxIdsEqual, sanitizeTaxID } from './TaxID.utilts'
const TaxID = () => {
const { ui } = useStore()
const { slug } = useParams()
const {
data: taxIds,
@@ -52,10 +52,10 @@ const TaxID = () => {
if (errors !== undefined && errors.length > 0) {
errors.forEach((taxId: any) => {
ui.setNotification({ category: 'error', message: taxId.result.error.message })
toast.error(taxId.result.error.message)
})
} else {
ui.setNotification({ category: 'success', message: 'Successfully updated tax IDs' })
toast.success('Successfully updated tax IDs')
}
},
})
@@ -1,15 +1,14 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { Button, Form, Input, Modal } from 'ui'
import toast from 'react-hot-toast'
import { useOrganizationDeleteMutation } from 'data/organizations/organization-delete-mutation'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { Button, Form, Input, Modal } from 'ui'
const DeleteOrganizationButton = () => {
const router = useRouter()
const { ui } = useStore()
const selectedOrganization = useSelectedOrganization()
const { slug: orgSlug, name: orgName } = selectedOrganization ?? {}
@@ -32,20 +31,14 @@ const DeleteOrganizationButton = () => {
const onConfirmDelete = async (values: any) => {
if (!canDeleteOrganization) {
return ui.setNotification({
category: 'error',
message: 'You do not have the required permissions to delete this organization',
})
return toast.error('You do not have the required permissions to delete this organization')
}
if (!orgSlug) return console.error('Org slug is required')
try {
await deleteOrganization({ slug: orgSlug })
} finally {
ui.setNotification({
category: 'success',
message: `Successfully deleted ${orgName}`,
})
toast.success(`Successfully deleted ${orgName}`)
router.push('/projects')
}
}
@@ -1,11 +1,10 @@
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import clsx from 'clsx'
import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { Collapsible, Form, IconChevronRight, Input, Toggle } from 'ui'
import toast from 'react-hot-toast'
import NoProjectsOnPaidOrgInfo from 'components/interfaces/Billing/NoProjectsOnPaidOrgInfo'
import { ScaffoldContainerLegacy } from 'components/layouts/Scaffold'
@@ -18,13 +17,13 @@ import {
} from 'components/ui/Forms'
import { useOrganizationUpdateMutation } from 'data/organizations/organization-update-mutation'
import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query'
import { useCheckPermissions, useIsFeatureEnabled, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useIsFeatureEnabled, useSelectedOrganization } from 'hooks'
import { OPT_IN_TAGS } from 'lib/constants'
import { Collapsible, Form, IconChevronRight, Input, Toggle, cn } from 'ui'
import OrganizationDeletePanel from './OrganizationDeletePanel'
const GeneralSettings = () => {
const queryClient = useQueryClient()
const { ui } = useStore()
const { slug } = useParams()
const [open, setOpen] = useState(false)
const selectedOrganization = useSelectedOrganization()
@@ -42,10 +41,7 @@ const GeneralSettings = () => {
const onUpdateOrganization = async (values: any, { resetForm }: any) => {
if (!canUpdateOrganization) {
return ui.setNotification({
category: 'error',
message: 'You do not have the required permissions to update this organization',
})
return toast.error('You do not have the required permissions to update this organization')
}
if (!slug) return console.error('Slug is required')
@@ -64,7 +60,7 @@ const GeneralSettings = () => {
onSuccess: () => {
resetForm({ values, initialValues: values })
invalidateOrganizationsQuery(queryClient)
ui.setNotification({ category: 'success', message: 'Successfully saved settings' })
toast.success('Successfully saved settings')
},
}
)
@@ -135,7 +131,7 @@ const GeneralSettings = () => {
<IconChevronRight
strokeWidth={2}
size={16}
className={clsx('transition-all', open ? 'rotate-90' : '')}
className={cn('transition-all', open ? 'rotate-90' : '')}
/>
<p className="text-sm text-foreground-light underline">
Important information regarding opting in
@@ -1,29 +1,18 @@
import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js'
import type { PaymentMethod } from '@stripe/stripe-js'
import { useQueryClient } from '@tanstack/react-query'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import {
Button,
IconEdit2,
IconExternalLink,
IconHelpCircle,
IconInfo,
Input,
Listbox,
Toggle,
} from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import SpendCapModal from 'components/interfaces/Billing/SpendCapModal'
import InformationBox from 'components/ui/InformationBox'
import Panel from 'components/ui/Panel'
import { useOrganizationCreateMutation } from 'data/organizations/organization-create-mutation'
import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query'
import { useStore } from 'hooks'
import { BASE_PATH, PRICING_TIER_LABELS_ORG } from 'lib/constants'
import { getURL } from 'lib/helpers'
import { Button, IconEdit2, IconHelpCircle, Input, Listbox, Toggle } from 'ui'
const ORG_KIND_TYPES = {
PERSONAL: 'Personal',
@@ -52,12 +41,10 @@ interface NewOrgFormProps {
* No org selected yet, create a new one
*/
const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
const queryClient = useQueryClient()
const { ui } = useStore()
const router = useRouter()
const stripe = useStripe()
const elements = useElements()
const queryClient = useQueryClient()
const { plan, name, kind, size, spend_cap } = useParams()
const [orgName, setOrgName] = useState(name || '')
@@ -142,7 +129,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
const isOrgNameValid = validateOrgName(orgName)
if (!isOrgNameValid) {
return ui.setNotification({ category: 'error', message: 'Organization name is empty' })
return toast.error('Organization name is empty')
}
if (!stripe || !elements) {
@@ -163,10 +150,7 @@ const NewOrgForm = ({ onPaymentMethodReset }: NewOrgFormProps) => {
})
if (error || !setupIntent.payment_method) {
ui.setNotification({
category: 'error',
message: error?.message ?? ' Failed to save card details',
})
toast.error(error?.message ?? ' Failed to save card details')
setNewOrgLoading(false)
return
}
@@ -1,7 +1,8 @@
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useOAuthAppDeleteMutation } from 'data/oauth/oauth-app-delete-mutation'
import type { OAuthApp } from 'data/oauth/oauth-apps-query'
import { useStore } from 'hooks'
import { Alert, IconLock, Modal } from 'ui'
export interface DeleteAppModalProps {
@@ -10,14 +11,10 @@ export interface DeleteAppModalProps {
}
const DeleteAppModal = ({ selectedApp, onClose }: DeleteAppModalProps) => {
const { ui } = useStore()
const { slug } = useParams()
const { mutate: deleteOAuthApp, isLoading: isDeleting } = useOAuthAppDeleteMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully deleted the app "${selectedApp?.name}"`,
})
toast.success(`Successfully deleted the app "${selectedApp?.name}"`)
onClose()
},
})
@@ -1,7 +1,8 @@
import clsx from 'clsx'
import { ChangeEvent, useEffect, useRef, useState } from 'react'
import type { OAuthScope } from '@supabase/shared-types/out/constants'
import Link from 'next/link'
import { ChangeEvent, useEffect, useRef, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import {
OAuthAppCreateResponse,
@@ -9,7 +10,6 @@ import {
} from 'data/oauth/oauth-app-create-mutation'
import { useOAuthAppUpdateMutation } from 'data/oauth/oauth-app-update-mutation'
import type { OAuthApp } from 'data/oauth/oauth-apps-query'
import { useStore } from 'hooks'
import { isValidHttpUrl, uuidv4 } from 'lib/helpers'
import { uploadAttachment } from 'lib/upload'
import {
@@ -26,10 +26,10 @@ import {
Input,
Modal,
SidePanel,
cn,
} from 'ui'
import AuthorizeRequesterDetails from '../AuthorizeRequesterDetails'
import { ScopesPanel } from './Scopes'
import Link from 'next/link'
export interface PublishAppSidePanelProps {
visible: boolean
@@ -44,43 +44,28 @@ const PublishAppSidePanel = ({
onClose,
onCreateSuccess,
}: PublishAppSidePanelProps) => {
const { ui } = useStore()
const { slug } = useParams()
const uploadButtonRef = useRef<any>()
const { mutate: createOAuthApp } = useOAuthAppCreateMutation({
onSuccess: (res, variables) => {
ui.setNotification({
category: 'success',
message: `Successfully created OAuth app "${variables.name}"!`,
})
toast.success(`Successfully created OAuth app "${variables.name}"!`)
onClose()
onCreateSuccess(res)
setIsSubmitting(false)
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to create OAuth application: ${error.message}`,
})
toast.error(`Failed to create OAuth application: ${error.message}`)
setIsSubmitting(false)
},
})
const { mutate: updateOAuthApp } = useOAuthAppUpdateMutation({
onSuccess: (res, variables) => {
ui.setNotification({
category: 'success',
message: `Successfully updated OAuth app "${variables.name}"!`,
})
toast.success(`Successfully updated OAuth app "${variables.name}"!`)
onClose()
setIsSubmitting(false)
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to update OAuth application: ${error.message}`,
})
toast.error(`Failed to update OAuth application: ${error.message}`)
setIsSubmitting(false)
},
})
@@ -239,7 +224,7 @@ const PublishAppSidePanel = ({
<div>
{iconUrl !== undefined ? (
<div
className={clsx(
className={cn(
'shadow transition group relative',
'bg-center bg-cover bg-no-repeat',
'mt-4 mr-4 space-y-2 rounded-full h-[120px] w-[120px] flex flex-col items-center justify-center'
@@ -280,7 +265,7 @@ const PublishAppSidePanel = ({
</div>
) : (
<div
className={clsx(
className={cn(
'border border-strong transition opacity-75 hover:opacity-100',
'mt-4 mr-4 space-y-2 rounded-full h-[120px] w-[120px] flex flex-col items-center justify-center cursor-pointer'
)}
@@ -1,15 +1,16 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { isNil } from 'lodash'
import { useEffect, useState } from 'react'
import { Button, Form, IconMail, Input, Listbox, Modal } from 'ui'
import toast from 'react-hot-toast'
import { object, string } from 'yup'
import { useParams } from 'common'
import { useOrganizationMemberInviteCreateMutation } from 'data/organizations/organization-member-invite-create-mutation'
import type { OrganizationMember } from 'data/organizations/organization-members-query'
import { doPermissionsCheck, useGetPermissions, useStore } from 'hooks'
import { doPermissionsCheck, useGetPermissions } from 'hooks'
import type { Role } from 'types'
import { Button, Form, IconMail, Input, Listbox, Modal } from 'ui'
export interface InviteMemberButtonProps {
orgId: number
@@ -26,7 +27,6 @@ const InviteMemberButton = ({
roles = [],
rolesAddable = [],
}: InviteMemberButtonProps) => {
const { ui } = useStore()
const { slug } = useParams()
const [isOpen, setIsOpen] = useState(false)
const { permissions: allPermissions } = useGetPermissions()
@@ -60,15 +60,9 @@ const InviteMemberButton = ({
)
if (existingMember !== undefined) {
if (existingMember.invited_id) {
return ui.setNotification({
category: 'info',
message: 'User has already been invited to this organization',
})
return toast('User has already been invited to this organization')
} else {
return ui.setNotification({
category: 'info',
message: 'User is already in this organization',
})
return toast('User is already in this organization')
}
}
@@ -82,9 +76,9 @@ const InviteMemberButton = ({
roleId,
})
if (isNil(response)) {
ui.setNotification({ category: 'error', message: 'Failed to add member' })
toast.error('Failed to add member')
} else {
ui.setNotification({ category: 'success', message: 'Successfully added new member.' })
toast.success('Successfully added new member')
setIsOpen(!isOpen)
resetForm({ initialValues: { ...initialValues, role: roleId } })
}
@@ -1,8 +1,17 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useOrganizationMemberDeleteMutation } from 'data/organizations/organization-member-delete-mutation'
import { useOrganizationMemberInviteCreateMutation } from 'data/organizations/organization-member-invite-create-mutation'
import { useOrganizationMemberInviteDeleteMutation } from 'data/organizations/organization-member-invite-delete-mutation'
import type { OrganizationMember } from 'data/organizations/organization-members-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useCheckPermissions, useIsFeatureEnabled, useSelectedOrganization } from 'hooks'
import type { Role } from 'types'
import {
Button,
DropdownMenu,
@@ -14,16 +23,7 @@ import {
IconTrash,
Modal,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useOrganizationMemberDeleteMutation } from 'data/organizations/organization-member-delete-mutation'
import { useOrganizationMemberInviteCreateMutation } from 'data/organizations/organization-member-invite-create-mutation'
import { useOrganizationMemberInviteDeleteMutation } from 'data/organizations/organization-member-invite-delete-mutation'
import type { OrganizationMember } from 'data/organizations/organization-members-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useCheckPermissions, useIsFeatureEnabled, useSelectedOrganization, useStore } from 'hooks'
import type { Role } from 'types'
import { isInviteExpired } from '../Organization.utils'
import { useGetRolesManagementPermissions } from './TeamSettings.utils'
interface MemberActionsProps {
@@ -32,9 +32,7 @@ interface MemberActionsProps {
}
const MemberActions = ({ member, roles }: MemberActionsProps) => {
const { ui } = useStore()
const { slug } = useParams()
const organizationMembersDeletionEnabled = useIsFeatureEnabled('organization_members:delete')
const selectedOrganization = useSelectedOrganization()
@@ -61,10 +59,7 @@ const MemberActions = ({ member, roles }: MemberActionsProps) => {
const { mutate: deleteOrganizationMember, isLoading: isDeletingMember } =
useOrganizationMemberDeleteMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Successfully removed ${member.primary_email}`,
})
toast.success(`Successfully removed ${member.primary_email}`)
setIsDeleteModalOpen(false)
},
})
@@ -72,13 +67,10 @@ const MemberActions = ({ member, roles }: MemberActionsProps) => {
const { mutate: createOrganizationMemberInvite, isLoading: isCreatingInvite } =
useOrganizationMemberInviteCreateMutation({
onSuccess: () => {
ui.setNotification({ category: 'success', message: 'Resent the invitation.' })
toast.success('Resent the invitation.')
},
onError: (error) => {
ui.setNotification({
category: 'error',
message: `Failed to resend invitation: ${error.message}`,
})
toast.error(`Failed to resend invitation: ${error.message}`)
},
})
@@ -113,10 +105,7 @@ const MemberActions = ({ member, roles }: MemberActionsProps) => {
if (!invitedId) return console.error('Member invited ID is required')
await asyncDeleteMemberInvite({ slug, invitedId })
ui.setNotification({
category: 'success',
message: 'Successfully revoked the invitation.',
})
toast.success('Successfully revoked the invitation.')
}
if (!canRemoveMember || (isPendingInviteAcceptance && !canResendInvite && !canRevokeInvite)) {
@@ -1,8 +1,22 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import Image from 'next/legacy/image'
import { Fragment, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useOrganizationMemberUpdateMutation } from 'data/organizations/organization-member-update-mutation'
import {
OrganizationMember,
useOrganizationMembersQuery,
} from 'data/organizations/organization-members-query'
import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useSelectedOrganization } from 'hooks'
import { useProfile } from 'lib/profile'
import {
Badge,
Button,
@@ -15,19 +29,6 @@ import {
Loading,
Modal,
} from 'ui'
import Table from 'components/to-be-cleaned/Table'
import AlertError from 'components/ui/AlertError'
import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader'
import { useOrganizationMemberUpdateMutation } from 'data/organizations/organization-member-update-mutation'
import {
OrganizationMember,
useOrganizationMembersQuery,
} from 'data/organizations/organization-members-query'
import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useSelectedOrganization, useStore } from 'hooks'
import { useProfile } from 'lib/profile'
import { getUserDisplayName, isInviteExpired } from '../Organization.utils'
import MemberActions from './MemberActions'
import RolesHelperModal from './RolesHelperModal/RolesHelperModal'
@@ -43,7 +44,6 @@ export interface MembersViewProps {
}
const MembersView = ({ searchString }: MembersViewProps) => {
const { ui } = useStore()
const { slug } = useParams()
const selectedOrganization = useSelectedOrganization()
@@ -65,18 +65,12 @@ const MembersView = ({ searchString }: MembersViewProps) => {
const { mutate: updateOrganizationMember, isLoading } = useOrganizationMemberUpdateMutation({
onSuccess() {
setUserRoleChangeModalVisible(false)
ui.setNotification({
category: 'success',
message: `Successfully updated role for ${getUserDisplayName(selectedMember)}`,
})
toast.success(`Successfully updated role for ${getUserDisplayName(selectedMember)}`)
},
onError(error) {
ui.setNotification({
category: 'error',
message: `Failed to update role for ${getUserDisplayName(selectedMember)}: ${
error.message
}`,
})
toast.error(
`Failed to update role for ${getUserDisplayName(selectedMember)}: ${error.message}`
)
},
})
@@ -173,13 +167,11 @@ const MembersView = ({ searchString }: MembersViewProps) => {
const canAddRole = rolesAddable.includes(selectedRole?.id ?? -1)
if (!canAddRole) {
return ui.setNotification({
category: 'error',
duration: 4000,
message: `You do not have permission to update this team member to ${
return toast.error(
`You do not have permission to update this team member to ${
selectedRole!.name
}`,
})
}`
)
}
setUserRoleChangeModalVisible(true)
@@ -1,8 +1,8 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { useParams } from 'common'
import { useState } from 'react'
import { Button, IconSearch, Input, Modal } from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import {
ScaffoldActionsContainer,
ScaffoldActionsGroup,
@@ -10,19 +10,19 @@ import {
ScaffoldFilterAndContent,
ScaffoldSectionContent,
} from 'components/layouts/Scaffold'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useOrganizationMemberDeleteMutation } from 'data/organizations/organization-member-delete-mutation'
import { useOrganizationMembersQuery } from 'data/organizations/organization-members-query'
import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
import { useIsFeatureEnabled, useSelectedOrganization, useStore } from 'hooks'
import { useIsFeatureEnabled, useSelectedOrganization } from 'hooks'
import { useProfile } from 'lib/profile'
import { Button, IconSearch, Input, Modal } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import InviteMemberButton from './InviteMemberButton'
import MembersView from './MembersView'
import { hasMultipleOwners, useGetRolesManagementPermissions } from './TeamSettings.utils'
const TeamSettings = () => {
const { ui } = useStore()
const { slug } = useParams()
const {
@@ -64,10 +64,7 @@ const TeamSettings = () => {
setIsLeaveTeamModalOpen(false)
window?.location.replace('/') // Force reload to clear Store
} catch (error: any) {
ui.setNotification({
category: 'error',
message: `Failed to leave organization: ${error?.message}`,
})
toast.error(`Failed to leave organization: ${error?.message}`)
} finally {
setIsLeaving(false)
}
@@ -1,18 +1,16 @@
import { useParams } from 'common'
import Link from 'next/link'
import { useState } from 'react'
import { Button, IconDownload, IconExternalLink } from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { generateTypes } from 'data/projects/project-type-generation-query'
import { useStore } from 'hooks'
import { Button, IconDownload, IconExternalLink } from 'ui'
import ContentSnippet from '../ContentSnippet'
import { DOCS_CONTENT } from '../ProjectAPIDocs.constants'
import type { ContentProps } from './Content.types'
const Entities = ({ language }: ContentProps) => {
const { ui } = useStore()
const { ref } = useParams()
const [isGeneratingTypes, setIsGeneratingTypes] = useState(false)
const onClickGenerateTypes = async () => {
@@ -26,16 +24,9 @@ const Entities = ({ language }: ContentProps) => {
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
ui.setNotification({
category: 'success',
message: `Successfully generated types! File is being downloaded`,
})
toast.success(`Successfully generated types! File is being downloaded`)
} catch (error: any) {
ui.setNotification({
error,
category: 'error',
message: `Failed to generate types: ${error.message}`,
})
toast.error(`Failed to generate types: ${error.message}`)
} finally {
setIsGeneratingTypes(false)
}
@@ -1,14 +1,15 @@
import { useParams } from 'common'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useSqlTitleGenerateMutation } from 'data/ai/sql-title-mutation'
import type { SqlSnippet } from 'data/content/sql-snippets-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { isError } from 'data/utils/error-check'
import { useSelectedOrganization, useStore } from 'hooks'
import { useEffect, useState } from 'react'
import { useSelectedOrganization } from 'hooks'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import { AiIconAnimation, Button, Form, Input, Modal } from 'ui'
import { subscriptionHasHipaaAddon } from '../Billing/Subscription/Subscription.utils'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
export interface RenameQueryModalProps {
snippet: SqlSnippet
@@ -18,7 +19,6 @@ export interface RenameQueryModalProps {
}
const RenameQueryModal = ({ snippet, visible, onCancel, onComplete }: RenameQueryModalProps) => {
const { ui } = useStore()
const { ref } = useParams()
const organization = useSelectedOrganization()
const snap = useSqlEditorStateSnapshot()
@@ -52,11 +52,7 @@ const RenameQueryModal = ({ snippet, visible, onCancel, onComplete }: RenameQuer
if (onComplete) onComplete()
return Promise.resolve()
} catch (error: any) {
ui.setNotification({
error,
category: 'error',
message: `Failed to rename query: ${error.message}`,
})
toast.error(`Failed to rename query: ${error.message}`)
}
}
@@ -105,10 +101,7 @@ const RenameQueryModal = ({ snippet, visible, onCancel, onComplete }: RenameQuer
}
} catch (error: unknown) {
if (isError(error)) {
ui.setNotification({
category: 'error',
message: `Failed to rename query: ${error.message}`,
})
toast.error(`Failed to rename query: ${error.message}`)
}
}
}}
@@ -1,12 +1,15 @@
import { useTelemetryProps } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useStore } from 'hooks'
import { copyToClipboard } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { compact, isObject, isString, map } from 'lodash'
// @ts-ignore
import MarkdownTable from 'markdown-table'
import { useRouter } from 'next/router'
import { useMemo, useRef } from 'react'
import { CSVLink } from 'react-csv'
import toast from 'react-hot-toast'
import { useTelemetryProps } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { copyToClipboard } from 'lib/helpers'
import Telemetry from 'lib/telemetry'
import { useSqlEditorStateSnapshot } from 'state/sql-editor'
import {
Button,
@@ -18,20 +21,16 @@ import {
IconClipboard,
IconDownload,
} from 'ui'
// @ts-ignore
import MarkdownTable from 'markdown-table'
export type ResultsDropdownProps = {
id: string
isExecuting?: boolean
}
const ResultsDropdown = ({ id, isExecuting }: ResultsDropdownProps) => {
const ResultsDropdown = ({ id }: ResultsDropdownProps) => {
const { project } = useProjectContext()
const snap = useSqlEditorStateSnapshot()
const telemetryProps = useTelemetryProps()
const result = snap.results?.[id]?.[0] ?? undefined
const { ui } = useStore()
const csvRef = useRef<CSVLink & HTMLAnchorElement & { link: HTMLAnchorElement }>(null)
const router = useRouter()
@@ -94,7 +93,7 @@ const ResultsDropdown = ({ id, isExecuting }: ResultsDropdownProps) => {
const markdownData = MarkdownTable(table)
copyToClipboard(markdownData, () => {
ui.setNotification({ category: 'success', message: 'Copied results to clipboard' })
toast.success('Copied results to clipboard')
Telemetry.sendEvent(
{ category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' },
telemetryProps,
@@ -111,7 +110,7 @@ const ResultsDropdown = ({ id, isExecuting }: ResultsDropdownProps) => {
if (result.rows.length == 0) return 'results is empty'
copyToClipboard(JSON.stringify(result.rows, null, 2), () => {
ui.setNotification({ category: 'success', message: 'Copied results to clipboard' })
toast.success('Copied results to clipboard')
Telemetry.sendEvent(
{ category: 'sql_editor', action: 'sql_copy_as_json', label: '' },
telemetryProps,
@@ -54,7 +54,7 @@ const UtilityPanel = ({
)}
</div>
<div className="flex gap-1 h-full">
{result && result.rows && <ResultsDropdown id={id} isExecuting={isExecuting} />}
{result && result.rows && <ResultsDropdown id={id} />}
<UtilityActions
id={id}
isExecuting={isExecuting}
@@ -6,6 +6,15 @@ import {
JwtSecretUpdateStatus,
} from '@supabase/shared-types/out/events'
import { Dispatch, SetStateAction, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import Panel from 'components/ui/Panel'
import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutation'
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useCheckPermissions } from 'hooks'
import { uuidv4 } from 'lib/helpers'
import {
Alert,
AlertDescription_Shadcn_,
@@ -29,22 +38,13 @@ import {
Input,
Modal,
} from 'ui'
import { useParams } from 'common/hooks'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import Panel from 'components/ui/Panel'
import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutation'
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useCheckPermissions, useStore } from 'hooks'
import { uuidv4 } from 'lib/helpers'
import {
JWT_SECRET_UPDATE_ERROR_MESSAGES,
JWT_SECRET_UPDATE_PROGRESS_MESSAGES,
} from './API.constants'
const JWTSettings = () => {
const { ui } = useStore()
const { ref: projectRef } = useParams()
const [customToken, setCustomToken] = useState<string>('')
@@ -83,11 +83,9 @@ const JWTSettings = () => {
try {
await updateJwt({ projectRef, jwtSecret: jwt_secret, changeTrackingId: trackingId })
setModalVisibility(false)
ui.setNotification({
category: 'info',
message:
'Successfully submitted JWT secret update request. Please wait while your project is updated.',
})
toast(
'Successfully submitted JWT secret update request. Please wait while your project is updated.'
)
} catch (error) {}
}
@@ -2,8 +2,9 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { indexOf } from 'lodash'
import { observer } from 'mobx-react-lite'
import { useEffect } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common/hooks'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import {
FormActions,
@@ -12,17 +13,15 @@ import {
FormSectionContent,
FormSectionLabel,
} from 'components/ui/Forms'
import MultiSelect from 'ui-patterns/MultiSelect'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
import { useSchemasQuery } from 'data/database/schemas-query'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import { Form, IconAlertCircle, Input, InputNumber } from 'ui'
import MultiSelect from 'ui-patterns/MultiSelect'
const PostgrestConfig = () => {
const { ref: projectRef } = useParams()
const { ui } = useStore()
const { project } = useProjectContext()
const { data: schemas } = useSchemasQuery({
projectRef: project?.ref,
@@ -33,7 +32,7 @@ const PostgrestConfig = () => {
const { mutate: updatePostgrestConfig, isLoading: isUpdating } =
useProjectPostgrestConfigUpdateMutation({
onSuccess: () => {
ui.setNotification({ category: 'success', message: 'Successfully saved settings' })
toast.success('Successfully saved settings')
},
})
const canUpdatePostgrestConfig = useCheckPermissions(
@@ -1,9 +1,9 @@
import { JwtSecretUpdateError, JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import { useQueryClient } from '@tanstack/react-query'
import { useParams } from 'common'
import { useEffect, useRef } from 'react'
import { Badge, IconAlertCircle, Input } from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import DatabaseSelector from 'components/ui/DatabaseSelector'
import Panel from 'components/ui/Panel'
@@ -15,15 +15,14 @@ import { useProjectApiQuery } from 'data/config/project-api-query'
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import { useLoadBalancersQuery } from 'data/read-replicas/load-balancers-query'
import { useReadReplicasQuery } from 'data/read-replicas/replicas-query'
import { useStore } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import { Badge, IconAlertCircle, Input } from 'ui'
import { JWT_SECRET_UPDATE_ERROR_MESSAGES } from './API.constants'
import JWTSettings from './JWTSettings'
import PostgrestConfig from './PostgrestConfig'
const ServiceList = () => {
const { ui } = useStore()
const client = useQueryClient()
const { project, isLoading } = useProjectContext()
const { ref: projectRef, source } = useParams()
@@ -74,14 +73,10 @@ const ServiceList = () => {
client.invalidateQueries(configKeys.api(projectRef))
client.invalidateQueries(configKeys.settings(projectRef))
client.invalidateQueries(configKeys.postgrest(projectRef))
ui.setNotification({ category: 'success', message: 'Successfully updated JWT secret' })
toast.success('Successfully updated JWT secret')
break
case Failed:
ui.setNotification({
category: 'error',
message: `JWT secret update failed: ${jwtSecretUpdateErrorMessage}`,
})
toast.error(`JWT secret update failed: ${jwtSecretUpdateErrorMessage}`)
break
}
}
@@ -1,24 +1,24 @@
import Link from 'next/link'
import { useState, useEffect } from 'react'
import { useParams } from 'common/hooks'
import { useStore } from 'hooks'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useParams } from 'common'
import { FormHeader, FormPanel } from 'components/ui/Forms'
import { useBannedIPsDeleteMutation } from 'data/banned-ips/banned-ips-delete-mutations'
import { useBannedIPsQuery } from 'data/banned-ips/banned-ips-query'
import { BASE_PATH } from 'lib/constants'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
Alert_Shadcn_,
Badge,
Button,
IconAlertTriangle,
IconExternalLink,
IconGlobe,
Modal,
Badge,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useBannedIPsQuery } from 'data/banned-ips/banned-ips-query'
import { useBannedIPsDeleteMutation } from 'data/banned-ips/banned-ips-delete-mutations'
import { BASE_PATH } from 'lib/constants'
const BannedIPs = () => {
const { ref } = useParams()
@@ -27,24 +27,17 @@ const BannedIPs = () => {
projectRef: ref,
})
const { ui } = useStore()
const [showUnban, setShowUnban] = useState(false)
const [confirmingIP, setConfirmingIP] = useState<string | null>(null) // Track the IP being confirmed for unban
const { mutate: unbanIPs, isLoading: isUnbanning } = useBannedIPsDeleteMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: 'IP address successfully unbanned',
})
toast.success('IP address successfully unbanned')
setSelectedIPToUnban(null) // Reset the selected IP for unban
setShowUnban(false)
},
onError: (error) => {
ui.setNotification({
category: 'error',
message: `Failed to unban IP: ${error?.message}`,
})
toast.error(`Failed to unban IP: ${error?.message}`)
},
})
@@ -3,23 +3,23 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import dayjs from 'dayjs'
import Link from 'next/link'
import { useState } from 'react'
import { Alert, Button, Form, InputNumber, Modal } from 'ui'
import toast from 'react-hot-toast'
import { number, object } from 'yup'
import { useParams } from 'common/hooks'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { FormHeader } from 'components/ui/Forms'
import Panel from 'components/ui/Panel'
import { useProjectDiskResizeMutation } from 'data/config/project-disk-resize-mutation'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { Alert, Button, Form, InputNumber, Modal } from 'ui'
export interface DiskSizeConfigurationProps {
disabled?: boolean
}
const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfigurationProps) => {
const { ui } = useStore()
const { project } = useProjectContext()
const organization = useSelectedOrganization()
const { ref: projectRef } = useParams()
@@ -46,10 +46,7 @@ const DiskSizeConfiguration = ({ disabled = false }: DiskSizeConfigurationProps)
const { mutate: updateProjectUsage, isLoading: isUpdatingDiskSize } =
useProjectDiskResizeMutation({
onSuccess: (res, variables) => {
ui.setNotification({
category: 'success',
message: `Successfully updated disk size to ${variables.volumeSize} GB`,
})
toast.success(`Successfully updated disk size to ${variables.volumeSize} GB`)
setShowResetDbPass(false)
},
})
@@ -1,10 +1,10 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { Alert, Button, IconDownload, IconExternalLink, IconLoader, Toggle } from 'ui'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import {
FormHeader,
@@ -16,10 +16,10 @@ import {
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
import { useSSLEnforcementQuery } from 'data/ssl-enforcement/ssl-enforcement-query'
import { useSSLEnforcementUpdateMutation } from 'data/ssl-enforcement/ssl-enforcement-update-mutation'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import { Alert, Button, IconDownload, IconExternalLink, IconLoader, Toggle } from 'ui'
const SSLConfiguration = () => {
const { ui } = useStore()
const { ref } = useParams()
const [isEnforced, setIsEnforced] = useState(false)
@@ -34,18 +34,11 @@ const SSLConfiguration = () => {
const { mutate: updateSSLEnforcement, isLoading: isSubmitting } = useSSLEnforcementUpdateMutation(
{
onSuccess: () => {
ui.setNotification({
category: 'success',
message: 'Successfully updated SSL configuration',
})
toast.success('Successfully updated SSL configuration')
},
onError: (error) => {
setIsEnforced(initialIsEnforced)
ui.setNotification({
error,
category: 'error',
message: `Failed to update SSL enforcement: ${error.message}`,
})
toast.error(`Failed to update SSL enforcement: ${error.message}`)
},
}
)
@@ -3,21 +3,21 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { useRouter } from 'next/router'
import { useState } from 'react'
import { Button, IconPause, Modal } from 'ui'
import toast from 'react-hot-toast'
import {
useIsProjectActive,
useProjectContext,
} from 'components/layouts/ProjectLayout/ProjectContext'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { useProjectPauseMutation } from 'data/projects/project-pause-mutation'
import { setProjectStatus } from 'data/projects/projects-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
import { Button, IconPause, Modal } from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
const PauseProjectButton = () => {
const { ui } = useStore()
const router = useRouter()
const queryClient = useQueryClient()
const { project } = useProjectContext()
@@ -38,17 +38,14 @@ const PauseProjectButton = () => {
const { mutate: pauseProject, isLoading: isPausing } = useProjectPauseMutation({
onSuccess: (res, variables) => {
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.PAUSING)
ui.setNotification({ category: 'success', message: 'Pausing project...' })
toast.success('Pausing project...')
router.push(`/project/${projectRef}`)
},
})
const requestPauseProject = () => {
if (!canPauseProject) {
return ui.setNotification({
category: 'error',
message: 'You do not have the required permissions to pause this project',
})
return toast.error('You do not have the required permissions to pause this project')
}
pauseProject({ ref: projectRef })
}
@@ -2,6 +2,13 @@ import { useQueryClient } from '@tanstack/react-query'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { useProjectUpgradeEligibilityQuery } from 'data/config/project-upgrade-eligibility-query'
import { useProjectUpgradeMutation } from 'data/projects/project-upgrade-mutation'
import { setProjectStatus } from 'data/projects/projects-query'
import { PROJECT_STATUS } from 'lib/constants'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -14,16 +21,8 @@ import {
Modal,
} from 'ui'
import { useParams } from 'common/hooks'
import { useProjectUpgradeEligibilityQuery } from 'data/config/project-upgrade-eligibility-query'
import { useProjectUpgradeMutation } from 'data/projects/project-upgrade-mutation'
import { setProjectStatus } from 'data/projects/projects-query'
import { useStore } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
const ProjectUpgradeAlert = () => {
const router = useRouter()
const { ui } = useStore()
const { ref } = useParams()
const queryClient = useQueryClient()
const [showUpgradeModal, setShowUpgradeModal] = useState(false)
@@ -40,13 +39,13 @@ const ProjectUpgradeAlert = () => {
const { mutate: upgradeProject, isLoading: isUpgrading } = useProjectUpgradeMutation({
onSuccess: (res, variables) => {
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.UPGRADING)
ui.setNotification({ category: 'success', message: 'Upgrading project' })
toast.success('Upgrading project')
router.push(`/project/${variables.ref}?upgradeInitiated=true`)
},
})
const onConfirmUpgrade = async (values: any) => {
if (!ref) return ui.setNotification({ category: 'error', message: 'Project ref not found' })
if (!ref) return toast.error('Project ref not found')
upgradeProject({ ref, target_version: values.version })
}
@@ -1,10 +1,10 @@
import { useState } from 'react'
import { Modal } from 'ui'
import toast from 'react-hot-toast'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useVaultSecretDeleteMutation } from 'data/vault/vault-secret-delete-mutation'
import { useStore } from 'hooks'
import type { VaultSecret } from 'types'
import { Modal } from 'ui'
interface DeleteSecretModalProps {
selectedSecret: VaultSecret | undefined
@@ -12,7 +12,6 @@ interface DeleteSecretModalProps {
}
const DeleteSecretModal = ({ selectedSecret, onClose }: DeleteSecretModalProps) => {
const { ui } = useStore()
const { project } = useProjectContext()
const [isDeleting, setIsDeleting] = useState(false)
@@ -32,16 +31,9 @@ const DeleteSecretModal = ({ selectedSecret, onClose }: DeleteSecretModalProps)
id: selectedSecret.id,
})
if (res.error) {
ui.setNotification({
error: res.error,
category: 'error',
message: `Failed to delete secret: ${res.error.message}`,
})
toast.error(`Failed to delete secret: ${res.error.message}`)
} else {
ui.setNotification({
category: 'success',
message: `Successfully deleted secret ${selectedSecret.name}`,
})
toast.success(`Successfully deleted secret ${selectedSecret.name}`)
onClose()
}
setIsDeleting(false)
@@ -1,23 +1,22 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useParams } from 'common'
import { observer } from 'mobx-react-lite'
import { useTheme } from 'next-themes'
import Link from 'next/link'
import { useState } from 'react'
import toast from 'react-hot-toast'
import { Button, IconExternalLink } from 'ui'
import { useParams } from 'common'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { useDatabaseExtensionEnableMutation } from 'data/database-extensions/database-extension-enable-mutation'
import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query'
import { executeSql } from 'data/sql/execute-sql-query'
import { useCheckPermissions, useStore } from 'hooks'
import { useCheckPermissions } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import { Button, IconExternalLink } from 'ui'
const VaultToggle = () => {
const { ref } = useParams()
const { ui } = useStore()
const { resolvedTheme } = useTheme()
const { project } = useProjectContext()
const [isEnabling, setIsEnabling] = useState(false)
@@ -54,11 +53,7 @@ const VaultToggle = () => {
})
} catch (createSchemaError: any) {
setIsEnabling(false)
return ui.setNotification({
error: createSchemaError,
category: 'error',
message: `Failed to create schema: ${createSchemaError.message}`,
})
return toast.error(`Failed to create schema: ${createSchemaError.message}`)
}
enableExtension({
@@ -1,40 +1,33 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { useRouter } from 'next/router'
import { useRef, useState } from 'react'
import { Button, Form, Input } from 'ui'
import toast from 'react-hot-toast'
import { object, string } from 'yup'
import { useResetPasswordMutation } from 'data/misc/reset-password-mutation'
import { useStore } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import { Button, Form, Input } from 'ui'
const forgotPasswordSchema = object({
email: string().email('Must be a valid email').required('Email is required'),
})
const ForgotPasswordForm = () => {
const { ui } = useStore()
const router = useRouter()
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const captchaRef = useRef<HCaptcha>(null)
const [captchaToken, setCaptchaToken] = useState<string | null>(null)
const { mutate: resetPassword, isLoading } = useResetPasswordMutation({
onSuccess: async () => {
ui.setNotification({
category: 'success',
message: `If you registered using your email and password, you will receive a password reset email.`,
})
toast.success(
`If you registered using your email and password, you will receive a password reset email.`
)
await router.push('/sign-in')
},
onError: (error) => {
setCaptchaToken(null)
captchaRef.current?.resetCaptcha()
ui.setNotification({
category: 'error',
message: `Failed to send reset email: ${error.message}`,
})
toast.error(`Failed to send reset email: ${error.message}`)
},
})
@@ -1,7 +1,9 @@
import { useRouter } from 'next/router'
import toast from 'react-hot-toast'
import { useStore } from 'hooks'
import { auth } from 'lib/gotrue'
import { passwordSchema } from 'lib/schemas'
import { useRouter } from 'next/router'
import { Button, Form, Input } from 'ui'
const ResetPasswordForm = () => {
@@ -17,22 +19,13 @@ const ResetPasswordForm = () => {
const { error } = await auth.updateUser({ password })
if (!error) {
ui.setNotification({
id: toastId,
category: 'success',
message: `Password saved successfully!`,
})
toast.success('Password saved successfully!', { id: toastId })
// logout all other sessions after changing password
await auth.signOut({ scope: 'others' })
await router.push('/projects')
} else {
ui.setNotification({
id: toastId,
category: 'error',
message: error.message,
})
toast.error(error.message, { id: toastId })
}
}
@@ -4,7 +4,7 @@ import { useQueryClient } from '@tanstack/react-query'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { Button, Form, IconLock, Input } from 'ui'
import toast from 'react-hot-toast'
import { object, string } from 'yup'
import { useTelemetryProps } from 'common'
@@ -16,6 +16,7 @@ import { useStore } from 'hooks'
import { useSignOut } from 'lib/auth'
import { getReturnToPath } from 'lib/gotrue'
import Telemetry from 'lib/telemetry'
import { Button, Form, IconLock, Input } from 'ui'
const signInSchema = object({
code: string().required('MFA Code is required'),
@@ -69,27 +70,17 @@ const SignInMfaForm = () => {
{ factorId: selectedFactor.id, code, refreshFactors: false },
{
onSuccess: async () => {
ui.setNotification({
id: toastId,
category: 'success',
message: `Signed in successfully!`,
})
toast.success('Signed in successfully!', { id: toastId })
Telemetry.sendEvent(
{ category: 'account', action: 'sign_in', label: '' },
telemetryProps,
router
)
await queryClient.resetQueries()
router.push(getReturnToPath())
},
onError: (error) => {
ui.setNotification({
id: toastId,
category: 'error',
message: (error as AuthError).message,
})
toast.error((error as AuthError).message, { id: toastId })
},
}
)
@@ -4,6 +4,7 @@ import { useStore } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import { auth, buildPathWithParams } from 'lib/gotrue'
import { useRef, useState } from 'react'
import toast from 'react-hot-toast'
import { Button, Form, Input } from 'ui'
import { object, string } from 'yup'
@@ -57,12 +58,7 @@ const SignInSSOForm = () => {
} else {
setCaptchaToken(null)
captchaRef.current?.resetCaptcha()
ui.setNotification({
id: toastId,
category: 'error',
message: error.message,
})
toast.error(error.message, { id: toastId })
}
}
@@ -1,12 +1,12 @@
import HCaptcha from '@hcaptcha/react-hcaptcha'
import { useRef, useState } from 'react'
import { Alert, Button, Form, IconEye, IconEyeOff, Input } from 'ui'
import toast from 'react-hot-toast'
import * as yup from 'yup'
import { useSignUpMutation } from 'data/misc/signup-mutation'
import { useStore } from 'hooks'
import { BASE_PATH } from 'lib/constants'
import { passwordSchema } from 'lib/schemas'
import { Alert, Button, Form, IconEye, IconEyeOff, Input } from 'ui'
import PasswordConditionsHelper from './PasswordConditionsHelper'
const signUpSchema = passwordSchema.shape({
@@ -14,7 +14,6 @@ const signUpSchema = passwordSchema.shape({
})
const SignUpForm = () => {
const { ui } = useStore()
const captchaRef = useRef<HCaptcha>(null)
const [showConditions, setShowConditions] = useState(false)
const [isSubmitted, setIsSubmitted] = useState(false)
@@ -23,19 +22,13 @@ const SignUpForm = () => {
const { mutate: signup, isLoading: isSigningUp } = useSignUpMutation({
onSuccess: () => {
ui.setNotification({
category: 'success',
message: `Signed up successfully!`,
})
toast.success(`Signed up successfully!`)
setIsSubmitted(true)
},
onError: (error) => {
setCaptchaToken(null)
captchaRef.current?.resetCaptcha()
ui.setNotification({
category: 'error',
message: `Failed to sign up: ${error.message}`,
})
toast.error(`Failed to sign up: ${error.message}`)
},
})
@@ -1,7 +1,17 @@
import clsx from 'clsx'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants'
import {
convertFromBytes,
convertToBytes,
} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { useBucketCreateMutation } from 'data/storage/bucket-create-mutation'
import { IS_PLATFORM } from 'lib/constants'
import {
Alert,
Button,
@@ -12,35 +22,21 @@ import {
Listbox,
Modal,
Toggle,
cn,
} from 'ui'
import { useParams } from 'common'
import { StorageSizeUnits } from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.constants'
import {
convertFromBytes,
convertToBytes,
} from 'components/to-be-cleaned/Storage/StorageSettings/StorageSettings.utils'
import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query'
import { useBucketCreateMutation } from 'data/storage/bucket-create-mutation'
import { useStore } from 'hooks'
import { IS_PLATFORM } from 'lib/constants'
export interface CreateBucketModalProps {
visible: boolean
onClose: () => void
}
const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => {
const { ui } = useStore()
const { ref } = useParams()
const router = useRouter()
const { mutate: createBucket, isLoading: isCreating } = useBucketCreateMutation({
onSuccess: (res) => {
ui.setNotification({
category: 'success',
message: `Successfully created bucket ${res.name}`,
})
toast.success(`Successfully created bucket ${res.name}`)
router.push(`/project/${ref}/storage/buckets/${res.name}`)
onClose()
},
@@ -163,7 +159,7 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => {
<IconChevronDown
size={18}
strokeWidth={2}
className={clsx('text-foreground-light', showConfiguration && 'rotate-180')}
className={cn('text-foreground-light', showConfiguration && 'rotate-180')}
/>
</div>
</Collapsible.Trigger>
@@ -1,8 +1,24 @@
import { useParams } from 'common'
import { CLIENT_LIBRARIES } from 'common/constants'
import { HelpCircle } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { ChangeEvent, useEffect, useRef, useState } from 'react'
import toast from 'react-hot-toast'
import { useParams } from 'common'
import Divider from 'components/ui/Divider'
import InformationBox from 'components/ui/InformationBox'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { getProjectAuthConfig } from 'data/auth/auth-config-query'
import { useSendSupportTicketMutation } from 'data/feedback/support-ticket-send'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import type { Project } from 'data/projects/project-detail-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useFlag } from 'hooks'
import useLatest from 'hooks/misc/useLatest'
import { detectBrowser } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import {
AlertDescription_Shadcn_,
AlertTitle_Shadcn_,
@@ -19,22 +35,7 @@ import {
Input,
Listbox,
} from 'ui'
import Divider from 'components/ui/Divider'
import InformationBox from 'components/ui/InformationBox'
import MultiSelect from 'ui-patterns/MultiSelect'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { getProjectAuthConfig } from 'data/auth/auth-config-query'
import { useSendSupportTicketMutation } from 'data/feedback/support-ticket-send'
import { useOrganizationsQuery } from 'data/organizations/organizations-query'
import type { Project } from 'data/projects/project-detail-query'
import { useProjectsQuery } from 'data/projects/projects-query'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useFlag, useStore } from 'hooks'
import useLatest from 'hooks/misc/useLatest'
import { detectBrowser } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import { HelpCircle } from 'lucide-react'
import DisabledStateForFreeTier from './DisabledStateForFreeTier'
import { CATEGORY_OPTIONS, SERVICE_OPTIONS, SEVERITY_OPTIONS } from './Support.constants'
import { formatMessage, uploadAttachments } from './SupportForm.utils'
@@ -47,7 +48,6 @@ export interface SupportFormProps {
}
const SupportForm = ({ setSentCategory }: SupportFormProps) => {
const { ui } = useStore()
const { isReady } = useRouter()
const { ref, slug, subject, category, message } = useParams()
@@ -77,15 +77,11 @@ const SupportForm = ({ setSentCategory }: SupportFormProps) => {
const { mutate: submitSupportTicket } = useSendSupportTicketMutation({
onSuccess: (res, variables) => {
ui.setNotification({ category: 'success', message: 'Support request sent. Thank you!' })
toast.success('Support request sent. Thank you!')
setSentCategory(variables.category)
},
onError: (error) => {
ui.setNotification({
error,
category: 'error',
message: `Failed to submit support ticket: ${error.message}`,
})
toast.error(`Failed to submit support ticket: ${error.message}`)
setIsSubmitting(false)
},
})
@@ -146,10 +142,7 @@ const SupportForm = ({ setSentCategory }: SupportFormProps) => {
setUploadedFiles(uploadedFiles.concat(itemsToBeUploaded))
if (items.length + uploadedFiles.length > MAX_ATTACHMENTS) {
ui.setNotification({
category: 'info',
message: `Only up to ${MAX_ATTACHMENTS} attachments are allowed`,
})
toast(`Only up to ${MAX_ATTACHMENTS} attachments are allowed`)
}
event.target.value = ''
@@ -1,28 +1,28 @@
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useQueryClient } from '@tanstack/react-query'
import { useParams } from 'common'
import Link from 'next/link'
import { useState } from 'react'
import { Button, IconPauseCircle, Modal } from 'ui'
import toast from 'react-hot-toast'
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
import { useParams } from 'common'
import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query'
import { useProjectRestoreMutation } from 'data/projects/project-restore-mutation'
import { setProjectStatus } from 'data/projects/projects-query'
import { useCheckPermissions, useSelectedOrganization, useStore } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
import { useProjectContext } from './ProjectContext'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useCheckPermissions, useSelectedOrganization } from 'hooks'
import { PROJECT_STATUS } from 'lib/constants'
import { Button, IconPauseCircle, Modal } from 'ui'
import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog'
import { useProjectContext } from './ProjectContext'
export interface ProjectPausedStateProps {
product?: string
}
const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
const queryClient = useQueryClient()
const { ui } = useStore()
const { ref } = useParams()
const queryClient = useQueryClient()
const selectedOrganization = useSelectedOrganization()
const { project } = useProjectContext()
const orgSlug = selectedOrganization?.slug
@@ -42,7 +42,7 @@ const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
const { mutate: restoreProject } = useProjectRestoreMutation({
onSuccess: (res, variables) => {
setProjectStatus(queryClient, variables.ref, PROJECT_STATUS.RESTORING)
ui.setNotification({ category: 'success', message: 'Restoring project' })
toast.success('Restoring project')
},
})
@@ -53,21 +53,14 @@ const ProjectPausedState = ({ product }: ProjectPausedStateProps) => {
const onSelectRestore = () => {
if (!canResumeProject) {
ui.setNotification({
category: 'error',
message: 'You do not have the required permissions to restore this project',
})
toast.error('You do not have the required permissions to restore this project')
} else if (hasMembersExceedingFreeTierLimit) setShowFreeProjectLimitWarning(true)
else setShowConfirmRestore(true)
}
const onConfirmRestore = () => {
if (!project) {
return ui.setNotification({
error: 'Project is required',
category: 'error',
message: 'Unable to restore: project is required',
})
return toast.error('Unable to restore: project is required')
}
restoreProject({ ref: project.ref })
}