diff --git a/packages/manager/.changeset/pr-13355-fixed-1770132340383.md b/packages/manager/.changeset/pr-13355-fixed-1770132340383.md new file mode 100644 index 00000000000..3086aedc887 --- /dev/null +++ b/packages/manager/.changeset/pr-13355-fixed-1770132340383.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Fixed +--- + +DBaaS Backup / delete dialog bugs ([#13355](https://github.com/linode/manager/pull/13355)) diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseAdvancedConfiguration/DatabaseAdvancedConfigurationDrawer.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseAdvancedConfiguration/DatabaseAdvancedConfigurationDrawer.tsx index e38b1542747..00379b6243b 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseAdvancedConfiguration/DatabaseAdvancedConfigurationDrawer.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseAdvancedConfiguration/DatabaseAdvancedConfigurationDrawer.tsx @@ -15,13 +15,7 @@ import Grid from '@mui/material/Grid'; import { Button } from 'akamai-cds-react-components'; import { enqueueSnackbar } from 'notistack'; import React, { useEffect, useMemo, useState } from 'react'; -import { - Controller, - get, - useFieldArray, - useForm, - useWatch, -} from 'react-hook-form'; +import { Controller, get, useFieldArray, useForm } from 'react-hook-form'; import type { SubmitHandler } from 'react-hook-form'; import { Link } from 'src/components/Link'; @@ -101,8 +95,6 @@ export const DatabaseAdvancedConfigurationDrawer = (props: Props) => { name: 'configs', }); - const configs = useWatch({ control, name: 'configs' }); - useEffect(() => { if (existingConfigurations.length > 0 || open) { reset({ configs: existingConfigurations }); @@ -212,12 +204,12 @@ export const DatabaseAdvancedConfigurationDrawer = (props: Props) => { )} - {!isLoading && configs.length === 0 && ( + {!isLoading && fields.length === 0 && ( No advanced configurations have been added. )} - {configs.map((config, index) => ( + {fields.map((config, index) => ( { { buttonType="primary" data-qa-settings-button="restore" disabled={ - versionOption === 'dateTime' && - (!date || !time || !!errors.time) + Boolean(unableToRestoreCopy) || + (versionOption === 'dateTime' && + (!date || !time || !!errors.time)) } onClick={() => setIsRestoreDialogOpen(true)} + tooltipText={unableToRestoreCopy} > Restore diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackupsDialog.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackupsDialog.tsx index f6112e22157..ffdeb40c111 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackupsDialog.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseBackups/DatabaseBackupsDialog.tsx @@ -3,7 +3,6 @@ import { ActionsPanel, Dialog, Notice, Typography } from '@linode/ui'; import { useNavigate } from '@tanstack/react-router'; import { useSnackbar } from 'notistack'; import * as React from 'react'; -import { useState } from 'react'; import { useFormContext, useWatch } from 'react-hook-form'; import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; @@ -24,7 +23,6 @@ export const DatabaseBackupsDialog = (props: Props) => { const { database, onClose, open } = props; const navigate = useNavigate(); const { enqueueSnackbar } = useSnackbar(); - const [isRestoring, setIsRestoring] = useState(false); const { control } = useFormContext(); const [date, time, region] = useWatch({ @@ -34,19 +32,25 @@ export const DatabaseBackupsDialog = (props: Props) => { const formattedDate = toFormattedDate(date, time); - const { error, mutateAsync: restore } = useRestoreFromBackupMutation( - database.engine, - { - fork: toDatabaseFork(database.id, date, time), - region, - // Assign same VPC when forking to the same region, otherwise set VPC to null - private_network: - database.region === region ? database.private_network : null, - } - ); + const { + error, + mutateAsync: restore, + reset, + isPending, + } = useRestoreFromBackupMutation(database.engine, { + fork: toDatabaseFork(database.id, date, time), + region, + // Assign same VPC when forking to the same region, otherwise set VPC to null + private_network: + database.region === region ? database.private_network : null, + }); + + const _onClose = () => { + onClose(); + reset(); + }; const handleRestoreDatabase = () => { - setIsRestoring(true); restore().then((database: Database) => { navigate({ to: `/databases/$engine/$databaseId`, @@ -58,7 +62,7 @@ export const DatabaseBackupsDialog = (props: Props) => { enqueueSnackbar('Your database is being restored.', { variant: 'success', }); - onClose(); + _onClose(); }); }; @@ -67,11 +71,20 @@ export const DatabaseBackupsDialog = (props: Props) => { return ( + {error && ( + + )} {isClusterWithVPCAndForkingToDifferentRegion && ( // Show warning when forking a cluster with VPC to a different region The database cluster is currently assigned to a VPC. When you restore @@ -91,13 +104,13 @@ export const DatabaseBackupsDialog = (props: Props) => { primaryButtonProps={{ 'data-testid': 'submit', label: 'Restore', - loading: isRestoring, + loading: isPending, onClick: handleRestoreDatabase, }} secondaryButtonProps={{ 'data-testid': 'cancel', label: 'Cancel', - onClick: onClose, + onClick: _onClose, }} sx={{ display: 'flex', @@ -105,15 +118,6 @@ export const DatabaseBackupsDialog = (props: Props) => { paddingBottom: '0', }} /> - {error ? ( - - ) : null} ); }; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx index d9b8c332039..7af56886759 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx @@ -22,7 +22,7 @@ import { useFlags } from 'src/hooks/useFlags'; import AccessControls from '../AccessControls'; import { useDatabaseDetailContext } from '../DatabaseDetailContext'; -import DatabaseSettingsDeleteClusterDialog from './DatabaseSettingsDeleteClusterDialog'; +import { DatabaseSettingsDeleteClusterDialog } from './DatabaseSettingsDeleteClusterDialog'; import { DatabaseSettingsMaintenance } from './DatabaseSettingsMaintenance'; import DatabaseSettingsMenuItem from './DatabaseSettingsMenuItem'; import DatabaseSettingsResetPasswordDialog from './DatabaseSettingsResetPasswordDialog'; diff --git a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog.tsx b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog.tsx index 620707f3244..4ccf79ce2f0 100644 --- a/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog.tsx +++ b/packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog.tsx @@ -17,35 +17,33 @@ interface Props { open: boolean; } -export const DatabaseSettingsDeleteClusterDialog: React.FC = (props) => { +export const DatabaseSettingsDeleteClusterDialog = (props: Props) => { const { databaseEngine, databaseID, databaseLabel, onClose, open } = props; const { enqueueSnackbar } = useSnackbar(); - const { mutateAsync: deleteDatabase } = useDeleteDatabaseMutation( - databaseEngine, - databaseID - ); - const defaultError = 'There was an error deleting this Database Cluster.'; - const [error, setError] = React.useState(''); - const [isLoading, setIsLoading] = React.useState(false); + const { + mutateAsync: deleteDatabase, + error, + isPending, + reset, + } = useDeleteDatabaseMutation(databaseEngine, databaseID); const navigate = useNavigate(); + const _onClose = () => { + onClose(); + reset(); + }; + const onDeleteCluster = () => { - setIsLoading(true); - deleteDatabase() - .then(() => { - setIsLoading(false); - enqueueSnackbar('Database Cluster deleted successfully.', { - variant: 'success', - }); - onClose(); - navigate({ - to: '/databases', - }); - }) - .catch((e) => { - setIsLoading(false); - setError(getAPIErrorOrDefault(e, defaultError)[0].reason); + deleteDatabase().then(() => { + enqueueSnackbar('Database Cluster deleted successfully.', { + variant: 'success', }); + _onClose(); + reset(); + navigate({ + to: '/databases', + }); + }); }; return ( @@ -59,13 +57,23 @@ export const DatabaseSettingsDeleteClusterDialog: React.FC = (props) => { }} expand label={'Cluster Name'} - loading={isLoading} + loading={isPending} onClick={onDeleteCluster} - onClose={onClose} + onClose={_onClose} open={open} title={`Delete Database Cluster ${databaseLabel}`} > - {error ? : null} + {error ? ( + + ) : null} Warning: Deleting your entire database will delete @@ -76,5 +84,3 @@ export const DatabaseSettingsDeleteClusterDialog: React.FC = (props) => { ); }; - -export default DatabaseSettingsDeleteClusterDialog; diff --git a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx index dba7dd1599a..9bb985eed80 100644 --- a/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx +++ b/packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx @@ -11,7 +11,7 @@ import { import React from 'react'; import { TableRowEmpty } from 'src/components/TableRowEmpty/TableRowEmpty'; -import DatabaseSettingsDeleteClusterDialog from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog'; +import { DatabaseSettingsDeleteClusterDialog } from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsDeleteClusterDialog'; import DatabaseSettingsResetPasswordDialog from 'src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsResetPasswordDialog'; import { ManageAccessControlDrawer } from 'src/features/Databases/DatabaseDetail/ManageAccessControlDrawer'; import DatabaseLogo from 'src/features/Databases/DatabaseLanding/DatabaseLogo';