From 540049992dfa8d7644b5fa380a1e46bb68c24ebb Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Fri, 8 Mar 2024 18:28:21 +0800 Subject: [PATCH] Replace ui setnotification with toast part 2 (#21872) * Replace ui setnotification with toast part 2 * Prettier lint --- .../interfaces/App/CommandMenuWrapper.tsx | 23 +++----- .../interfaces/App/RouteValidationWrapper.tsx | 8 +-- .../BasicAuthSettingsForm.tsx | 14 ++--- .../Auth/Hooks/EnterpriseHooksConfig.tsx | 14 ++--- .../Auth/Policies/PolicyEditorModal/index.tsx | 36 ++++--------- .../interfaces/Auth/Users/UserDropdown.tsx | 37 ++++--------- .../BranchManagement/BranchManagement.tsx | 35 ++++++------ .../Database/Hooks/DeleteHookModal.tsx | 7 ++- .../Database/Hooks/EditHookPanel.tsx | 33 ++++-------- .../Database/Indexes/CreateIndexSidePanel.tsx | 15 ++---- .../interfaces/Docs/Description.tsx | 20 ++----- .../interfaces/Docs/GeneratingTypes.tsx | 18 ++----- .../EdgeFunctionDetails.tsx | 35 ++++++------ .../BillingSettings/BillingEmail.tsx | 14 ++--- .../CostControl/SpendCapSidePanel.tsx | 40 ++++++-------- .../ChangePaymentMethodModal.tsx | 18 +++---- .../Subscription/ExitSurveyModal.tsx | 28 ++++------ .../Subscription/PlanUpdateSidePanel.tsx | 29 ++++------ .../BillingSettings/TaxID/TaxID.tsx | 8 +-- .../DeleteOrganizationButton.tsx | 17 ++---- .../GeneralSettings/GeneralSettings.tsx | 16 +++--- .../Organization/NewOrg/NewOrgForm.tsx | 26 ++------- .../Organization/OAuthApps/DeleteAppModal.tsx | 9 ++-- .../OAuthApps/PublishAppSidePanel/index.tsx | 37 ++++--------- .../TeamSettings/InviteMemberButton.tsx | 22 +++----- .../TeamSettings/MemberActions.tsx | 39 +++++--------- .../Organization/TeamSettings/MembersView.tsx | 54 ++++++++----------- .../TeamSettings/TeamSettings.tsx | 15 +++--- .../ProjectAPIDocs/Content/Entities.tsx | 19 ++----- .../interfaces/SQLEditor/RenameQueryModal.tsx | 21 +++----- .../UtilityPanel/ResultsDropdown.tsx | 23 ++++---- .../SQLEditor/UtilityPanel/UtilityPanel.tsx | 2 +- .../interfaces/Settings/API/JWTSettings.tsx | 26 +++++---- .../Settings/API/PostgrestConfig.tsx | 11 ++-- .../interfaces/Settings/API/ServiceList.tsx | 15 ++---- .../Settings/Database/BannedIPs.tsx | 29 ++++------ .../Database/DiskSizeConfiguration.tsx | 13 ++--- .../Settings/Database/SSLConfiguration.tsx | 19 +++---- .../Infrastructure/PauseProjectButton.tsx | 15 +++--- .../ProjectUpgradeAlert.tsx | 19 ++++--- .../Vault/Secrets/DeleteSecretModal.tsx | 16 ++---- .../interfaces/Settings/Vault/VaultToggle.tsx | 13 ++--- .../interfaces/SignIn/ForgotPasswordForm.tsx | 21 +++----- .../interfaces/SignIn/ResetPasswordForm.tsx | 17 ++---- .../interfaces/SignIn/SignInMfaForm.tsx | 17 ++---- .../interfaces/SignIn/SignInSSOForm.tsx | 8 +-- .../interfaces/SignIn/SignUpForm.tsx | 15 ++---- .../interfaces/Storage/CreateBucketModal.tsx | 32 +++++------ .../interfaces/Support/SupportForm.tsx | 47 +++++++--------- .../ProjectLayout/ProjectPausedState.tsx | 29 ++++------ 50 files changed, 388 insertions(+), 706 deletions(-) diff --git a/apps/studio/components/interfaces/App/CommandMenuWrapper.tsx b/apps/studio/components/interfaces/App/CommandMenuWrapper.tsx index ca9daaa5688..3494f1a0cb5 100644 --- a/apps/studio/components/interfaces/App/CommandMenuWrapper.tsx +++ b/apps/studio/components/interfaces/App/CommandMenuWrapper.tsx @@ -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}`) } } diff --git a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx index fc1a18e4082..a6ff31026b9 100644 --- a/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx +++ b/apps/studio/components/interfaces/App/RouteValidationWrapper.tsx @@ -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 } diff --git a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx index 099c2d64ca5..1f92215b86e 100644 --- a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx +++ b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx @@ -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 }) }, } diff --git a/apps/studio/components/interfaces/Auth/Hooks/EnterpriseHooksConfig.tsx b/apps/studio/components/interfaces/Auth/Hooks/EnterpriseHooksConfig.tsx index 167f26f0c12..7c63d093e00 100644 --- a/apps/studio/components/interfaces/Auth/Hooks/EnterpriseHooksConfig.tsx +++ b/apps/studio/components/interfaces/Auth/Hooks/EnterpriseHooksConfig.tsx @@ -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 }) }, } diff --git a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx index e0f4b6548c9..83dc6de9ec8 100644 --- a/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx +++ b/apps/studio/components/interfaces/Auth/Policies/PolicyEditorModal/index.tsx @@ -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) diff --git a/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx b/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx index 82e1b24415f..2fcc49d359c 100644 --- a/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx @@ -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 { } } diff --git a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx index 89a4ac4dbc5..80caf03f0b8 100644 --- a/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx +++ b/apps/studio/components/interfaces/BranchManagement/BranchManagement.tsx @@ -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) }, }) diff --git a/apps/studio/components/interfaces/Database/Hooks/DeleteHookModal.tsx b/apps/studio/components/interfaces/Database/Hooks/DeleteHookModal.tsx index cfbc125fdef..19c5d54e5bc 100644 --- a/apps/studio/components/interfaces/Database/Hooks/DeleteHookModal.tsx +++ b/apps/studio/components/interfaces/Database/Hooks/DeleteHookModal.tsx @@ -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({ diff --git a/apps/studio/components/interfaces/Database/Hooks/EditHookPanel.tsx b/apps/studio/components/interfaces/Database/Hooks/EditHookPanel.tsx index a146b94780d..c978ecc60d5 100644 --- a/apps/studio/components/interfaces/Database/Hooks/EditHookPanel.tsx +++ b/apps/studio/components/interfaces/Database/Hooks/EditHookPanel.tsx @@ -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(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 diff --git a/apps/studio/components/interfaces/Database/Indexes/CreateIndexSidePanel.tsx b/apps/studio/components/interfaces/Database/Indexes/CreateIndexSidePanel.tsx index 712dc412d14..d7fa601be4c 100644 --- a/apps/studio/components/interfaces/Database/Indexes/CreateIndexSidePanel.tsx +++ b/apps/studio/components/interfaces/Database/Indexes/CreateIndexSidePanel.tsx @@ -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}`) }, }) diff --git a/apps/studio/components/interfaces/Docs/Description.tsx b/apps/studio/components/interfaces/Docs/Description.tsx index 8cabc869455..0592aba5856 100644 --- a/apps/studio/components/interfaces/Docs/Description.tsx +++ b/apps/studio/components/interfaces/Docs/Description.tsx @@ -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}`) } } diff --git a/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx b/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx index f8af8a112f0..2d3d2a0c026 100644 --- a/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx +++ b/apps/studio/components/interfaces/Docs/GeneratingTypes.tsx @@ -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) } diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx index 490e892abc1..95314c48e1f 100644 --- a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx +++ b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionDetails.tsx @@ -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) {} } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx index a7ae06dad72..1dbe974b405 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingEmail.tsx @@ -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 { } } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/CostControl/SpendCapSidePanel.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/CostControl/SpendCapSidePanel.tsx index f30acf2548d..136394db3d0 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/CostControl/SpendCapSidePanel.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/CostControl/SpendCapSidePanel.tsx @@ -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 (
{ !isFreePlan && setSelectedOption(option.value) Telemetry.sendActivity( @@ -248,7 +240,7 @@ const SpendCapSidePanel = () => { > Spend Cap { />

{ - 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}`) }, } ) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx index ed7f6166289..7cb44fcfe5b 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/ExitSurveyModal.tsx @@ -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(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' }) } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx index fbdae99ac0b..12c5349d50c 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx @@ -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 = () => { >

-

{plan.name}

+

{plan.name}

{isCurrentPlan ? (
Current plan @@ -394,7 +387,7 @@ const PlanUpdateSidePanel = () => { className="!pl-0 !pr-1" icon={ { - 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') } }, }) diff --git a/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx b/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx index 664e4ec4415..0c32d4f14d8 100644 --- a/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx +++ b/apps/studio/components/interfaces/Organization/GeneralSettings/DeleteOrganizationButton.tsx @@ -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') } } diff --git a/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx b/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx index 792b45ccddd..47ac6753cae 100644 --- a/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx +++ b/apps/studio/components/interfaces/Organization/GeneralSettings/GeneralSettings.tsx @@ -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 = () => {

Important information regarding opting in diff --git a/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx b/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx index 59ee3e0b6a3..bee32c51f15 100644 --- a/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx +++ b/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx @@ -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 } diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/DeleteAppModal.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/DeleteAppModal.tsx index 69abd09e844..ee92712e936 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/DeleteAppModal.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/DeleteAppModal.tsx @@ -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() }, }) diff --git a/apps/studio/components/interfaces/Organization/OAuthApps/PublishAppSidePanel/index.tsx b/apps/studio/components/interfaces/Organization/OAuthApps/PublishAppSidePanel/index.tsx index 179f341a6f1..780ce02d523 100644 --- a/apps/studio/components/interfaces/Organization/OAuthApps/PublishAppSidePanel/index.tsx +++ b/apps/studio/components/interfaces/Organization/OAuthApps/PublishAppSidePanel/index.tsx @@ -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() 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 = ({

{iconUrl !== undefined ? (
) : (
{ - 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 } }) } diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx index 6a22efd6008..0f16818ff06 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx @@ -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)) { diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx index 009243ad0ea..9371ce689ef 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx @@ -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) diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx index d0f7d7b1f50..cebaac3d4f0 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx @@ -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) } diff --git a/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx b/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx index 165c1bce3dd..0b79aeff184 100644 --- a/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx +++ b/apps/studio/components/interfaces/ProjectAPIDocs/Content/Entities.tsx @@ -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) } diff --git a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx index d9bba744401..a2de3b21cfd 100644 --- a/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx +++ b/apps/studio/components/interfaces/SQLEditor/RenameQueryModal.tsx @@ -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}`) } } }} diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx index 6e7e25010df..af21687008e 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx @@ -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(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, diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityPanel.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityPanel.tsx index 9ba744f6ca2..7077a5ff1d1 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityPanel.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityPanel.tsx @@ -54,7 +54,7 @@ const UtilityPanel = ({ )}
- {result && result.rows && } + {result && result.rows && } { - const { ui } = useStore() const { ref: projectRef } = useParams() const [customToken, setCustomToken] = useState('') @@ -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) {} } diff --git a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx index 706d3291012..9da616d53fd 100644 --- a/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx +++ b/apps/studio/components/interfaces/Settings/API/PostgrestConfig.tsx @@ -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( diff --git a/apps/studio/components/interfaces/Settings/API/ServiceList.tsx b/apps/studio/components/interfaces/Settings/API/ServiceList.tsx index 72c50f94124..6a15bfa2ab3 100644 --- a/apps/studio/components/interfaces/Settings/API/ServiceList.tsx +++ b/apps/studio/components/interfaces/Settings/API/ServiceList.tsx @@ -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 } } diff --git a/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx b/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx index 93c2dd68104..57d11287a6d 100644 --- a/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx +++ b/apps/studio/components/interfaces/Settings/Database/BannedIPs.tsx @@ -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(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}`) }, }) diff --git a/apps/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx b/apps/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx index 829187d9818..40a026c0417 100644 --- a/apps/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx +++ b/apps/studio/components/interfaces/Settings/Database/DiskSizeConfiguration.tsx @@ -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) }, }) diff --git a/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx b/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx index d08ee2e9460..6cc8968c6dc 100644 --- a/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx +++ b/apps/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx @@ -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}`) }, } ) diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx index c878c8e147a..82d7d190067 100644 --- a/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx +++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/PauseProjectButton.tsx @@ -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 }) } diff --git a/apps/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx b/apps/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx index 73a3ae05b88..20eeb656975 100644 --- a/apps/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx +++ b/apps/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx @@ -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 }) } diff --git a/apps/studio/components/interfaces/Settings/Vault/Secrets/DeleteSecretModal.tsx b/apps/studio/components/interfaces/Settings/Vault/Secrets/DeleteSecretModal.tsx index 6d05a4ad92e..f413e421313 100644 --- a/apps/studio/components/interfaces/Settings/Vault/Secrets/DeleteSecretModal.tsx +++ b/apps/studio/components/interfaces/Settings/Vault/Secrets/DeleteSecretModal.tsx @@ -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) diff --git a/apps/studio/components/interfaces/Settings/Vault/VaultToggle.tsx b/apps/studio/components/interfaces/Settings/Vault/VaultToggle.tsx index 35f89b0008b..b7de3eea001 100644 --- a/apps/studio/components/interfaces/Settings/Vault/VaultToggle.tsx +++ b/apps/studio/components/interfaces/Settings/Vault/VaultToggle.tsx @@ -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({ diff --git a/apps/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx b/apps/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx index 08f519c3053..65ebad425ba 100644 --- a/apps/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx +++ b/apps/studio/components/interfaces/SignIn/ForgotPasswordForm.tsx @@ -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(null) const captchaRef = useRef(null) + const [captchaToken, setCaptchaToken] = useState(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}`) }, }) diff --git a/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx b/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx index 37f88ab4a82..dca1a2d8730 100644 --- a/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx +++ b/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx @@ -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 }) } } diff --git a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx index 23108222919..c3552582891 100644 --- a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx @@ -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 }) }, } ) diff --git a/apps/studio/components/interfaces/SignIn/SignInSSOForm.tsx b/apps/studio/components/interfaces/SignIn/SignInSSOForm.tsx index e53a992b2cb..2c887f01b49 100644 --- a/apps/studio/components/interfaces/SignIn/SignInSSOForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignInSSOForm.tsx @@ -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 }) } } diff --git a/apps/studio/components/interfaces/SignIn/SignUpForm.tsx b/apps/studio/components/interfaces/SignIn/SignUpForm.tsx index 4be3413cd80..17bcfbb61e2 100644 --- a/apps/studio/components/interfaces/SignIn/SignUpForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignUpForm.tsx @@ -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(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}`) }, }) diff --git a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx index 096ed297d3b..e108f6e4c47 100644 --- a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx +++ b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx @@ -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) => {
diff --git a/apps/studio/components/interfaces/Support/SupportForm.tsx b/apps/studio/components/interfaces/Support/SupportForm.tsx index 74860bf5ab2..11eb7c4e590 100644 --- a/apps/studio/components/interfaces/Support/SupportForm.tsx +++ b/apps/studio/components/interfaces/Support/SupportForm.tsx @@ -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 = '' diff --git a/apps/studio/components/layouts/ProjectLayout/ProjectPausedState.tsx b/apps/studio/components/layouts/ProjectLayout/ProjectPausedState.tsx index fd30a1e30d7..81aed121ef9 100644 --- a/apps/studio/components/layouts/ProjectLayout/ProjectPausedState.tsx +++ b/apps/studio/components/layouts/ProjectLayout/ProjectPausedState.tsx @@ -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 }) }