-
Notifications
You must be signed in to change notification settings - Fork 361
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: [UIE-8090] - DBaaS Settings and Landing Page #11152
feat: [UIE-8090] - DBaaS Settings and Landing Page #11152
Conversation
2ccd150
to
5be73c9
Compare
Coverage Report: ✅ |
5be73c9
to
4ac31c4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend breaking up this PR up so that this banner into its own PR. The banner logic seems somewhat complex and the implementation may be flawed (see comment below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai After discussion, we've removed the banner and associated logic from this pull request and will separate that into it's own ticket. The design and logic will be reworked as part of that new ticket.
); | ||
}; | ||
|
||
export default DatabaseSettingsSuspendClusterDialog; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please avoid using default exports. You can just use the export const DatabaseSettingsSuspendClusterDialog
and remove this export
); | ||
|
||
return ( | ||
<Dialog |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You may want to consider using the Confirmation Dialog component rather than a pure Dialog. It may help you to fix the style/spacing issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai Can the confirmation dialog support a checkbox that enables/disables the Suspend Cluster button?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai I've updated the suspend dialog to use the confirmation dialog.
const [error, setError] = React.useState(''); | ||
const [isLoading, setIsLoading] = React.useState(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to define your own error and loading state. The useSuspendDatabaseMutation
hook returns a isPending
and error
state you can use
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated this to leverage isPending
and error
from useSuspendDatabaseMutation
export const DatabaseSettingsSuspendClusterDialog: React.FC<SuspendDialogProps> = ( | ||
props | ||
) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We prefer to not use React.FC
in our repo. You can type your props this way:
export const DatabaseSettingsSuspendClusterDialog: React.FC<SuspendDialogProps> = ( | |
props | |
) => { | |
export const DatabaseSettingsSuspendClusterDialog = ( | |
props: SuspendDialogProps | |
) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I've made this update.
return notifications; | ||
} | ||
// Loop through new databases to find suspended databases and check if a notification should be displayed. | ||
newDatabases.data.forEach((database) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We may need to reconsider/rearchitect this banner feature. Our tables are API-paginated, meaning that newDatabases
only contains the first page of databases. This logic may not produce the desired result a suspended database is not on the current API paginated page.
I do not reccomend fetching all databases to solve this because it kind of defeats the purpose of API based pagunation.
There may be a clever way we can use api-v4 x-filtering to query for only suspended databases, which might be a more scaleable solution.
I'm happy to do a proof of concept or talk through alternatives to this, just let me know!
const isNewDatabase = isV2Enabled && !!newDatabases?.data.length; | ||
const showSuspend = isDatabasesV2GA && !!newDatabases?.data.length; | ||
|
||
const getSuspendNotification = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I recommend extracting stuff like this into utility functions outside of this component. It makes it easier to unit test and makes the code more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai Thanks for the recommendation. We'll follow moving this into utilities once/if it's used in a second place.
{showSuspend | ||
? suspendNotifications.map((notification) => ( | ||
<DismissibleBanner | ||
preferenceKey={`${notification.id}-suspend-notice`} | ||
variant="warning" | ||
key={`${notification.id}-key`} | ||
> | ||
<Box> | ||
<Typography> | ||
{`${notification.label} has been suspended for ${SUSPEND_WARNING_DAYS} days. If it is not resumed to be operational it will be automatically deleted ${notification.expirationCopy}.`} | ||
</Typography> | ||
</Box> | ||
</DismissibleBanner> | ||
)) | ||
: null} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is going to look very bad if a user has lots of suspended databases (especially on mobile). I think we should consider a more scaleable UI. Cloud Manager already has enough banners.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai Thanks, we'll note this concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bnussman-akamai After discussion, we've removed the banner and associated logic from this pull request and will separate that into it's own ticket. The design and logic will be reworked as part of that new ticket.
setIsSuspendClusterDialogOpen(true); | ||
}; | ||
|
||
const handleResume = (database: DatabaseInstance) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this resume logic isn't really used in this component we might be able to move it down into the DatabaseActionMenu
component
queryClient.invalidateQueries({ | ||
queryKey: databaseQueries.databases.queryKey, | ||
}); | ||
queryClient.removeQueries({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the removeQueries
call here and in useResumeDatabaseMutation
intentional? I believe that will evict that database from the cache.
invalidateQueries
might be the desired function here. invalidateQueries
will make Cloud Manager refetch the database as needed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reviewing your comment, I believe that invalidateQueries makes more sense for this use case as the database hasn't been removed, only updated. So we'll want to trigger a refetch on stale data rather than removing the cache entirely.
I'll update this for both useResumeDatabaseMutation
and useSuspendDatabaseMutation
.
9129ac6
to
cd8bab9
Compare
cd8bab9
to
7394385
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking great. Thanks for responding to that initial feedback and breaking up this PR, we appreciate it!
Just a few more changes and I think this will be good to go
<ConfirmationDialog | ||
maxWidth="sm" | ||
actions={actions} | ||
onClose={onClose} | ||
open={open} | ||
title={`Suspend ${databaseLabel} cluster?`} | ||
> | ||
{isError && ( | ||
<Notice | ||
text={getAPIErrorOrDefault(error, defaultError)[0].reason} | ||
variant="error" | ||
/> | ||
)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<ConfirmationDialog | |
maxWidth="sm" | |
actions={actions} | |
onClose={onClose} | |
open={open} | |
title={`Suspend ${databaseLabel} cluster?`} | |
> | |
{isError && ( | |
<Notice | |
text={getAPIErrorOrDefault(error, defaultError)[0].reason} | |
variant="error" | |
/> | |
)} | |
<ConfirmationDialog | |
error={ | |
error ? getAPIErrorOrDefault(error, defaultError)[0].reason : undefined | |
} | |
actions={actions} | |
maxWidth="sm" | |
onClose={onClose} | |
open={open} | |
title={`Suspend ${databaseLabel} cluster?`} | |
> |
Our ConfirmationDialog
component has an error
prop you can use to show the error state. It looks a bit different than the notice, but it allows us to maintain consistancy across Cloud Manager.
We also have an upcoming effort to improve this error state (Ticket M3-8792) so it will look very similar to what you implemented soon
<b>{suspendClusterCopy}</b> | ||
</Typography> | ||
</Notice> | ||
<div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This <div>
might be unnecessary
const closeButton = getByTestId('CloseIcon'); | ||
expect(closeButton).toBeVisible(); | ||
|
||
fireEvent.click(closeButton); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should use await userEvent
rather than fireEvent
whenever possible. It is recommended for testing interactions by React Testing library (docs)
variant: 'success', | ||
}); | ||
}) | ||
.catch((e: any) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use APIError[]
here
.catch((e: any) => { | |
.catch((e: APIError[]) => { |
7394385
to
05ea8b4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Functionality LGTM! left few comments to lift conditional rendering for modals and nit-picky things to improve readability.
packages/manager/src/features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettings.tsx
Outdated
Show resolved
Hide resolved
.../features/Databases/DatabaseDetail/DatabaseSettings/DatabaseSettingsSuspendClusterDialog.tsx
Outdated
Show resolved
Hide resolved
You can resume the clusters work within 180 days from its suspension. | ||
After that time, the cluster will be deleted permanently.`; | ||
|
||
const actions = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use capital naming conventions for JSX elements used within a component (Contributing Guidelines)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the point of this comment, but if you search the code for const actions =
you'll see there are a dozen like this and none with capitalization. For now, it seems like consistency would be preferred, but I am new to this whole thing, so please correct me if I am wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are indeed transitioning to this new approach to maintain a cleaner and consistent codebase.
setHasConfirmed(false); | ||
}; | ||
|
||
const suspendClusterCopy = `A suspended cluster stops working immediately and you won't be billed for it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be moved to constants file or out side of the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be but there are two different "suspendClusterCopy" constants in the code and they are different. I'm just a visitor here while Sam is on vacation :) but I prefer the way Sam has done it, being close to the one place it's used.
reset, | ||
} = useSuspendDatabaseMutation(databaseEngine, databaseId); | ||
|
||
const defaultError = 'There was an error suspending this Database Cluster.'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be moved to constants file or out side of the component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above but in this case it's an even more generic name and is being used once in this area. I toyed with the idea of removing it altogether and just inlining the text as is done throughout this codebase, it seems, but in the end decided to leave it alone.
packages/manager/src/features/Databases/DatabaseLanding/DatabaseLandingTable.tsx
Outdated
Show resolved
Hide resolved
packages/manager/src/features/Databases/DatabaseLanding/DatabaseActionMenu.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me!
There is a conflict that needs to be addressed
<Typography style={{ fontSize: '0.875rem' }}> | ||
<b>{suspendClusterCopy}</b> | ||
</Typography> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might just want to pass
<Typography style={{ fontSize: '0.875rem' }}> | |
<b>{suspendClusterCopy}</b> | |
</Typography> | |
{suspendClusterCopy} |
and let the Notice handle the text size and bolding
import { useResumeDatabaseMutation } from 'src/queries/databases/databases'; | ||
import { enqueueSnackbar } from 'notistack'; | ||
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils'; | ||
import { APIError } from '@linode/api-v4/src/types'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Our import should not contain src
. Might cause problems in the future
import { APIError } from '@linode/api-v4/src/types'; | |
import { APIError } from '@linode/api-v4'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was removed because the import was removed. (It was being used to because someone had added a type defn to the catch's error, but doing so was causing "TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified." so I changed APIError
to any
and this import was no longer needed.) Good to know going forward though to look for this problem.
…nding UIE-8090: Add suspend and resume to Landing Page and details Settings…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for addressing the feedback, LGTM! Please update this branch with the latest from develop.
…nding UIE-8090 dbaas settings landing - updating from develop
@smans-akamai @mpolotsk-akamai @rodonnel-akamai Looks like there are some type errors in the DatabaseSettings file. |
…nding UIE 8090 dbaas settings landing - update to catch up with develop and fix merge problem
Cloud Manager UI test results🎉 445 passing tests on test run #13 ↗︎
|
Confirming failed tests passed and type errors are fixed. |
Description 📝
Adding Suspend and Resume functionality to Landing Page and database details Settings Views for GA
Changes 🔄
Target release date 🗓️
11/12/2024
Preview 📷
How to test 🧪
Prerequisites
Verification steps
As an Author I have considered 🤔
Check all that apply
Commit message and pull request title format standards
<commit type>: [JIRA-ticket-number] - <description>
Commit Types:
feat
: New feature for the user (not a part of the code, or ci, ...).fix
: Bugfix for the user (not a fix to build something, ...).change
: Modifying an existing visual UI instance. Such as a component or a feature.refactor
: Restructuring existing code without changing its external behavior or visual UI. Typically to improve readability, maintainability, and performance.test
: New tests or changes to existing tests. Does not change the production code.upcoming
: A new feature that is in progress, not visible to users yet, and usually behind a feature flag.Example:
feat: [M3-1234] - Allow user to view their login history