From b3a8f922fbcbda8f2d006e25b58b6c3032e9e3b2 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 18 Aug 2023 17:00:50 -0230 Subject: [PATCH 01/27] First draft --- .../Home/ProjectList/ProjectCard.tsx | 20 +++++- .../Home/ProjectList/ProjectCardWarnings.tsx | 66 +++++++++++++++++++ .../Home/ProjectList/ProjectList.tsx | 8 +++ .../layouts/ProjectLayout/ProjectLayout.tsx | 11 +++- studio/components/ui/CardButton.tsx | 2 +- .../ui/UsageWarnings/UsageWarningBanner.tsx | 16 +++++ studio/data/usage/keys.ts | 1 + studio/data/usage/resource-warnings-query.ts | 40 +++++++++++ studio/pages/project/[ref]/index.tsx | 4 ++ 9 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx create mode 100644 studio/components/ui/UsageWarnings/UsageWarningBanner.tsx create mode 100644 studio/data/usage/resource-warnings-query.ts diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 0b1801f51a2..1c81df2fad7 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -5,7 +5,6 @@ import { IconGitHub, IconLoader, IconPauseCircle, - IconTriangle, } from 'ui' import CardButton from 'components/ui/CardButton' @@ -13,12 +12,15 @@ import { useProjectReadOnlyStatus } from 'hooks/misc/useProjectReadOnlyStatus' import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' +import { ResourceWarningResponse } from 'data/usage/resource-warnings-query' +import ProjectCardWarnings from './ProjectCardWarnings' export interface ProjectCardProps { project: Project rewriteHref?: string githubIntegration?: IntegrationProjectConnection vercelIntegration?: IntegrationProjectConnection + resourceWarnings?: ResourceWarningResponse } const ProjectCard = ({ @@ -26,6 +28,7 @@ const ProjectCard = ({ rewriteHref, githubIntegration, vercelIntegration, + resourceWarnings, }: ProjectCardProps) => { const { name, ref: projectRef } = project const desc = `${project.cloud_provider} | ${project.region}` @@ -36,13 +39,23 @@ const ProjectCard = ({ const isVercelIntegrated = vercelIntegration !== undefined const githubRepository = githubIntegration?.metadata.name ?? undefined - // Project status should supersede is read only status + // Project status should supersede its read only status const isHealthy = project.status === PROJECT_STATUS.ACTIVE_HEALTHY const isPausing = project.status === PROJECT_STATUS.GOING_DOWN || project.status === PROJECT_STATUS.PAUSING const isPaused = project.status === PROJECT_STATUS.INACTIVE const isRestoring = project.status === PROJECT_STATUS.RESTORING + const checkProjectResourceWarnings = (resourceWarnings: ResourceWarningResponse) => { + return Object.values(resourceWarnings).some( + (value) => typeof value === 'boolean' && value === true + ) + } + + // const projectHasResourceWarnings = + // resourceWarnings !== undefined ? checkProjectResourceWarnings(resourceWarnings) : false + const projectHasResourceWarnings = true + return (
  • )} + {resourceWarnings && projectHasResourceWarnings && ( + + )} } footer={ diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx new file mode 100644 index 00000000000..37fa3c17bd4 --- /dev/null +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -0,0 +1,66 @@ +import { ResourceWarningResponse } from 'data/usage/resource-warnings-query' +import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, IconAlertTriangle } from 'ui' + +export type ResourceWarningResponses = Omit< + Omit, + 'is_readonly_mode_enabled' +> + +export interface ProjectCardWarningsProps { + resourceWarnings: ResourceWarningResponses +} + +// reference for message wording +// www.notion.so/supabase/Notify-users-on-resource-exhaustion-on-the-dashboard-4bb7b9a990104720b179b7a37d07f41c?pvs=4#a79798b5ac72402e8bfea4c4715e7439 +const RESOURCE_WARNING_MESSAGES = { + is_readonly_mode_enabled: { + title: 'Project in readonly mode', + description: 'This database will no longer accept write requests.', + }, + is_disk_io_budget_below_threshold: { + title: 'Your project is depleting its Disk IO Budget', + description: 'Your instance may become unresponsive if fully exhausted.', + }, + is_disk_space_usage_beyond_threshold: { + title: 'Project has exhausted disk space budget', + description: 'Your instance may become unresponsive if fully exhausted.', + }, + is_cpu_load_beyond_threshold: { + title: 'Your project has high CPU usage', + description: `Your instance's performance is affected`, + }, + is_memory_and_swap_usage_beyond_threshold: { + title: 'Your project has high memory usage', + description: `Your instance's performance is affected.`, + }, + multiple_resource_warnings: { + title: 'Your project is burning up', + description: `Your instance's performance is affected.`, + }, +} + +export default function ProjectCardWarnings({ resourceWarnings }: ProjectCardWarningsProps) { + const activeWarnings = Object.keys(resourceWarnings).filter( + (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true + ) + + return ( +
    + + + + {activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.title + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof resourceWarnings]?.title} + + + {activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.description + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof resourceWarnings] + ?.description} + + + {/*
    {JSON.stringify(resourceWarnings, null, 2)}
    */} +
    + ) +} diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index d24aa81b1d0..5066c17a004 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -19,6 +19,7 @@ import { Organization, Project, ResponseError } from 'types' import ProjectCard from './ProjectCard' import ShimmeringCard from './ShimmeringCard' import { useOrgIntegrationsQuery } from 'data/integrations/integrations-query-org-only' +import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' export interface ProjectListProps { rewriteHref?: (projectRef: string) => string @@ -95,6 +96,10 @@ const OrganizationProjects = ({ }: OrganizationProjectsProps) => { const isEmpty = !projects || projects.length === 0 const canReadProjects = useCheckPermissions(PermissionAction.READ, 'projects', undefined, id) + const { data: resourceWarnings } = useResourceWarningQuery() + + console.log(resourceWarnings) + console.log('one render') return (
    @@ -165,6 +170,9 @@ const OrganizationProjects = ({ key={makeRandomString(5)} project={project} rewriteHref={rewriteHref ? rewriteHref(project.ref) : undefined} + resourceWarnings={resourceWarnings?.find( + (resourceWarning) => resourceWarning.project === project.ref + )} /> )) )} diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 90d627cc098..9f1e2429b0e 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -17,6 +17,8 @@ import ProjectPausedState from './ProjectPausedState' import RestoringState from './RestoringState' import UpgradingState from './UpgradingState' import AppLayout from '../AppLayout/AppLayout' +import UsageWarningBanner from 'components/ui/UsageWarnings/UsageWarningBanner' +import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' // [Joshen] This is temporary while we unblock users from managing their project // if their project is not responding well for any reason. Eventually needs a bit of an overhaul @@ -94,14 +96,12 @@ const ProjectLayout = ({
    {/* Left-most navigation side bar to access products */} {!hideIconBar && } - {/* Product menu bar */} {!showPausedState && ( {productMenu} )} -
    {!navLayoutV2 && !hideHeader && IS_PLATFORM && } {showPausedState ? ( @@ -179,6 +179,8 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { selectedProject?.status === PROJECT_STATUS.PAUSING const isProjectOffline = selectedProject?.postgrestStatus === 'OFFLINE' + const { data: resourceWarnings } = useResourceWarningQuery() + console.log(resourceWarnings) return ( <> {isLoading || (requiresProjectDetails && selectedProject === undefined) ? ( @@ -194,7 +196,10 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { ) : requiresDbConnection && isProjectBuilding ? ( ) : ( - {children} + <> + + {children} + )} ) diff --git a/studio/components/ui/CardButton.tsx b/studio/components/ui/CardButton.tsx index f0696bafe7b..f374f6cef82 100644 --- a/studio/components/ui/CardButton.tsx +++ b/studio/components/ui/CardButton.tsx @@ -27,7 +27,7 @@ const CardButton = ({ imgAlt, onClick, icon, - containerHeightClassName = 'h-32', + containerHeightClassName = 'h-48', loading = false, }: PropsWithChildren) => { const LinkContainer = ({ children }: { children: React.ReactNode }) => ( diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx new file mode 100644 index 00000000000..ea70a140856 --- /dev/null +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -0,0 +1,16 @@ +import Link from 'next/link' +import React from 'react' + +import { useParams } from 'common' +import { useSelectedOrganization } from 'hooks' + +export default function UsageWarningBanner() { + const { ref } = useParams() + const organization = useSelectedOrganization() + + return ( +
    + Project is experiencing high [product] usage. Learn more +
    + ) +} diff --git a/studio/data/usage/keys.ts b/studio/data/usage/keys.ts index aac6590a78e..b5aca6f8298 100644 --- a/studio/data/usage/keys.ts +++ b/studio/data/usage/keys.ts @@ -1,4 +1,5 @@ export const usageKeys = { usage: (projectRef: string | undefined) => ['projects', projectRef, 'usage'] as const, orgUsage: (orgSlug: string | undefined) => ['organizations', orgSlug, 'usage'] as const, + resourceWarnings: () => ['project', 'resource-warnings'] as const, } diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts new file mode 100644 index 00000000000..82be8e7f1eb --- /dev/null +++ b/studio/data/usage/resource-warnings-query.ts @@ -0,0 +1,40 @@ +import { useQuery, UseQueryOptions } from '@tanstack/react-query' +import { get, isResponseOk } from 'lib/common/fetch' +import { API_URL } from 'lib/constants' +import { usageKeys } from './keys' + +export type ResourceWarningResponse = { + project: string + is_readonly_mode_enabled: boolean + is_disk_io_budget_below_threshold: boolean + is_disk_space_usage_beyond_threshold: boolean + is_cpu_load_beyond_threshold: boolean + is_memory_and_swap_usage_beyond_threshold: boolean +} + +export async function getResourceWarnings(signal?: AbortSignal) { + const response = await get(`${API_URL}/projects-resource-warnings`, { + signal, + }) + if (!isResponseOk(response)) { + throw response.error + } + + return response +} + +export type ResourceWarningData = Awaited> +export type ResourceError = unknown + +export const useResourceWarningQuery = ({ + enabled = true, + ...options +}: UseQueryOptions = {}) => + useQuery( + usageKeys.resourceWarnings(), + ({ signal }) => getResourceWarnings(signal), + { + enabled: enabled, + ...options, + } + ) diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 06b56fd2a10..727d5789606 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -4,6 +4,7 @@ import ProjectUsageSection from 'components/interfaces/Home/ProjectUsageSection' import { ProjectLayoutWithAuth } from 'components/layouts' import ProjectPausedState from 'components/layouts/ProjectLayout/ProjectPausedState' import ProjectUpgradeFailedBanner from 'components/ui/ProjectUpgradeFailedBanner' +import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' import { useSelectedProject } from 'hooks' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import { NextPageWithLayout } from 'types' @@ -16,6 +17,9 @@ const Home: NextPageWithLayout = () => { ? project?.name : 'Welcome to your project' + const { data: resourceWarnings } = useResourceWarningQuery() + + console.log(resourceWarnings) return (
    From 17d4445ce70cd2a4870d12fa1b88ca90de707fc5 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 23 Aug 2023 15:54:14 +0800 Subject: [PATCH 02/27] Update warning on card buttons --- .../Home/ProjectList/ProjectCard.tsx | 109 ++++++++++-------- .../Home/ProjectList/ProjectCardWarnings.tsx | 49 ++++---- .../Home/ProjectList/ProjectList.tsx | 14 +-- .../layouts/ProjectLayout/ProjectLayout.tsx | 4 +- studio/components/ui/CardButton.tsx | 4 +- studio/data/usage/keys.ts | 2 +- studio/data/usage/resource-warnings-query.ts | 42 +++---- studio/pages/project/[ref]/index.tsx | 4 - 8 files changed, 110 insertions(+), 118 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 1c81df2fad7..68532417f5d 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -12,7 +12,7 @@ import { useProjectReadOnlyStatus } from 'hooks/misc/useProjectReadOnlyStatus' import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' -import { ResourceWarningResponse } from 'data/usage/resource-warnings-query' +import { ResourceWarning } from 'data/usage/resource-warnings-query' import ProjectCardWarnings from './ProjectCardWarnings' export interface ProjectCardProps { @@ -20,7 +20,7 @@ export interface ProjectCardProps { rewriteHref?: string githubIntegration?: IntegrationProjectConnection vercelIntegration?: IntegrationProjectConnection - resourceWarnings?: ResourceWarningResponse + resourceWarnings?: ResourceWarning } const ProjectCard = ({ @@ -34,10 +34,15 @@ const ProjectCard = ({ const desc = `${project.cloud_provider} | ${project.region}` const isReadonly = useProjectReadOnlyStatus(projectRef) - const isBranchingEnabled = project.preview_branch_refs.length > 0 - const isGithubIntegrated = githubIntegration !== undefined - const isVercelIntegrated = vercelIntegration !== undefined - const githubRepository = githubIntegration?.metadata.name ?? undefined + // const isBranchingEnabled = project.preview_branch_refs.length > 0 + // const isGithubIntegrated = githubIntegration !== undefined + // const isVercelIntegrated = vercelIntegration !== undefined + // const githubRepository = githubIntegration?.metadata.name ?? undefined + + const isBranchingEnabled = true + const isGithubIntegrated = true + const isVercelIntegrated = true + const githubRepository = 'supabase/supabase-js' // Project status should supersede its read only status const isHealthy = project.status === PROJECT_STATUS.ACTIVE_HEALTHY @@ -46,22 +51,21 @@ const ProjectCard = ({ const isPaused = project.status === PROJECT_STATUS.INACTIVE const isRestoring = project.status === PROJECT_STATUS.RESTORING - const checkProjectResourceWarnings = (resourceWarnings: ResourceWarningResponse) => { + const checkProjectResourceWarnings = (resourceWarnings: ResourceWarning) => { return Object.values(resourceWarnings).some( (value) => typeof value === 'boolean' && value === true ) } - // const projectHasResourceWarnings = - // resourceWarnings !== undefined ? checkProjectResourceWarnings(resourceWarnings) : false - const projectHasResourceWarnings = true + const projectHasResourceWarnings = + resourceWarnings !== undefined ? checkProjectResourceWarnings(resourceWarnings) : false return (
  • +

    {name}

    {isVercelIntegrated && ( @@ -87,61 +91,66 @@ const ProjectCard = ({ )}
    - {resourceWarnings && projectHasResourceWarnings && ( - - )}
    } footer={ -
    - {desc} +
    + {desc} - {isHealthy && isReadonly && ( -
    - -
    - - Read-only mode -
    -
    -
    + {isHealthy && ( + <> + {isReadonly ? ( + +
    + + Read-only mode +
    +
    + ) : ( + +
    + Active +
    +
    + )} + )} {isRestoring && ( -
    - -
    - - Restoring -
    -
    -
    + +
    + + Restoring +
    +
    )} {isPausing && ( -
    - -
    - - Pausing -
    -
    -
    + +
    + + Pausing +
    +
    )} {isPaused && ( -
    - -
    - - Paused -
    -
    -
    + +
    + + Paused +
    +
    )}
    } - /> + > + {resourceWarnings && projectHasResourceWarnings ? ( + + ) : ( +
    + )} +
  • ) } diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index 37fa3c17bd4..ec8f4af8e43 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -1,41 +1,36 @@ -import { ResourceWarningResponse } from 'data/usage/resource-warnings-query' +import { ResourceWarning } from 'data/usage/resource-warnings-query' import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, IconAlertTriangle } from 'ui' -export type ResourceWarningResponses = Omit< - Omit, - 'is_readonly_mode_enabled' -> - export interface ProjectCardWarningsProps { - resourceWarnings: ResourceWarningResponses + resourceWarnings: ResourceWarning } // reference for message wording // www.notion.so/supabase/Notify-users-on-resource-exhaustion-on-the-dashboard-4bb7b9a990104720b179b7a37d07f41c?pvs=4#a79798b5ac72402e8bfea4c4715e7439 const RESOURCE_WARNING_MESSAGES = { is_readonly_mode_enabled: { - title: 'Project in readonly mode', - description: 'This database will no longer accept write requests.', + title: 'Project is in readonly mode', + description: 'Database is no longer accept write requests.', }, is_disk_io_budget_below_threshold: { - title: 'Your project is depleting its Disk IO Budget', - description: 'Your instance may become unresponsive if fully exhausted.', + title: 'Project is depleting its Disk IO Budget', + description: 'Instance may become unresponsive if fully exhausted.', }, is_disk_space_usage_beyond_threshold: { - title: 'Project has exhausted disk space budget', - description: 'Your instance may become unresponsive if fully exhausted.', + title: 'Project is exhausting disk space budget', + description: 'Instance may become unresponsive if fully exhausted.', }, is_cpu_load_beyond_threshold: { - title: 'Your project has high CPU usage', - description: `Your instance's performance is affected`, + title: 'Project has high CPU usage', + description: `Instance's performance is affected`, }, is_memory_and_swap_usage_beyond_threshold: { - title: 'Your project has high memory usage', - description: `Your instance's performance is affected.`, + title: 'Project has high memory usage', + description: `Instance's performance is affected.`, }, multiple_resource_warnings: { - title: 'Your project is burning up', - description: `Your instance's performance is affected.`, + title: 'Project is exhausting multiple resources', + description: `Instance's performance is affected.`, }, } @@ -43,20 +38,26 @@ export default function ProjectCardWarnings({ resourceWarnings }: ProjectCardWar const activeWarnings = Object.keys(resourceWarnings).filter( (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true ) + const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') return (
    - + - + + {activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.title - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof resourceWarnings]?.title} + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.title} - + {activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.description - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof resourceWarnings] + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] ?.description} diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index ef5eff5de8e..a297da6c18d 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -12,7 +12,7 @@ import { import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useProjectsQuery } from 'data/projects/projects-query' -import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' +import { ResourceWarning, useResourceWarningsQuery } from 'data/usage/resource-warnings-query' import { useCheckPermissions } from 'hooks' import { IS_PLATFORM } from 'lib/constants' import { makeRandomString } from 'lib/helpers' @@ -37,6 +37,7 @@ const ProjectList = ({ rewriteHref }: ProjectListProps) => { isError: isErrorPermissions, error: permissionsError, } = usePermissionsQuery() + const { data: resourceWarnings } = useResourceWarningsQuery() const { data: allOverdueInvoices } = useOverdueInvoicesQuery({ enabled: IS_PLATFORM }) const projectsByOrg = groupBy(allProjects, 'organization_id') const isLoadingPermissions = IS_PLATFORM ? _isLoadingPermissions : false @@ -52,6 +53,7 @@ const ProjectList = ({ rewriteHref }: ProjectListProps) => { overdueInvoices={(allOverdueInvoices ?? []).filter( (it) => it.organization_id === organization.id )} + resourceWarnings={resourceWarnings ?? []} rewriteHref={rewriteHref} isLoadingPermissions={isLoadingPermissions} isErrorPermissions={isErrorPermissions} @@ -72,6 +74,7 @@ type OrganizationProjectsProps = { organization: Organization projects: Project[] overdueInvoices: OverdueInvoicesResponse[] + resourceWarnings: ResourceWarning[] isLoadingPermissions: boolean isErrorPermissions: boolean permissionsError: ResponseError | null @@ -82,9 +85,10 @@ type OrganizationProjectsProps = { } const OrganizationProjects = ({ - organization: { id, name, slug, subscription_id }, + organization: { id, name, slug }, projects, overdueInvoices, + resourceWarnings, isLoadingPermissions, isErrorPermissions, permissionsError, @@ -95,10 +99,6 @@ const OrganizationProjects = ({ }: OrganizationProjectsProps) => { const isEmpty = !projects || projects.length === 0 const canReadProjects = useCheckPermissions(PermissionAction.READ, 'projects', undefined, id) - const { data: resourceWarnings } = useResourceWarningQuery() - - console.log(resourceWarnings) - console.log('one render') return (
    @@ -166,7 +166,7 @@ const OrganizationProjects = ({ key={makeRandomString(5)} project={project} rewriteHref={rewriteHref ? rewriteHref(project.ref) : undefined} - resourceWarnings={resourceWarnings?.find( + resourceWarnings={resourceWarnings.find( (resourceWarning) => resourceWarning.project === project.ref )} /> diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 7a1cd41cb61..586022bfbec 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -18,7 +18,7 @@ import RestoringState from './RestoringState' import UpgradingState from './UpgradingState' import AppLayout from '../AppLayout/AppLayout' import UsageWarningBanner from 'components/ui/UsageWarnings/UsageWarningBanner' -import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' +import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' // [Joshen] This is temporary while we unblock users from managing their project // if their project is not responding well for any reason. Eventually needs a bit of an overhaul @@ -179,7 +179,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { selectedProject?.status === PROJECT_STATUS.PAUSING const isProjectOffline = selectedProject?.postgrestStatus === 'OFFLINE' - const { data: resourceWarnings } = useResourceWarningQuery() + const { data: resourceWarnings } = useResourceWarningsQuery() console.log(resourceWarnings) return ( <> diff --git a/studio/components/ui/CardButton.tsx b/studio/components/ui/CardButton.tsx index f374f6cef82..9c0690af6ac 100644 --- a/studio/components/ui/CardButton.tsx +++ b/studio/components/ui/CardButton.tsx @@ -27,7 +27,7 @@ const CardButton = ({ imgAlt, onClick, icon, - containerHeightClassName = 'h-48', + containerHeightClassName = '', loading = false, }: PropsWithChildren) => { const LinkContainer = ({ children }: { children: React.ReactNode }) => ( @@ -47,7 +47,7 @@ const CardButton = ({ 'group relative text-left', 'bg-panel-header-light dark:bg-panel-header-dark', 'border border-panel-border-light dark:border-panel-border-dark', - 'rounded-md py-4 px-6 flex flex-row', + 'rounded-md py-4 flex flex-row', 'transition ease-in-out duration-150', containerHeightClassName, ] diff --git a/studio/data/usage/keys.ts b/studio/data/usage/keys.ts index b5aca6f8298..54be56a1858 100644 --- a/studio/data/usage/keys.ts +++ b/studio/data/usage/keys.ts @@ -1,5 +1,5 @@ export const usageKeys = { usage: (projectRef: string | undefined) => ['projects', projectRef, 'usage'] as const, orgUsage: (orgSlug: string | undefined) => ['organizations', orgSlug, 'usage'] as const, - resourceWarnings: () => ['project', 'resource-warnings'] as const, + resourceWarnings: () => ['projects', 'resource-warnings'] as const, } diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index 82be8e7f1eb..19a92acbe8d 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -1,40 +1,26 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' -import { get, isResponseOk } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' -import { usageKeys } from './keys' -export type ResourceWarningResponse = { - project: string - is_readonly_mode_enabled: boolean - is_disk_io_budget_below_threshold: boolean - is_disk_space_usage_beyond_threshold: boolean - is_cpu_load_beyond_threshold: boolean - is_memory_and_swap_usage_beyond_threshold: boolean -} +import { get } from 'data/fetchers' +import { usageKeys } from './keys' +import { ResponseError } from 'types' +import { components } from 'data/api' export async function getResourceWarnings(signal?: AbortSignal) { - const response = await get(`${API_URL}/projects-resource-warnings`, { - signal, - }) - if (!isResponseOk(response)) { - throw response.error - } - - return response + const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) + if (error) throw error + return data } -export type ResourceWarningData = Awaited> -export type ResourceError = unknown +export type ResourceWarning = components['schemas']['ProjectResourceWarningsResponse'] +export type ResourceWarningsData = Awaited> +export type ResourceWarningsError = ResponseError -export const useResourceWarningQuery = ({ +export const useResourceWarningsQuery = ({ enabled = true, ...options -}: UseQueryOptions = {}) => - useQuery( +}: UseQueryOptions = {}) => + useQuery( usageKeys.resourceWarnings(), ({ signal }) => getResourceWarnings(signal), - { - enabled: enabled, - ...options, - } + { enabled, ...options } ) diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx index 727d5789606..06b56fd2a10 100644 --- a/studio/pages/project/[ref]/index.tsx +++ b/studio/pages/project/[ref]/index.tsx @@ -4,7 +4,6 @@ import ProjectUsageSection from 'components/interfaces/Home/ProjectUsageSection' import { ProjectLayoutWithAuth } from 'components/layouts' import ProjectPausedState from 'components/layouts/ProjectLayout/ProjectPausedState' import ProjectUpgradeFailedBanner from 'components/ui/ProjectUpgradeFailedBanner' -import { useResourceWarningQuery } from 'data/usage/resource-warnings-query' import { useSelectedProject } from 'hooks' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import { NextPageWithLayout } from 'types' @@ -17,9 +16,6 @@ const Home: NextPageWithLayout = () => { ? project?.name : 'Welcome to your project' - const { data: resourceWarnings } = useResourceWarningQuery() - - console.log(resourceWarnings) return (
    From 7115c81681711bea2618bb68d95896178157b08d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 23 Aug 2023 17:16:00 +0800 Subject: [PATCH 03/27] Update usage warning banner --- .../BillingV2/Usage/Infrastructure.tsx | 1 + .../Home/ProjectList/ProjectCardWarnings.tsx | 1 + .../layouts/ProjectLayout/ProjectLayout.tsx | 9 +- .../ui/UsageWarnings/UsageWarningBanner.tsx | 145 +++++++++++++++++- studio/data/usage/resource-warnings-query.ts | 6 +- 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index fdb0948c26c..f4314f551be 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -246,6 +246,7 @@ const Infrastructure = ({
    )} + {/* [Joshen TODO] Add the necessary alert warnings for CPU and RAM here as well */}
    {attribute.key === 'disk_io_consumption' ? ( diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index ec8f4af8e43..887230179ed 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -39,6 +39,7 @@ export default function ProjectCardWarnings({ resourceWarnings }: ProjectCardWar (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true ) const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') + // [Joshen] We should probably read readonly mode separately for multiple resource warnings, read only mode needs to take highest precedence return (
    diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 586022bfbec..37ad6281554 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -4,8 +4,10 @@ import { Fragment, PropsWithChildren, ReactNode } from 'react' import { useParams } from 'common/hooks' import Connecting from 'components/ui/Loading' +import UsageWarningBanner from 'components/ui/UsageWarnings/UsageWarningBanner' import { useFlag, useSelectedOrganization, useSelectedProject, withAuth } from 'hooks' -import { PROJECT_STATUS, IS_PLATFORM } from 'lib/constants' +import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' +import AppLayout from '../AppLayout/AppLayout' import BuildingState from './BuildingState' import ConnectingState from './ConnectingState' import LayoutHeader from './LayoutHeader' @@ -16,9 +18,6 @@ import { ProjectContextProvider } from './ProjectContext' import ProjectPausedState from './ProjectPausedState' import RestoringState from './RestoringState' import UpgradingState from './UpgradingState' -import AppLayout from '../AppLayout/AppLayout' -import UsageWarningBanner from 'components/ui/UsageWarnings/UsageWarningBanner' -import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' // [Joshen] This is temporary while we unblock users from managing their project // if their project is not responding well for any reason. Eventually needs a bit of an overhaul @@ -179,8 +178,6 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { selectedProject?.status === PROJECT_STATUS.PAUSING const isProjectOffline = selectedProject?.postgrestStatus === 'OFFLINE' - const { data: resourceWarnings } = useResourceWarningsQuery() - console.log(resourceWarnings) return ( <> {isLoading || (requiresProjectDetails && selectedProject === undefined) ? ( diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index ea70a140856..48656d4a197 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -1,16 +1,145 @@ import Link from 'next/link' -import React from 'react' - import { useParams } from 'common' -import { useSelectedOrganization } from 'hooks' +import { + AlertDescription_Shadcn_, + AlertTitle_Shadcn_, + Alert_Shadcn_, + Button, + IconAlertTriangle, + IconExternalLink, +} from 'ui' -export default function UsageWarningBanner() { +import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' + +// [TODO] Just double check if the docs url and correction urls are all correct +const RESOURCE_WARNING_MESSAGES = { + is_readonly_mode_enabled: { + title: 'Your project is currently in readonly mode and is no longer accepting write requests', + description: + 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', + correctionUrl: undefined, + buttonText: undefined, + }, + is_disk_io_budget_below_threshold: { + title: + 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', + description: + 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', + correctionUrl: '/project/[ref]/settings/billing/usage#disk_io', + buttonText: 'Check usage', + }, + is_disk_space_usage_beyond_threshold: { + title: + 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', + description: 'Some CTA description here', + docsUrl: undefined, + correctionUrl: undefined, + buttonText: undefined, + }, + is_cpu_load_beyond_threshold: { + title: + "Your project is currently facing high CPU usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', + correctionUrl: '/project/[ref]/settings/billing/usage#cpu', + buttonText: 'Check usage', + }, + is_memory_and_swap_usage_beyond_threshold: { + title: + "Your project is currently facing high memory usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', + correctionUrl: '/project/[ref]/settings/billing/usage#ram', + buttonText: 'Check usage', + }, + multiple_resource_warnings: { + title: + "Your project is currently exhausting multiple resources, and your instance's performance is affected", + description: "Check which resources are reaching their threshold on your project's usage page.", + docsUrl: undefined, + correctionUrl: '/project/[ref]/settings/billing/usage', + buttonText: 'Check usage', + }, +} + +const UsageWarningBanner = () => { const { ref } = useParams() - const organization = useSelectedOrganization() + const { data: resourceWarnings } = useResourceWarningsQuery() + const projectResourceWarnings = (resourceWarnings ?? [])?.find( + (warning) => warning.project === ref + ) + + const activeWarnings = + projectResourceWarnings !== undefined + ? Object.keys(projectResourceWarnings).filter( + (property) => + projectResourceWarnings[property as keyof typeof projectResourceWarnings] === true + ) + : [] + + const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') + + const title = + activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.title + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.title + const description = + activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.description + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.description + const learnMoreUrl = + activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.docsUrl + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.docsUrl + const correctionUrl = ( + activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.correctionUrl + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.correctionUrl + )?.replace('[ref]', ref ?? 'default') + const buttonText = + activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.buttonText + : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] + ?.buttonText + + if (activeWarnings.length === 0) return null return ( -
    - Project is experiencing high [product] usage. Learn more -
    + + + {title} + {description} +
    + {learnMoreUrl !== undefined && ( + + + + + + )} + {correctionUrl !== undefined && ( + + + + + + )} +
    +
    ) } + +export default UsageWarningBanner diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index 19a92acbe8d..d7ecd7c350e 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -22,5 +22,9 @@ export const useResourceWarningsQuery = ({ useQuery( usageKeys.resourceWarnings(), ({ signal }) => getResourceWarnings(signal), - { enabled, ...options } + { + enabled, + staleTime: 1000 * 60 * 30, // default 30 minutes + ...options, + } ) From 760500f7cd5f50a4c558cc238d5a56f6e879545b Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 23 Aug 2023 17:19:44 +0800 Subject: [PATCH 04/27] Remove all other queries that check project read only status --- .../Home/ProjectList/ProjectCard.tsx | 36 ++---- .../LayoutHeader/LayoutHeader.tsx | 22 +--- studio/data/config/project-read-only-query.ts | 60 ---------- .../projects/project-readonly-status-query.ts | 55 --------- .../projects-readonly-status-query.ts | 110 ------------------ studio/hooks/misc/useProjectReadOnlyStatus.ts | 9 -- 6 files changed, 11 insertions(+), 281 deletions(-) delete mode 100644 studio/data/config/project-read-only-query.ts delete mode 100644 studio/data/projects/project-readonly-status-query.ts delete mode 100644 studio/data/projects/projects-readonly-status-query.ts delete mode 100644 studio/hooks/misc/useProjectReadOnlyStatus.ts diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 68532417f5d..430de0ce0d3 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -8,7 +8,6 @@ import { } from 'ui' import CardButton from 'components/ui/CardButton' -import { useProjectReadOnlyStatus } from 'hooks/misc/useProjectReadOnlyStatus' import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' @@ -33,16 +32,10 @@ const ProjectCard = ({ const { name, ref: projectRef } = project const desc = `${project.cloud_provider} | ${project.region}` - const isReadonly = useProjectReadOnlyStatus(projectRef) - // const isBranchingEnabled = project.preview_branch_refs.length > 0 - // const isGithubIntegrated = githubIntegration !== undefined - // const isVercelIntegrated = vercelIntegration !== undefined - // const githubRepository = githubIntegration?.metadata.name ?? undefined - - const isBranchingEnabled = true - const isGithubIntegrated = true - const isVercelIntegrated = true - const githubRepository = 'supabase/supabase-js' + const isBranchingEnabled = project.preview_branch_refs.length > 0 + const isGithubIntegrated = githubIntegration !== undefined + const isVercelIntegrated = vercelIntegration !== undefined + const githubRepository = githubIntegration?.metadata.name ?? undefined // Project status should supersede its read only status const isHealthy = project.status === PROJECT_STATUS.ACTIVE_HEALTHY @@ -98,22 +91,11 @@ const ProjectCard = ({ {desc} {isHealthy && ( - <> - {isReadonly ? ( - -
    - - Read-only mode -
    -
    - ) : ( - -
    - Active -
    -
    - )} - + +
    + Active +
    +
    )} {isRestoring && ( diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index cfd42ae8d16..96924e3d888 100644 --- a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -3,7 +3,6 @@ import Link from 'next/link' import { Badge } from 'ui' import { getResourcesExceededLimits } from 'components/ui/OveragesBanner/OveragesBanner.utils' -import { useProjectReadOnlyQuery } from 'data/config/project-read-only-query' import { useProjectUsageQuery } from 'data/usage/project-usage-query' import { useFlag, useSelectedOrganization, useSelectedProject } from 'hooks' import { IS_PLATFORM } from 'lib/constants' @@ -16,14 +15,9 @@ import ProjectDropdown from './ProjectDropdown' import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = true }: any) => { - const selectedOrganization = useSelectedOrganization() - const selectedProject = useSelectedProject() - const { ref: projectRef } = useParams() - const { data: isReadOnlyMode } = useProjectReadOnlyQuery({ - projectRef: selectedProject?.ref, - connectionString: selectedProject?.connectionString, - }) + const selectedProject = useSelectedProject() + const selectedOrganization = useSelectedOrganization() // Skip with org-level-billing, as quota is for the entire org const { data: usage } = useProjectUsageQuery( @@ -81,18 +75,6 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = {/* Project Dropdown */} - {/* [Terry] Temporary until we figure out how we want to display this permanently */} - {/* context: https://www.notion.so/supabase/DB-Disk-Size-Free-tier-Read-only-Critical-f2b8937c13a149e3ac769fe5888f6db0*/} - {isReadOnlyMode && ( - - )} - {showOverUsageBadge && (
    diff --git a/studio/data/config/project-read-only-query.ts b/studio/data/config/project-read-only-query.ts deleted file mode 100644 index ebbdbe55768..00000000000 --- a/studio/data/config/project-read-only-query.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { UseQueryOptions } from '@tanstack/react-query' -import { useCallback } from 'react' -import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query' - -// TODO: temporary solution to check if project is in read only mode -// until we get an api endpoint for this - -export const getProjectReadOnlySql = () => { - const sql = /* SQL */ ` - show default_transaction_read_only; - ` - - return sql -} - -export type ProjectReadOnlyVariables = { - projectRef?: string - connectionString?: string -} - -export type ProjectReadOnlyData = boolean -export type ProjectReadOnlyError = unknown - -export const useProjectReadOnlyQuery = ( - { projectRef, connectionString }: ProjectReadOnlyVariables, - options: Omit< - UseQueryOptions, - 'select' - > = {} -) => - useExecuteSqlQuery( - { - projectRef, - connectionString, - sql: getProjectReadOnlySql(), - queryKey: ['project-read-only'], - }, - { - select(data) { - return data.result[0]?.default_transaction_read_only === 'on' - }, - enabled: typeof projectRef !== 'undefined' && typeof connectionString !== 'undefined', - ...options, - } - ) - -export const useProjectReadOnlyPrefetch = () => { - const prefetch = useExecuteSqlPrefetch() - - return useCallback( - ({ projectRef, connectionString }: ProjectReadOnlyVariables) => - prefetch({ - projectRef, - connectionString, - sql: getProjectReadOnlySql(), - queryKey: ['project-read-only'], - }), - [prefetch] - ) -} diff --git a/studio/data/projects/project-readonly-status-query.ts b/studio/data/projects/project-readonly-status-query.ts deleted file mode 100644 index bcd6274f02c..00000000000 --- a/studio/data/projects/project-readonly-status-query.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { get } from 'lib/common/fetch' -import { API_ADMIN_URL } from 'lib/constants' -import { useCallback } from 'react' -import { projectKeys } from './keys' - -export type ProjectReadOnlyStatusVariables = { projectRef?: string } -export type ProjectReadOnlyStatus = { - enabled: boolean - override_enabled: boolean - override_active_until: string -} - -export async function getProjectReadOnlyStatus( - { projectRef }: ProjectReadOnlyStatusVariables, - signal?: AbortSignal -) { - if (!projectRef) throw new Error('Project ref is required') - const data = await get(`${API_ADMIN_URL}/projects/${projectRef}/readonly`, { signal }) - if (data.error) throw data.error - return data as ProjectReadOnlyStatus -} - -export type ProjectReadOnlyStatusData = Awaited> -export type ProjectReadOnlyStatusError = unknown - -export const useProjectReadOnlyStatusQuery = ( - { projectRef }: ProjectReadOnlyStatusVariables, - { - enabled = true, - ...options - }: UseQueryOptions = {} -) => - useQuery( - projectKeys.readonlyStatus(projectRef), - ({ signal }) => getProjectReadOnlyStatus({ projectRef }, signal), - { - enabled: enabled && typeof projectRef !== 'undefined', - ...options, - } - ) - -export const useProjectReadOnlyStatusPrefetch = ({ - projectRef, -}: ProjectReadOnlyStatusVariables) => { - const client = useQueryClient() - - return useCallback(() => { - if (projectRef) { - client.prefetchQuery(projectKeys.readonlyStatus(projectRef), ({ signal }) => - getProjectReadOnlyStatus({ projectRef }, signal) - ) - } - }, [projectRef]) -} diff --git a/studio/data/projects/projects-readonly-status-query.ts b/studio/data/projects/projects-readonly-status-query.ts deleted file mode 100644 index 0cae487cb70..00000000000 --- a/studio/data/projects/projects-readonly-status-query.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { QueryClient, useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query' -import { useCallback, useRef } from 'react' - -import { get } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' -import { Project, ResponseError } from 'types' -import { projectKeys } from './keys' - -export type ProjectsReadonlyStatusesVariables = { - ref?: string -} - -export async function getProjectsReadonlyStatuses(signal?: AbortSignal) { - const data = await get(`${API_URL}/projects?include_readonly_mode_status=true`, { signal }) - if (data.error) throw data.error - - return Object.fromEntries( - data.map((project: any) => [project.ref, project.is_readonly_mode_enabled]) - ) as { - [k: string]: boolean - } -} - -export type ProjectsReadonlyStatusesData = Awaited> -export type ProjectsReadonlyStatusesError = ResponseError - -export const useProjectsReadonlyStatusesQuery = ({ - enabled = true, - ...options -}: UseQueryOptions = {}) => - useQuery( - projectKeys.readonlyStatusList(), - ({ signal }) => getProjectsReadonlyStatuses(signal), - { enabled: enabled, ...options } - ) - -export function prefetchProjectsReadonlyStatuses(client: QueryClient) { - return client.prefetchQuery(projectKeys.readonlyStatusList(), ({ signal }) => - getProjectsReadonlyStatuses(signal) - ) -} - -export function useProjectsReadonlyStatusesPrefetch() { - const client = useQueryClient() - - return useCallback(() => { - prefetchProjectsReadonlyStatuses(client) - }, [client]) -} - -export function useAutoProjectsReadonlyStatusesPrefetch() { - const prefetch = useProjectsReadonlyStatusesPrefetch() - - const called = useRef(false) - if (called.current === false) { - called.current = true - prefetch() - } -} - -export function invalidateProjectsReadonlyStatusesQuery(client: QueryClient) { - return client.invalidateQueries(projectKeys.readonlyStatusList()) -} - -export function setProjectStatus( - client: QueryClient, - projectRef: Project['ref'], - status: Project['status'] -) { - client.setQueriesData( - projectKeys.readonlyStatusList(), - (old) => { - if (!old) return old - - return old.map((project) => { - if (project.ref === projectRef) { - return { ...project, status } - } - return project - }) - }, - { updatedAt: Date.now() } - ) - - client.setQueriesData( - projectKeys.detail(projectRef), - (old) => { - if (!old) return old - - return { ...old, status } - }, - { updatedAt: Date.now() } - ) -} - -export function setProjectPostgrestStatus( - client: QueryClient, - projectRef: Project['ref'], - status: Project['postgrestStatus'] -) { - client.setQueriesData( - projectKeys.detail(projectRef), - (old) => { - if (!old) return old - - return { ...old, postgrestStatus: status } - }, - { updatedAt: Date.now() } - ) -} diff --git a/studio/hooks/misc/useProjectReadOnlyStatus.ts b/studio/hooks/misc/useProjectReadOnlyStatus.ts deleted file mode 100644 index 4c889b54528..00000000000 --- a/studio/hooks/misc/useProjectReadOnlyStatus.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { useProjectsReadonlyStatusesQuery } from 'data/projects/projects-readonly-status-query' - -export function useProjectReadOnlyStatus(projectRef?: string) { - const { data } = useProjectsReadonlyStatusesQuery() - - if (!projectRef) return false - - return data?.[projectRef] ?? false -} From 68a305201d7ce3d69980fc2995b4d9ef4bca7cef Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 23 Aug 2023 17:45:18 +0800 Subject: [PATCH 05/27] Read only error takes higher precendence over multiple resource warnings --- .../Home/ProjectList/ProjectCardWarnings.tsx | 14 +++++++++----- .../ui/UsageWarnings/UsageWarningBanner.tsx | 11 +++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index 887230179ed..5453f2df74b 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -34,12 +34,14 @@ const RESOURCE_WARNING_MESSAGES = { }, } -export default function ProjectCardWarnings({ resourceWarnings }: ProjectCardWarningsProps) { - const activeWarnings = Object.keys(resourceWarnings).filter( - (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true - ) +const ProjectCardWarnings = ({ resourceWarnings }: ProjectCardWarningsProps) => { + // [Joshen] Read only takes higher precendence over multiple resource warnings + const activeWarnings = resourceWarnings.is_readonly_mode_enabled + ? ['is_readonly_mode_enabled'] + : Object.keys(resourceWarnings).filter( + (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true + ) const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') - // [Joshen] We should probably read readonly mode separately for multiple resource warnings, read only mode needs to take highest precedence return (
    @@ -66,3 +68,5 @@ export default function ProjectCardWarnings({ resourceWarnings }: ProjectCardWar
    ) } + +export default ProjectCardWarnings diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index 48656d4a197..aab7bd51390 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -73,12 +73,15 @@ const UsageWarningBanner = () => { (warning) => warning.project === ref ) + // [Joshen] Read only takes higher precendence over multiple resource warnings const activeWarnings = projectResourceWarnings !== undefined - ? Object.keys(projectResourceWarnings).filter( - (property) => - projectResourceWarnings[property as keyof typeof projectResourceWarnings] === true - ) + ? projectResourceWarnings.is_readonly_mode_enabled + ? ['is_readonly_mode_enabled'] + : Object.keys(projectResourceWarnings).filter( + (property) => + projectResourceWarnings[property as keyof typeof projectResourceWarnings] === true + ) : [] const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') From 1daa80e15c7bc32cf1ddddfa9c1f2d6c8589d292 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Mon, 28 Aug 2023 16:21:44 +0800 Subject: [PATCH 06/27] Scaffold warnings in usage project usage page for CPU and RAM --- .../BillingV2/Usage/Infrastructure.tsx | 107 ++++-------------- .../interfaces/BillingV2/Usage/Usage.tsx | 9 +- .../Usage/UsageWarningAlerts/CPUWarnings.tsx | 74 ++++++++++++ .../DiskIOBandwidthWarnings.tsx | 106 +++++++++++++++++ .../Usage/UsageWarningAlerts/RAMWarnings.tsx | 74 ++++++++++++ .../Usage/UsageWarningAlerts/index.ts | 5 + .../Home/ProjectList/ProjectCardWarnings.tsx | 2 +- .../layouts/ProjectLayout/BuildingState.tsx | 15 ++- .../layouts/ProjectLayout/ProjectLayout.tsx | 2 +- .../UsageWarningBanner.constants.ts | 54 +++++++++ .../ui/UsageWarnings/UsageWarningBanner.tsx | 62 +--------- 11 files changed, 355 insertions(+), 155 deletions(-) create mode 100644 studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx create mode 100644 studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx create mode 100644 studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx create mode 100644 studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/index.ts create mode 100644 studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index f4314f551be..60d929e28fb 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -1,18 +1,19 @@ +import dayjs from 'dayjs' +import { IconBarChart2 } from 'ui' + +import Panel from 'components/ui/Panel' import ShimmeringLoader from 'components/ui/ShimmeringLoader' import { DataPoint } from 'data/analytics/constants' import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query' -import dayjs from 'dayjs' -import Link from 'next/link' -import { Alert, Button, IconBarChart2 } from 'ui' +import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' +import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' +import { getAddons } from '../Subscription/Subscription.utils' import SectionContent from './SectionContent' import SectionHeader from './SectionHeader' import { USAGE_CATEGORIES } from './Usage.constants' import { getUpgradeUrl } from './Usage.utils' import UsageBarChart from './UsageBarChart' -import Panel from 'components/ui/Panel' -import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' -import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' -import { getAddons } from '../Subscription/Subscription.utils' +import { CPUWarnings, DiskIOBandwidthWarnings, RAMWarnings } from './UsageWarningAlerts' export interface InfrastructureProps { projectRef: string @@ -28,7 +29,6 @@ const Infrastructure = ({ currentBillingCycleSelected, }: InfrastructureProps) => { const { data: subscription } = useProjectSubscriptionV2Query({ projectRef }) - const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === 'infra') const upgradeUrl = getUpgradeUrl(projectRef, subscription) @@ -125,81 +125,14 @@ const Infrastructure = ({ {attribute.key === 'disk_io_consumption' && ( <> - {hasLatest && latestIoBudgetConsumption >= 100 ? ( - -

    - Your workload has used up all your Disk IO Budget and is now running at the - baseline performance. If you need consistent disk performance, consider - upgrading to a larger compute add-on. -

    - - - - - -
    - ) : hasLatest && latestIoBudgetConsumption >= 80 ? ( - -

    - Your workload has consumed {latestIoBudgetConsumption}% of your Disk IO - Budget. If you use up all your Disk IO Budget, your instance will reverted - to baseline performance. If you need consistent disk performance, consider - upgrading to a larger compute add-on. -

    - - - - - -
    - ) : currentBillingCycleSelected && highestIoBudgetConsumption >= 100 ? ( - -

    - Your workload has used up all your Disk IO Budget and reverted to baseline - performance at least once during this billing cycle. If you need consistent - disk performance, consider upgrading to a larger compute add-on. -

    - - - - - -
    - ) : currentBillingCycleSelected && highestIoBudgetConsumption >= 80 ? ( - -

    - Your workload has consumed {highestIoBudgetConsumption}% of your Disk IO - budget during this billing cycle. If you use up all your Disk IO Budget, - your instance will reverted to baseline performance. If you need consistent - disk performance, consider upgrading to a larger compute add-on. -

    - - - - - -
    - ) : null} +

    Disk IO Bandwidth

    @@ -246,7 +179,13 @@ const Infrastructure = ({
    )} - {/* [Joshen TODO] Add the necessary alert warnings for CPU and RAM here as well */} + {attribute.key === 'max_cpu_usage' && ( + + )} + {attribute.key === 'ram_usage' && ( + + )} +
    {attribute.key === 'disk_io_consumption' ? ( diff --git a/studio/components/interfaces/BillingV2/Usage/Usage.tsx b/studio/components/interfaces/BillingV2/Usage/Usage.tsx index cd61a2d5889..456e3de8db6 100644 --- a/studio/components/interfaces/BillingV2/Usage/Usage.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Usage.tsx @@ -1,15 +1,16 @@ import clsx from 'clsx' import { useParams } from 'common' +import dayjs from 'dayjs' +import { useMemo, useRef, useState } from 'react' +import { InView } from 'react-intersection-observer' +import { IconAlertCircle, IconLoader, cn } from 'ui' + import DateRangePicker from 'components/to-be-cleaned/DateRangePicker' import { useInfraMonitoringQuery } from 'data/analytics/infra-monitoring-query' import { useProjectSubscriptionV2Query } from 'data/subscriptions/project-subscription-v2-query' import { useProjectUsageQuery } from 'data/usage/project-usage-query' -import dayjs from 'dayjs' import { useSelectedOrganization } from 'hooks' import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants' -import { useMemo, useRef, useState } from 'react' -import { InView } from 'react-intersection-observer' -import { IconAlertCircle, IconLoader, cn } from 'ui' import Activity from './Activity' import Bandwidth from './Bandwidth' import Infrastructure from './Infrastructure' diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx new file mode 100644 index 00000000000..fe6a94f102d --- /dev/null +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx @@ -0,0 +1,74 @@ +import { AlertTitle } from '@ui/components/shadcn/ui/alert' +import Link from 'next/link' +import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from 'ui' + +interface CPUWarningsProps { + isFreeTier: boolean + upgradeUrl: string +} + +const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { + // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings + const isApproaching = false + const isExceeded = false + + if (isApproaching) { + return ( + + + Your max CPU usage has exceeded 80% + + High CPU usage could result in slower queries, disruption of daily back routines, and in + rare cases, your instance may become unresponsive. If you need more resources, consider + upgrading to a larger compute add-on. + + + + ) + } + + if (isExceeded) { + return ( + + + Your max CPU usage has reached 100% + + High CPU usage could result in slower queries, disruption of daily back routines, and in + rare cases, your instance may become unresponsive. If you need more resources, consider + upgrading to a larger compute add-on. + + + + ) + } + + return null +} + +export default CPUWarnings diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx new file mode 100644 index 00000000000..0d092e75878 --- /dev/null +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx @@ -0,0 +1,106 @@ +import Link from 'next/link' +import { Alert, Button } from 'ui' + +// [Joshen] In the future, conditionals should be from resource exhaustion endpoint as single source of truth +interface DiskIOBandwidthWarningsProps { + isFreeTier: boolean + hasLatest: boolean + upgradeUrl: string + currentBillingCycleSelected: boolean + latestIoBudgetConsumption: number + highestIoBudgetConsumption: number +} + +const DiskIOBandwidthWarnings = ({ + isFreeTier, + hasLatest, + currentBillingCycleSelected, + upgradeUrl, + latestIoBudgetConsumption, + highestIoBudgetConsumption, +}: DiskIOBandwidthWarningsProps) => { + if (hasLatest && latestIoBudgetConsumption >= 100) { + return ( + +

    + Your workload has used up all your Disk IO Budget and is now running at the baseline + performance. If you need consistent disk performance, consider upgrading to a larger + compute add-on. +

    + + + + + +
    + ) + } + + if (hasLatest && latestIoBudgetConsumption >= 80) { + return ( + +

    + Your workload has consumed {latestIoBudgetConsumption}% of your Disk IO Budget. If you use + up all your Disk IO Budget, your instance will reverted to baseline performance. If you + need consistent disk performance, consider upgrading to a larger compute add-on. +

    + + + + + +
    + ) + } + + if (currentBillingCycleSelected && highestIoBudgetConsumption >= 100) { + return ( + +

    + Your workload has used up all your Disk IO Budget and reverted to baseline performance at + least once during this billing cycle. If you need consistent disk performance, consider + upgrading to a larger compute add-on. +

    + + + + + +
    + ) + } + + if (currentBillingCycleSelected && highestIoBudgetConsumption >= 80) { + return ( + +

    + Your workload has consumed {highestIoBudgetConsumption}% of your Disk IO budget during + this billing cycle. If you use up all your Disk IO Budget, your instance will reverted to + baseline performance. If you need consistent disk performance, consider upgrading to a + larger compute add-on. +

    + + + + + +
    + ) + } + + return null +} + +export default DiskIOBandwidthWarnings diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx new file mode 100644 index 00000000000..0a106669311 --- /dev/null +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx @@ -0,0 +1,74 @@ +import { AlertTitle } from '@ui/components/shadcn/ui/alert' +import Link from 'next/link' +import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from 'ui' + +interface RAMWarningsProps { + isFreeTier: boolean + upgradeUrl: string +} + +// [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings +const RAMWarnings = ({ isFreeTier, upgradeUrl }: RAMWarningsProps) => { + const isApproaching = true + const isExceeded = false + + if (isApproaching) { + return ( + + + Your memory usage has exceeded 80% + + High memory usage could result in overall degraded performance, and in rare cases, your + instance may become unresponsive. If you need more resources, consider upgrading to a + larger compute add-on. + + + + ) + } + + if (isExceeded) { + return ( + + + Your memory usage has reached 100% + + High memory usage could result in overall degraded performance, and in rare cases, your + instance may become unresponsive. If you need more resources, consider upgrading to a + larger compute add-on. + + + + ) + } + + return null +} + +export default RAMWarnings diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/index.ts b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/index.ts new file mode 100644 index 00000000000..3b3afe3f30e --- /dev/null +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/index.ts @@ -0,0 +1,5 @@ +import CPUWarnings from './CPUWarnings' +import DiskIOBandwidthWarnings from './DiskIOBandwidthWarnings' +import RAMWarnings from './RAMWarnings' + +export { CPUWarnings, DiskIOBandwidthWarnings, RAMWarnings } diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index 5453f2df74b..a0d7b2bbd85 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -35,7 +35,7 @@ const RESOURCE_WARNING_MESSAGES = { } const ProjectCardWarnings = ({ resourceWarnings }: ProjectCardWarningsProps) => { - // [Joshen] Read only takes higher precendence over multiple resource warnings + // [Joshen] Read only takes higher precedence over multiple resource warnings const activeWarnings = resourceWarnings.is_readonly_mode_enabled ? ['is_readonly_mode_enabled'] : Object.keys(resourceWarnings).filter( diff --git a/studio/components/layouts/ProjectLayout/BuildingState.tsx b/studio/components/layouts/ProjectLayout/BuildingState.tsx index 42418ba43ab..733d04fbe93 100644 --- a/studio/components/layouts/ProjectLayout/BuildingState.tsx +++ b/studio/components/layouts/ProjectLayout/BuildingState.tsx @@ -6,20 +6,17 @@ import Link from 'next/link' import { useEffect, useRef } from 'react' import { Badge, Button, IconArrowRight, IconLoader } from 'ui' +import { useParams } from 'common' import { DisplayApiSettings, DisplayConfigSettings } from 'components/ui/ProjectSettings' +import { invalidateProjectDetailsQuery } from 'data/projects/project-detail-query' import { invalidateProjectsQuery } from 'data/projects/projects-query' +import { useSelectedProject } from 'hooks' import { getWithTimeout } from 'lib/common/fetch' import { API_URL, PROJECT_STATUS } from 'lib/constants' -import { Project } from 'types' -import { invalidateProjectDetailsQuery } from 'data/projects/project-detail-query' -import { useParams } from 'common' -export interface BuildingStateProps { - project: Project -} - -const BuildingState = ({ project }: BuildingStateProps) => { +const BuildingState = () => { const { ref } = useParams() + const project = useSelectedProject() const queryClient = useQueryClient() const checkServerInterval = useRef() @@ -48,6 +45,8 @@ const BuildingState = ({ project }: BuildingStateProps) => { } }, []) + if (project === undefined) return null + return (
    diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index 37ad6281554..adeba22da72 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -191,7 +191,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { ) : requiresDbConnection && isProjectRestoring ? ( ) : requiresDbConnection && isProjectBuilding ? ( - + ) : ( <> diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts new file mode 100644 index 00000000000..2a6ca960637 --- /dev/null +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts @@ -0,0 +1,54 @@ +// [TODO] Just double check if the docs url and correction urls are all correct +export const RESOURCE_WARNING_MESSAGES = { + is_readonly_mode_enabled: { + title: 'Your project is currently in readonly mode and is no longer accepting write requests', + description: + 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', + correctionUrl: undefined, + buttonText: undefined, + }, + is_disk_io_budget_below_threshold: { + title: + 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', + description: + 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', + correctionUrl: '/project/[ref]/settings/billing/usage#disk_io', + buttonText: 'Check usage', + }, + is_disk_space_usage_beyond_threshold: { + title: + 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', + description: 'Some CTA description here', + docsUrl: undefined, + correctionUrl: undefined, + buttonText: undefined, + }, + is_cpu_load_beyond_threshold: { + title: + "Your project is currently facing high CPU usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', + correctionUrl: '/project/[ref]/settings/billing/usage#cpu', + buttonText: 'Check usage', + }, + is_memory_and_swap_usage_beyond_threshold: { + title: + "Your project is currently facing high memory usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', + correctionUrl: '/project/[ref]/settings/billing/usage#ram', + buttonText: 'Check usage', + }, + multiple_resource_warnings: { + title: + "Your project is currently exhausting multiple resources, and your instance's performance is affected", + description: "Check which resources are reaching their threshold on your project's usage page.", + docsUrl: undefined, + correctionUrl: '/project/[ref]/settings/billing/usage', + buttonText: 'Check usage', + }, +} diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index aab7bd51390..c3672308ae0 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -10,70 +10,18 @@ import { } from 'ui' import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' - -// [TODO] Just double check if the docs url and correction urls are all correct -const RESOURCE_WARNING_MESSAGES = { - is_readonly_mode_enabled: { - title: 'Your project is currently in readonly mode and is no longer accepting write requests', - description: - 'You will need to manually override read-only mode and reduce the disk size to below 95%.', - docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', - correctionUrl: undefined, - buttonText: undefined, - }, - is_disk_io_budget_below_threshold: { - title: - 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', - description: - 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', - docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', - correctionUrl: '/project/[ref]/settings/billing/usage#disk_io', - buttonText: 'Check usage', - }, - is_disk_space_usage_beyond_threshold: { - title: - 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', - description: 'Some CTA description here', - docsUrl: undefined, - correctionUrl: undefined, - buttonText: undefined, - }, - is_cpu_load_beyond_threshold: { - title: - "Your project is currently facing high CPU usage, and your instance's performance is affected", - description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', - docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', - correctionUrl: '/project/[ref]/settings/billing/usage#cpu', - buttonText: 'Check usage', - }, - is_memory_and_swap_usage_beyond_threshold: { - title: - "Your project is currently facing high memory usage, and your instance's performance is affected", - description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', - docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', - correctionUrl: '/project/[ref]/settings/billing/usage#ram', - buttonText: 'Check usage', - }, - multiple_resource_warnings: { - title: - "Your project is currently exhausting multiple resources, and your instance's performance is affected", - description: "Check which resources are reaching their threshold on your project's usage page.", - docsUrl: undefined, - correctionUrl: '/project/[ref]/settings/billing/usage', - buttonText: 'Check usage', - }, -} +import { RESOURCE_WARNING_MESSAGES } from './UsageWarningBanner.constants' +import { useRouter } from 'next/router' const UsageWarningBanner = () => { const { ref } = useParams() + const router = useRouter() const { data: resourceWarnings } = useResourceWarningsQuery() const projectResourceWarnings = (resourceWarnings ?? [])?.find( (warning) => warning.project === ref ) - // [Joshen] Read only takes higher precendence over multiple resource warnings + // [Joshen] Read only takes higher precedence over multiple resource warnings const activeWarnings = projectResourceWarnings !== undefined ? projectResourceWarnings.is_readonly_mode_enabled @@ -113,7 +61,7 @@ const UsageWarningBanner = () => { : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] ?.buttonText - if (activeWarnings.length === 0) return null + if (activeWarnings.length === 0 || router.pathname.includes('/usage')) return null return ( Date: Mon, 28 Aug 2023 17:09:42 +0800 Subject: [PATCH 07/27] Add warnings for CPU and RAM in Infrastructure and INfrastructureActivity, and fix link from UsageWarningBanner chekc usage CTA --- .../BillingV2/Subscription/AddOns/AddOns.tsx | 4 +- .../BillingV2/Usage/Infrastructure.tsx | 21 ++- .../Usage/UsageWarningAlerts/CPUWarnings.tsx | 22 ++-- .../DiskIOBandwidthWarnings.tsx | 12 +- .../Usage/UsageWarningAlerts/RAMWarnings.tsx | 14 +- .../interfaces/Settings/Addons/Addons.tsx | 20 +-- .../Infrastructure/InfrastructureActivity.tsx | 123 ++++++++++-------- .../UsageWarningBanner.constants.ts | 13 +- .../ui/UsageWarnings/UsageWarningBanner.tsx | 30 ++++- 9 files changed, 158 insertions(+), 101 deletions(-) diff --git a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx index 3d7c6d34e0d..444317ed056 100644 --- a/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx +++ b/studio/components/interfaces/BillingV2/Subscription/AddOns/AddOns.tsx @@ -22,9 +22,7 @@ import { } from 'components/layouts/ProjectLayout/ProjectContext' import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' -export interface AddOnsProps {} - -const AddOns = ({}: AddOnsProps) => { +const AddOns = () => { const { ref: projectRef } = useParams() const snap = useSubscriptionPageStateSnapshot() const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate') diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index 60d929e28fb..2789f1e139b 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -14,6 +14,7 @@ import { USAGE_CATEGORIES } from './Usage.constants' import { getUpgradeUrl } from './Usage.utils' import UsageBarChart from './UsageBarChart' import { CPUWarnings, DiskIOBandwidthWarnings, RAMWarnings } from './UsageWarningAlerts' +import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' export interface InfrastructureProps { projectRef: string @@ -29,10 +30,12 @@ const Infrastructure = ({ currentBillingCycleSelected, }: InfrastructureProps) => { const { data: subscription } = useProjectSubscriptionV2Query({ projectRef }) + const { data: resourceWarnings } = useResourceWarningsQuery() + const projectResourceWarnings = resourceWarnings?.find((x) => x.project === projectRef) const categoryMeta = USAGE_CATEGORIES.find((category) => category.key === 'infra') const upgradeUrl = getUpgradeUrl(projectRef, subscription) - const isFreeTier = subscription?.plan?.id === 'free' + const isFreePlan = subscription?.plan?.id === 'free' const { data: addons, isLoading } = useProjectAddonsQuery({ projectRef }) const selectedAddons = addons?.selected_addons ?? [] @@ -127,7 +130,7 @@ const Infrastructure = ({ <> )} {attribute.key === 'max_cpu_usage' && ( - + )} {attribute.key === 'ram_usage' && ( - + )}
    diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx index fe6a94f102d..818e649f4ac 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx @@ -3,13 +3,15 @@ import Link from 'next/link' import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from 'ui' interface CPUWarningsProps { - isFreeTier: boolean + isFreePlan: boolean upgradeUrl: string + isBeyondThreshold: boolean } -const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { - // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings - const isApproaching = false +const CPUWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: CPUWarningsProps) => { + // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings once + // endpoint is updated to return text instead of boolean + const isApproaching = isBeyondThreshold const isExceeded = false if (isApproaching) { @@ -18,8 +20,8 @@ const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { Your max CPU usage has exceeded 80% - High CPU usage could result in slower queries, disruption of daily back routines, and in - rare cases, your instance may become unresponsive. If you need more resources, consider + High CPU usage could result in slower queries, disruption of daily back up routines, and + in rare cases, your instance may become unresponsive. If you need more resources, consider upgrading to a larger compute add-on.
    @@ -31,7 +33,7 @@ const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { @@ -46,8 +48,8 @@ const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { Your max CPU usage has reached 100% - High CPU usage could result in slower queries, disruption of daily back routines, and in - rare cases, your instance may become unresponsive. If you need more resources, consider + High CPU usage could result in slower queries, disruption of daily back up routines, and + in rare cases, your instance may become unresponsive. If you need more resources, consider upgrading to a larger compute add-on.
    @@ -59,7 +61,7 @@ const CPUWarnings = ({ isFreeTier, upgradeUrl }: CPUWarningsProps) => { diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx index 0d092e75878..6d257ac3f10 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/DiskIOBandwidthWarnings.tsx @@ -3,7 +3,7 @@ import { Alert, Button } from 'ui' // [Joshen] In the future, conditionals should be from resource exhaustion endpoint as single source of truth interface DiskIOBandwidthWarningsProps { - isFreeTier: boolean + isFreePlan: boolean hasLatest: boolean upgradeUrl: string currentBillingCycleSelected: boolean @@ -12,7 +12,7 @@ interface DiskIOBandwidthWarningsProps { } const DiskIOBandwidthWarnings = ({ - isFreeTier, + isFreePlan, hasLatest, currentBillingCycleSelected, upgradeUrl, @@ -30,7 +30,7 @@ const DiskIOBandwidthWarnings = ({ @@ -49,7 +49,7 @@ const DiskIOBandwidthWarnings = ({ @@ -68,7 +68,7 @@ const DiskIOBandwidthWarnings = ({ @@ -92,7 +92,7 @@ const DiskIOBandwidthWarnings = ({ diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx index 0a106669311..8006c3d079d 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx @@ -3,13 +3,15 @@ import Link from 'next/link' import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from 'ui' interface RAMWarningsProps { - isFreeTier: boolean + isFreePlan: boolean upgradeUrl: string + isBeyondThreshold: boolean } -// [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings -const RAMWarnings = ({ isFreeTier, upgradeUrl }: RAMWarningsProps) => { - const isApproaching = true +const RAMWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: RAMWarningsProps) => { + // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings once + // endpoint is updated to return text instead of boolean + const isApproaching = isBeyondThreshold const isExceeded = false if (isApproaching) { @@ -31,7 +33,7 @@ const RAMWarnings = ({ isFreeTier, upgradeUrl }: RAMWarningsProps) => { @@ -59,7 +61,7 @@ const RAMWarnings = ({ isFreeTier, upgradeUrl }: RAMWarningsProps) => { diff --git a/studio/components/interfaces/Settings/Addons/Addons.tsx b/studio/components/interfaces/Settings/Addons/Addons.tsx index 88035a87b70..edff68aac31 100644 --- a/studio/components/interfaces/Settings/Addons/Addons.tsx +++ b/studio/components/interfaces/Settings/Addons/Addons.tsx @@ -134,14 +134,14 @@ const Addons = () => {
    -

    Optimized compute

    +

    Optimized compute

    -

    More information

    +

    More information

    -

    About compute add-ons

    +

    About compute add-ons

    @@ -151,7 +151,7 @@ const Addons = () => {
    -

    Connection Pooler

    +

    Connection Pooler

    @@ -339,14 +339,14 @@ const Addons = () => {
    -

    Point in time recovery

    +

    Point in time recovery

    -

    More information

    +

    More information

    -

    About PITR backups

    +

    About PITR backups

    @@ -403,14 +403,14 @@ const Addons = () => {
    -

    Custom domain

    +

    Custom domain

    -

    More information

    +

    More information

    -

    About custom domains

    +

    About custom domains

    diff --git a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index caacdeee269..28e6dce7415 100644 --- a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -1,8 +1,16 @@ +import { useParams } from 'common' import dayjs from 'dayjs' import Link from 'next/link' import { Fragment, useMemo, useState } from 'react' +import { IconBarChart2, IconExternalLink } from 'ui' -import { useParams } from 'common' +import { getAddons } from 'components/interfaces/BillingV2/Subscription/Subscription.utils' +import { + CPUWarnings, + DiskIOBandwidthWarnings, + RAMWarnings, +} from 'components/interfaces/BillingV2/Usage/UsageWarningAlerts' +import UsageBarChart from 'components/interfaces/Organization/Usage/UsageBarChart' import { ScaffoldContainer, ScaffoldDivider, @@ -19,10 +27,8 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useSelectedOrganization } from 'hooks' import { TIME_PERIODS_BILLING, TIME_PERIODS_REPORTS } from 'lib/constants' -import { Alert, Button, IconBarChart2, IconExternalLink } from 'ui' -import { getAddons } from '../../BillingV2/Subscription/Subscription.utils' -import UsageBarChart from '../../Organization/Usage/UsageBarChart' import { INFRA_ACTIVITY_METRICS } from './Infrastructure.constants' +import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' const InfrastructureActivity = () => { const { ref: projectRef } = useParams() @@ -30,17 +36,17 @@ const InfrastructureActivity = () => { const isOrgBilling = !!organization?.subscription_id const [dateRange, setDateRange] = useState() - const currentBillingCycleSelected = true - const upgradeUrl = '/' - const categoryMeta = INFRA_ACTIVITY_METRICS.find((category) => category.key === 'infra') - const { data: subscription, isLoading: isLoadingSubscription } = useOrgSubscriptionQuery({ orgSlug: organization?.slug, }) const isFreePlan = subscription?.plan?.id === 'free' + const { data: resourceWarnings } = useResourceWarningsQuery() + const projectResourceWarnings = resourceWarnings?.find((x) => x.project === projectRef) + const { data: addons, isLoading } = useProjectAddonsQuery({ projectRef }) const selectedAddons = addons?.selected_addons ?? [] + const { computeInstance } = getAddons(selectedAddons) const currentComputeInstanceSpecs = computeInstance?.variant?.meta ?? { baseline_disk_io_mbs: 87, @@ -50,6 +56,27 @@ const InfrastructureActivity = () => { memory_gb: 1, } + const currentBillingCycleSelected = useMemo(() => { + // Selected by default + if (!dateRange?.period_start || !dateRange?.period_end || !subscription) return true + + const { current_period_start, current_period_end } = subscription + + return ( + dayjs(dateRange.period_start.date).isSame(new Date(current_period_start * 1000)) && + dayjs(dateRange.period_end.date).isSame(new Date(current_period_end * 1000)) + ) + }, [dateRange, subscription]) + + const upgradeUrl = + subscription === undefined + ? `/` + : subscription.plan.id === 'free' + ? `/org/${organization?.slug ?? '[slug]'}/billing#subscription` + : `/project/${projectRef}/settings/addons` + + const categoryMeta = INFRA_ACTIVITY_METRICS.find((category) => category.key === 'infra') + const startDate = useMemo(() => { if (dateRange?.period_start?.date === 'Invalid Date') return undefined @@ -116,6 +143,13 @@ const InfrastructureActivity = () => { dateFormat, }) + const hasLatest = dayjs(endDate!).isAfter(dayjs().startOf('day')) + + const latestIoBudgetConsumption = + hasLatest && ioBudgetData?.data?.slice(-1)?.[0] + ? Number(ioBudgetData.data.slice(-1)[0].disk_io_consumption) + : 0 + const highestIoBudgetConsumption = Math.max( ...(ioBudgetData?.data || []).map((x) => Number(x.disk_io_consumption) ?? 0), 0 @@ -180,11 +214,11 @@ const InfrastructureActivity = () => {
    -

    {attribute.name}

    +

    {attribute.name}

    {attribute.description.split('\n').map((value, idx) => ( -

    +

    {value}

    ))} @@ -199,7 +233,7 @@ const InfrastructureActivity = () => {
    -

    {link.name}

    +

    {link.name}

    @@ -212,46 +246,14 @@ const InfrastructureActivity = () => { {attribute.key === 'disk_io_consumption' && ( <> - {currentBillingCycleSelected && highestIoBudgetConsumption >= 100 ? ( - -

    - Your workload has used up all the burst IO throughput minutes and ran at - the baseline performance. If you need consistent disk performance, - consider upgrading to a larger compute add-on. -

    - - - - - -
    - ) : currentBillingCycleSelected && highestIoBudgetConsumption >= 80 ? ( - -

    - Your workload is about to use up all the burst IO throughput minutes - during the day. Once this is completely used up, your workload will run - at the baseline performance. If you need consistent disk performance, - consider upgrading to a larger compute add-on. -

    - - - - - -
    - ) : null} +

    Disk IO Bandwidth

    @@ -302,6 +304,25 @@ const InfrastructureActivity = () => {
    )} + {attribute.key === 'max_cpu_usage' && ( + + )} + {attribute.key === 'ram_usage' && ( + + )} +
    {attribute.key === 'disk_io_consumption' ? ( diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts index 2a6ca960637..2ef96fafd7a 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts @@ -1,12 +1,13 @@ // [TODO] Just double check if the docs url and correction urls are all correct +// [TODO] Correction URL should depend if V2 or V1 billing export const RESOURCE_WARNING_MESSAGES = { is_readonly_mode_enabled: { title: 'Your project is currently in readonly mode and is no longer accepting write requests', description: 'You will need to manually override read-only mode and reduce the disk size to below 95%.', docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', - correctionUrl: undefined, buttonText: undefined, + metric: undefined, }, is_disk_io_budget_below_threshold: { title: @@ -14,16 +15,16 @@ export const RESOURCE_WARNING_MESSAGES = { description: 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', - correctionUrl: '/project/[ref]/settings/billing/usage#disk_io', buttonText: 'Check usage', + metric: 'disk_io', }, is_disk_space_usage_beyond_threshold: { title: 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', description: 'Some CTA description here', docsUrl: undefined, - correctionUrl: undefined, buttonText: undefined, + metric: undefined, }, is_cpu_load_beyond_threshold: { title: @@ -31,8 +32,8 @@ export const RESOURCE_WARNING_MESSAGES = { description: 'You will need to either optimize your performance or upgrade your compute to a larger instance', docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', - correctionUrl: '/project/[ref]/settings/billing/usage#cpu', buttonText: 'Check usage', + metric: 'cpu', }, is_memory_and_swap_usage_beyond_threshold: { title: @@ -40,15 +41,15 @@ export const RESOURCE_WARNING_MESSAGES = { description: 'You will need to either optimize your performance or upgrade your compute to a larger instance', docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', - correctionUrl: '/project/[ref]/settings/billing/usage#ram', buttonText: 'Check usage', + metric: 'ram', }, multiple_resource_warnings: { title: "Your project is currently exhausting multiple resources, and your instance's performance is affected", description: "Check which resources are reaching their threshold on your project's usage page.", docsUrl: undefined, - correctionUrl: '/project/[ref]/settings/billing/usage', buttonText: 'Check usage', + metric: null, }, } diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index c3672308ae0..b000addf113 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -12,15 +12,19 @@ import { import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' import { RESOURCE_WARNING_MESSAGES } from './UsageWarningBanner.constants' import { useRouter } from 'next/router' +import { useSelectedOrganization } from 'hooks' const UsageWarningBanner = () => { const { ref } = useParams() const router = useRouter() + const organization = useSelectedOrganization() const { data: resourceWarnings } = useResourceWarningsQuery() const projectResourceWarnings = (resourceWarnings ?? [])?.find( (warning) => warning.project === ref ) + const isOrgBilling = !!organization?.subscription_id + // [Joshen] Read only takes higher precedence over multiple resource warnings const activeWarnings = projectResourceWarnings !== undefined @@ -49,19 +53,35 @@ const UsageWarningBanner = () => { ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.docsUrl : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] ?.docsUrl - const correctionUrl = ( + const metric = activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.correctionUrl + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.metric : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] - ?.correctionUrl - )?.replace('[ref]', ref ?? 'default') + ?.metric + + const correctionUrl = ( + metric === undefined + ? undefined + : metric === null + ? '/project/[ref]/settings/[infra-path]' + : `/project/[ref]/settings/[infra-path]#${metric}` + ) + ?.replace('[ref]', ref ?? 'default') + ?.replace('[infra-path]', isOrgBilling ? 'infrastructure' : 'billing/usage') + const buttonText = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.buttonText : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] ?.buttonText - if (activeWarnings.length === 0 || router.pathname.includes('/usage')) return null + // Don't show banner if no warnings, or on usage/infra page + if ( + activeWarnings.length === 0 || + router.pathname.endsWith('/usage') || + router.pathname.endsWith('/infrastructure') + ) + return null return ( Date: Mon, 28 Aug 2023 17:39:43 +0800 Subject: [PATCH 08/27] Add mock stubs for new data fetchers --- studio/__mocks__/data/fetchers.js | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 studio/__mocks__/data/fetchers.js diff --git a/studio/__mocks__/data/fetchers.js b/studio/__mocks__/data/fetchers.js new file mode 100644 index 00000000000..2331502c3d9 --- /dev/null +++ b/studio/__mocks__/data/fetchers.js @@ -0,0 +1,8 @@ +export const get = jest.fn() +export const post = jest.fn() +export const patch = jest.fn() +export const del = jest.fn() +export const put = jest.fn() +export const head = jest.fn() +export const trace = jest.fn() +export const options = jest.fn() From cc43b2e0e4988d14df8a6958f0bbaa1901be4f53 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Aug 2023 16:25:02 +0800 Subject: [PATCH 09/27] Midway updating based on endpoint changes --- .../Home/ProjectList/ProjectCard.tsx | 11 +- .../Home/ProjectList/ProjectCardWarnings.tsx | 112 +++++++++++++----- .../UsageWarningBanner.constants.ts | 112 +++++++++++++----- .../ui/UsageWarnings/UsageWarningBanner.tsx | 46 +++++-- studio/data/api.d.ts | 40 ++++++- studio/data/usage/resource-warnings-query.ts | 19 +++ studio/pages/projects.tsx | 2 +- 7 files changed, 259 insertions(+), 83 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 430de0ce0d3..6c70c5ced6b 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -44,15 +44,6 @@ const ProjectCard = ({ const isPaused = project.status === PROJECT_STATUS.INACTIVE const isRestoring = project.status === PROJECT_STATUS.RESTORING - const checkProjectResourceWarnings = (resourceWarnings: ResourceWarning) => { - return Object.values(resourceWarnings).some( - (value) => typeof value === 'boolean' && value === true - ) - } - - const projectHasResourceWarnings = - resourceWarnings !== undefined ? checkProjectResourceWarnings(resourceWarnings) : false - return (
  • } > - {resourceWarnings && projectHasResourceWarnings ? ( + {resourceWarnings ? ( ) : (
    diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index a0d7b2bbd85..7ae429f8976 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -5,32 +5,66 @@ export interface ProjectCardWarningsProps { resourceWarnings: ResourceWarning } -// reference for message wording -// www.notion.so/supabase/Notify-users-on-resource-exhaustion-on-the-dashboard-4bb7b9a990104720b179b7a37d07f41c?pvs=4#a79798b5ac72402e8bfea4c4715e7439 const RESOURCE_WARNING_MESSAGES = { is_readonly_mode_enabled: { - title: 'Project is in readonly mode', - description: 'Database is no longer accept write requests.', + warning: { + title: 'Project is in readonly mode', + description: 'Database is no longer accept write requests.', + }, + critical: { + title: 'Project is in readonly mode', + description: 'Database is no longer accept write requests.', + }, }, - is_disk_io_budget_below_threshold: { - title: 'Project is depleting its Disk IO Budget', - description: 'Instance may become unresponsive if fully exhausted.', + disk_io_exhaustion: { + warning: { + title: 'Project is depleting its Disk IO Budget', + description: 'Instance may become unresponsive if fully exhausted.', + }, + critical: { + title: 'Project has depleted its Disk IO Budget', + description: 'Instance may become unresponsive.', + }, }, - is_disk_space_usage_beyond_threshold: { - title: 'Project is exhausting disk space budget', - description: 'Instance may become unresponsive if fully exhausted.', + disk_space_exhaustion: { + warning: { + title: 'Project is exhausting disk space budget', + description: 'Instance may become unresponsive if fully exhausted.', + }, + critical: { + title: 'Project has exhausted disk space budget', + description: 'Instance may become unresponsive.', + }, }, - is_cpu_load_beyond_threshold: { - title: 'Project has high CPU usage', - description: `Instance's performance is affected`, + cpu_exhaustion: { + warning: { + title: 'Project has high CPU usage', + description: `Instance's performance is affected`, + }, + critical: { + title: 'Project CPU usage is at 100%', + description: `Instance's performance is affected`, + }, }, - is_memory_and_swap_usage_beyond_threshold: { - title: 'Project has high memory usage', - description: `Instance's performance is affected.`, + memory_and_swap_exhaustion: { + warning: { + title: 'Project has high memory usage', + description: `Instance's performance is affected.`, + }, + critical: { + title: 'Project memory usage is at 100%', + description: `Instance's performance is affected.`, + }, }, multiple_resource_warnings: { - title: 'Project is exhausting multiple resources', - description: `Instance's performance is affected.`, + warning: { + title: 'Project is exhausting multiple resources', + description: `Instance's performance is affected.`, + }, + critical: { + title: 'Project has exhausted at least one resource', + description: `Instance's performance is affected.`, + }, }, } @@ -39,32 +73,54 @@ const ProjectCardWarnings = ({ resourceWarnings }: ProjectCardWarningsProps) => const activeWarnings = resourceWarnings.is_readonly_mode_enabled ? ['is_readonly_mode_enabled'] : Object.keys(resourceWarnings).filter( - (property) => resourceWarnings[property as keyof typeof resourceWarnings] === true + (property) => + property !== 'project' && + property !== 'is_readonly_mode_enabled' && + resourceWarnings[property as keyof typeof resourceWarnings] !== null ) - const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') + + const getContent = (metric: string) => { + if (metric === 'is_readonly_mode_enabled') { + return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.warning + } + const severity = resourceWarnings[metric as keyof typeof resourceWarnings] + if (typeof severity !== 'string') return undefined + return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES][ + severity as 'warning' | 'critical' + ] + } + + const hasCriticalWarning = activeWarnings.some( + (x) => resourceWarnings[x as keyof typeof resourceWarnings] === 'critical' + ) + + const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning + + if (activeWarnings.length === 0) return
    return (
    {activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.title - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] - ?.title} + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings[ + hasCriticalWarning ? 'critical' : 'warning' + ].title + : getContent(activeWarnings[0])?.title} {activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.description - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] - ?.description} + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings[ + hasCriticalWarning ? 'critical' : 'warning' + ].description + : getContent(activeWarnings[0])?.description} - {/*
    {JSON.stringify(resourceWarnings, null, 2)}
    */}
    ) } diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts index 2ef96fafd7a..2cb01037184 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts @@ -1,53 +1,111 @@ -// [TODO] Just double check if the docs url and correction urls are all correct -// [TODO] Correction URL should depend if V2 or V1 billing export const RESOURCE_WARNING_MESSAGES = { is_readonly_mode_enabled: { - title: 'Your project is currently in readonly mode and is no longer accepting write requests', - description: - 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + content: { + warning: { + title: + 'Your project is currently in readonly mode and is no longer accepting write requests', + description: + 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + }, + critical: { + title: + 'Your project is currently in readonly mode and is no longer accepting write requests', + description: + 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', buttonText: undefined, metric: undefined, }, - is_disk_io_budget_below_threshold: { - title: - 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', - description: - 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + disk_io_exhaustion: { + content: { + warning: { + title: + 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', + description: + 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + }, + critical: { + title: + 'Your project has depleted its Disk IO Budget, and your instance may become unresponsive', + description: + 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', buttonText: 'Check usage', metric: 'disk_io', }, - is_disk_space_usage_beyond_threshold: { - title: - 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', - description: 'Some CTA description here', + disk_space_exhaustion: { + content: { + warning: { + title: + 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', + description: 'Some CTA description here', + }, + critical: { + title: + 'Your project has exhausted its disk space budget, and your instance may become unresponsive', + description: 'Some CTA description here', + }, + }, docsUrl: undefined, buttonText: undefined, metric: undefined, }, - is_cpu_load_beyond_threshold: { - title: - "Your project is currently facing high CPU usage, and your instance's performance is affected", - description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + cpu_exhaustion: { + content: { + warning: { + title: + "Your project is currently facing high CPU usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + }, + critical: { + title: "Your project's CPU usage is at 100% and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', buttonText: 'Check usage', metric: 'cpu', }, - is_memory_and_swap_usage_beyond_threshold: { - title: - "Your project is currently facing high memory usage, and your instance's performance is affected", - description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + memory_and_swap_exhaustion: { + content: { + warning: { + title: + "Your project is currently facing high memory usage, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + }, + critical: { + title: + "Your project's memory usage is at 100%, and your instance's performance is affected", + description: + 'You will need to either optimize your performance or upgrade your compute to a larger instance', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', buttonText: 'Check usage', metric: 'ram', }, multiple_resource_warnings: { - title: - "Your project is currently exhausting multiple resources, and your instance's performance is affected", - description: "Check which resources are reaching their threshold on your project's usage page.", + content: { + warning: { + title: + "Your project is currently exhausting multiple resources, and your instance's performance is affected", + description: + "Check which resources are reaching their threshold on your project's usage page.", + }, + critical: { + title: + "Your project has exhausted at least one resource, and your instance's performance is affected", + description: + "Check which resources have reached their threshold on your project's usage page.", + }, + }, docsUrl: undefined, buttonText: 'Check usage', metric: null, diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index b000addf113..647882dd84c 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -1,5 +1,6 @@ -import Link from 'next/link' import { useParams } from 'common' +import Link from 'next/link' +import { useRouter } from 'next/router' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, @@ -10,9 +11,8 @@ import { } from 'ui' import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' -import { RESOURCE_WARNING_MESSAGES } from './UsageWarningBanner.constants' -import { useRouter } from 'next/router' import { useSelectedOrganization } from 'hooks' +import { RESOURCE_WARNING_MESSAGES } from './UsageWarningBanner.constants' const UsageWarningBanner = () => { const { ref } = useParams() @@ -32,22 +32,44 @@ const UsageWarningBanner = () => { ? ['is_readonly_mode_enabled'] : Object.keys(projectResourceWarnings).filter( (property) => - projectResourceWarnings[property as keyof typeof projectResourceWarnings] === true + property !== 'project' && + property !== 'is_readonly_mode_enabled' && + projectResourceWarnings[property as keyof typeof projectResourceWarnings] !== null ) : [] - const hasHighPriorityWarning = activeWarnings.includes('is_readonly_mode_enabled') + const hasCriticalWarning = + projectResourceWarnings !== undefined + ? activeWarnings.some( + (x) => projectResourceWarnings[x as keyof typeof projectResourceWarnings] === 'critical' + ) + : false + const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning + + const getContent = (metric: string) => { + if (projectResourceWarnings === undefined) return undefined + if (metric === 'is_readonly_mode_enabled') { + return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.content.warning + } + const severity = projectResourceWarnings[metric as keyof typeof projectResourceWarnings] + if (typeof severity !== 'string') return undefined + return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES].content[ + severity as 'warning' | 'critical' + ] + } const title = activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.title - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] - ?.title + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.content[ + hasCriticalWarning ? 'critical' : 'warning' + ].title + : getContent(activeWarnings[0])?.title const description = activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.description - : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] - ?.description + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.content[ + hasCriticalWarning ? 'critical' : 'warning' + ].description + : getContent(activeWarnings[0])?.description const learnMoreUrl = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.docsUrl @@ -85,7 +107,7 @@ const UsageWarningBanner = () => { return ( diff --git a/studio/data/api.d.ts b/studio/data/api.d.ts index 30c7c2551c9..adf68e2f1f7 100644 --- a/studio/data/api.d.ts +++ b/studio/data/api.d.ts @@ -214,9 +214,13 @@ export interface paths { "/platform/organizations/{slug}/billing/subscription": { /** Gets the current subscription */ get: operations["SubscriptionController_getSubscription"]; - /** Updates subscription */ + /** Previews subscription change */ put: operations["SubscriptionController_updateSubscription"]; }; + "/platform/organizations/{slug}/billing/subscription/preview": { + /** Updates subscription */ + post: operations["SubscriptionController_previewSubscriptionChange"]; + }; "/platform/organizations/{slug}/billing/plans": { /** Gets subscription plans */ get: operations["OrgPlansController_getAvailablePlans"]; @@ -1643,12 +1647,16 @@ export interface components { redirectTo?: string; }; ProjectResourceWarningsResponse: { + /** @enum {string|null} */ + disk_io_exhaustion: "critical" | "warning" | null; + /** @enum {string|null} */ + disk_space_exhaustion: "critical" | "warning" | null; + /** @enum {string|null} */ + cpu_exhaustion: "critical" | "warning" | null; + /** @enum {string|null} */ + memory_and_swap_exhaustion: "critical" | "warning" | null; project: string; is_readonly_mode_enabled: boolean; - is_disk_io_budget_below_threshold: boolean; - is_disk_space_usage_beyond_threshold: boolean; - is_cpu_load_beyond_threshold: boolean; - is_memory_and_swap_usage_beyond_threshold: boolean; }; GetGoTrueConfigResponse: { SITE_URL: string; @@ -1818,6 +1826,8 @@ export interface components { SMS_PROVIDER?: string; SMS_MESSAGEBIRD_ACCESS_KEY?: string; SMS_MESSAGEBIRD_ORIGINATOR?: string; + SMS_TEST_OTP?: string; + SMS_TEST_OTP_VALID_UNTIL?: string; SMS_TEXTLOCAL_API_KEY?: string; SMS_TEXTLOCAL_SENDER?: string; SMS_TWILIO_ACCOUNT_SID?: string; @@ -5463,6 +5473,26 @@ export interface operations { 500: never; }; }; + /** Updates subscription */ + SubscriptionController_previewSubscriptionChange: { + parameters: { + path: { + /** @description Organization slug */ + slug: string; + }; + }; + requestBody: { + content: { + "application/json": components["schemas"]["UpdateSubscriptionBody"]; + }; + }; + responses: { + 201: never; + 403: never; + /** @description Failed to update subscription */ + 500: never; + }; + }; /** Gets subscription plans */ OrgPlansController_getAvailablePlans: { parameters: { diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index d7ecd7c350e..7249b41e989 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -8,6 +8,25 @@ import { components } from 'data/api' export async function getResourceWarnings(signal?: AbortSignal) { const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) if (error) throw error + + return [ + { + project: 'iqixcyokaezexqncgirb', + is_readonly_mode_enabled: false, + disk_io_exhaustion: null, + disk_space_exhaustion: null, + cpu_exhaustion: null, + memory_and_swap_exhaustion: null, + }, + { + project: 'yhkgeeesimzjzbigwxwv', + is_readonly_mode_enabled: false, + disk_io_exhaustion: null, + disk_space_exhaustion: null, + cpu_exhaustion: 'critical', + memory_and_swap_exhaustion: null, + }, + ] as ResourceWarning[] return data } diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index c7e183eced8..909a567dad3 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -48,7 +48,7 @@ const ProjectsPage: NextPageWithLayout = () => {
    )} - {isError && ( + {true && (
    From e0b554a2472d91a7b5f39ea8e22a9fca4029ae8a Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Aug 2023 16:56:41 +0800 Subject: [PATCH 10/27] Wrap up updates according to endpoint changes --- .../BillingV2/Usage/Infrastructure.tsx | 6 ++---- .../Usage/UsageWarningAlerts/CPUWarnings.tsx | 13 ++++--------- .../Usage/UsageWarningAlerts/RAMWarnings.tsx | 13 ++++--------- .../Infrastructure/InfrastructureActivity.tsx | 8 ++------ .../ui/UsageWarnings/UsageWarningBanner.tsx | 4 ++-- studio/data/usage/resource-warnings-query.ts | 19 ------------------- studio/pages/projects.tsx | 2 +- 7 files changed, 15 insertions(+), 50 deletions(-) diff --git a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx index 2789f1e139b..39c3c24459a 100644 --- a/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx +++ b/studio/components/interfaces/BillingV2/Usage/Infrastructure.tsx @@ -186,16 +186,14 @@ const Infrastructure = ({ )} {attribute.key === 'ram_usage' && ( )} diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx index 818e649f4ac..8a207e12518 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/CPUWarnings.tsx @@ -5,16 +5,11 @@ import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from interface CPUWarningsProps { isFreePlan: boolean upgradeUrl: string - isBeyondThreshold: boolean + severity?: 'warning' | 'critical' | null } -const CPUWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: CPUWarningsProps) => { - // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings once - // endpoint is updated to return text instead of boolean - const isApproaching = isBeyondThreshold - const isExceeded = false - - if (isApproaching) { +const CPUWarnings = ({ isFreePlan, upgradeUrl, severity }: CPUWarningsProps) => { + if (severity === 'warning') { return ( @@ -42,7 +37,7 @@ const CPUWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: CPUWarningsP ) } - if (isExceeded) { + if (severity === 'critical') { return ( diff --git a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx index 8006c3d079d..fe233f08695 100644 --- a/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx +++ b/studio/components/interfaces/BillingV2/Usage/UsageWarningAlerts/RAMWarnings.tsx @@ -5,16 +5,11 @@ import { AlertDescription_Shadcn_, Alert_Shadcn_, Button, IconAlertCircle } from interface RAMWarningsProps { isFreePlan: boolean upgradeUrl: string - isBeyondThreshold: boolean + severity?: 'warning' | 'critical' | null } -const RAMWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: RAMWarningsProps) => { - // [Joshen TODO] Need to implement the necessary conditionals for rendering the warnings once - // endpoint is updated to return text instead of boolean - const isApproaching = isBeyondThreshold - const isExceeded = false - - if (isApproaching) { +const RAMWarnings = ({ isFreePlan, upgradeUrl, severity }: RAMWarningsProps) => { + if (severity === 'warning') { return ( @@ -42,7 +37,7 @@ const RAMWarnings = ({ isFreePlan, upgradeUrl, isBeyondThreshold }: RAMWarningsP ) } - if (isExceeded) { + if (severity === 'critical') { return ( diff --git a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index 28e6dce7415..1594655f2da 100644 --- a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -308,18 +308,14 @@ const InfrastructureActivity = () => { )} {attribute.key === 'ram_usage' && ( )} diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index 647882dd84c..19b7fd6e1d8 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -100,8 +100,8 @@ const UsageWarningBanner = () => { // Don't show banner if no warnings, or on usage/infra page if ( activeWarnings.length === 0 || - router.pathname.endsWith('/usage') || - router.pathname.endsWith('/infrastructure') + ((router.pathname.endsWith('/usage') || router.pathname.endsWith('/infrastructure')) && + !activeWarnings.includes('is_readonly_mode_enabled')) ) return null diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index 7249b41e989..d7ecd7c350e 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -8,25 +8,6 @@ import { components } from 'data/api' export async function getResourceWarnings(signal?: AbortSignal) { const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) if (error) throw error - - return [ - { - project: 'iqixcyokaezexqncgirb', - is_readonly_mode_enabled: false, - disk_io_exhaustion: null, - disk_space_exhaustion: null, - cpu_exhaustion: null, - memory_and_swap_exhaustion: null, - }, - { - project: 'yhkgeeesimzjzbigwxwv', - is_readonly_mode_enabled: false, - disk_io_exhaustion: null, - disk_space_exhaustion: null, - cpu_exhaustion: 'critical', - memory_and_swap_exhaustion: null, - }, - ] as ResourceWarning[] return data } diff --git a/studio/pages/projects.tsx b/studio/pages/projects.tsx index 909a567dad3..c7e183eced8 100644 --- a/studio/pages/projects.tsx +++ b/studio/pages/projects.tsx @@ -48,7 +48,7 @@ const ProjectsPage: NextPageWithLayout = () => {
    )} - {true && ( + {isError && (
    From 464b51d791a41f03a143b19a09ccfc4a8e61beee Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Aug 2023 17:11:56 +0800 Subject: [PATCH 11/27] Update CTAs for disk space exhaustion --- .../ui/UsageWarnings/UsageWarningBanner.constants.ts | 10 ++++++---- .../components/ui/UsageWarnings/UsageWarningBanner.tsx | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts index 2cb01037184..51855ae57ec 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts @@ -42,17 +42,19 @@ export const RESOURCE_WARNING_MESSAGES = { warning: { title: 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', - description: 'Some CTA description here', + description: + 'You can opt to increase your disk size up to 200GB if required on the database settings page.', }, critical: { title: 'Your project has exhausted its disk space budget, and your instance may become unresponsive', - description: 'Some CTA description here', + description: + 'You can opt to increase your disk size up to 200GB if required on the database settings page.', }, }, - docsUrl: undefined, + docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disk-management', buttonText: undefined, - metric: undefined, + metric: 'disk_space', }, cpu_exhaustion: { content: { diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx index 19b7fd6e1d8..e058b084801 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx @@ -86,6 +86,8 @@ const UsageWarningBanner = () => { ? undefined : metric === null ? '/project/[ref]/settings/[infra-path]' + : metric === 'disk_space' + ? '/project/[ref]/settings/database' : `/project/[ref]/settings/[infra-path]#${metric}` ) ?.replace('[ref]', ref ?? 'default') From f134966bbd40bb8cea623e32ab8a6b74a6c5e066 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Aug 2023 17:18:36 +0800 Subject: [PATCH 12/27] Add feature flag, and ensure that banner shows up in all ProjectLayout --- .../Home/ProjectList/ProjectCard.tsx | 5 ++++- .../layouts/ProjectLayout/ProjectLayout.tsx | 19 ++++++++++++------- ...ourceExhaustionWarningBanner.constants.ts} | 0 .../ResourceExhaustionWarningBanner.tsx} | 6 +++--- 4 files changed, 19 insertions(+), 11 deletions(-) rename studio/components/ui/{UsageWarnings/UsageWarningBanner.constants.ts => ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts} (100%) rename studio/components/ui/{UsageWarnings/UsageWarningBanner.tsx => ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx} (96%) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 6c70c5ced6b..68817113fd7 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -13,6 +13,7 @@ import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' import { ResourceWarning } from 'data/usage/resource-warnings-query' import ProjectCardWarnings from './ProjectCardWarnings' +import { useFlag } from 'hooks' export interface ProjectCardProps { project: Project @@ -32,6 +33,8 @@ const ProjectCard = ({ const { name, ref: projectRef } = project const desc = `${project.cloud_provider} | ${project.region}` + const showResourceExhaustionWarnings = useFlag('resourceExhaustionWarnings') + const isBranchingEnabled = project.preview_branch_refs.length > 0 const isGithubIntegrated = githubIntegration !== undefined const isVercelIntegrated = vercelIntegration !== undefined @@ -118,7 +121,7 @@ const ProjectCard = ({
    } > - {resourceWarnings ? ( + {showResourceExhaustionWarnings && resourceWarnings ? ( ) : (
    diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx index adeba22da72..f314cb4aae9 100644 --- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx +++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx @@ -4,7 +4,7 @@ import { Fragment, PropsWithChildren, ReactNode } from 'react' import { useParams } from 'common/hooks' import Connecting from 'components/ui/Loading' -import UsageWarningBanner from 'components/ui/UsageWarnings/UsageWarningBanner' +import ResourceExhaustionWarningBanner from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner' import { useFlag, useSelectedOrganization, useSelectedProject, withAuth } from 'hooks' import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants' import AppLayout from '../AppLayout/AppLayout' @@ -69,6 +69,7 @@ const ProjectLayout = ({ const organizationName = selectedOrganization?.name const navLayoutV2 = useFlag('navigationLayoutV2') + const showResourceExhaustionWarnings = useFlag('resourceExhaustionWarnings') const isPaused = selectedProject?.status === PROJECT_STATUS.INACTIVE const ignorePausedState = @@ -110,7 +111,10 @@ const ProjectLayout = ({
    ) : ( - {children} + + {showResourceExhaustionWarnings && } + {children} + )}
  • @@ -193,10 +197,7 @@ const ContentWrapper = ({ isLoading, children }: ContentWrapperProps) => { ) : requiresDbConnection && isProjectBuilding ? ( ) : ( - <> - - {children} - + {children} )} ) @@ -224,6 +225,7 @@ export const ProjectLayoutNonBlocking = ({ const showPausedState = isPaused && !ignorePausedState const navLayoutV2 = useFlag('navigationLayoutV2') + const showResourceExhaustionWarnings = useFlag('resourceExhaustionWarnings') return ( @@ -251,7 +253,10 @@ export const ProjectLayoutNonBlocking = ({
    ) : ( - children + <> + {showResourceExhaustionWarnings && } + {children} + )} diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts similarity index 100% rename from studio/components/ui/UsageWarnings/UsageWarningBanner.constants.ts rename to studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts diff --git a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx similarity index 96% rename from studio/components/ui/UsageWarnings/UsageWarningBanner.tsx rename to studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index e058b084801..76a9dd279db 100644 --- a/studio/components/ui/UsageWarnings/UsageWarningBanner.tsx +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -12,9 +12,9 @@ import { import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' import { useSelectedOrganization } from 'hooks' -import { RESOURCE_WARNING_MESSAGES } from './UsageWarningBanner.constants' +import { RESOURCE_WARNING_MESSAGES } from './ResourceExhaustionWarningBanner.constants' -const UsageWarningBanner = () => { +const ResourceExhaustionWarningBanner = () => { const { ref } = useParams() const router = useRouter() const organization = useSelectedOrganization() @@ -137,4 +137,4 @@ const UsageWarningBanner = () => { ) } -export default UsageWarningBanner +export default ResourceExhaustionWarningBanner From 8e50bc2c990948bf922c6854c9d8b408bbe20a5b Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Aug 2023 18:01:17 +0800 Subject: [PATCH 13/27] Add badge for coming up in project card --- .../interfaces/Home/ProjectList/ProjectCard.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 68817113fd7..3af4a774a98 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -46,6 +46,8 @@ const ProjectCard = ({ project.status === PROJECT_STATUS.GOING_DOWN || project.status === PROJECT_STATUS.PAUSING const isPaused = project.status === PROJECT_STATUS.INACTIVE const isRestoring = project.status === PROJECT_STATUS.RESTORING + const isComingUp = + project.status === PROJECT_STATUS.UNKNOWN || project.status === PROJECT_STATUS.COMING_UP return (
  • @@ -118,6 +120,15 @@ const ProjectCard = ({ )} + + {isComingUp && ( + +
    + + Coming up +
    +
    + )} } > From d4885df7f317b4716945a1b6b22b2923f20d7bd9 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Fri, 1 Sep 2023 11:16:35 -0230 Subject: [PATCH 14/27] Updates --- .../Home/ProjectList/ProjectCard.tsx | 1 + .../Home/ProjectList/ProjectCardWarnings.tsx | 3 +- .../Infrastructure/InfrastructureActivity.tsx | 2 +- ...sourceExhaustionWarningBanner.constants.ts | 43 ++++++++----------- .../ResourceExhaustionWarningBanner.tsx | 3 +- studio/data/usage/resource-warnings-query.ts | 1 + 6 files changed, 26 insertions(+), 27 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 3af4a774a98..bf101db33cd 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -53,6 +53,7 @@ const ProjectCard = ({
  • {name}

    diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx index 7ae429f8976..a07d41ff3b1 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx @@ -102,7 +102,8 @@ const ProjectCardWarnings = ({ resourceWarnings }: ProjectCardWarningsProps) =>
    diff --git a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index 1594655f2da..9a077356b21 100644 --- a/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -214,7 +214,7 @@ const InfrastructureActivity = () => {
    -

    {attribute.name}

    +

    {attribute.name}

    {attribute.description.split('\n').map((value, idx) => ( diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 51855ae57ec..5e54f833199 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -3,13 +3,13 @@ export const RESOURCE_WARNING_MESSAGES = { content: { warning: { title: - 'Your project is currently in readonly mode and is no longer accepting write requests', + 'Your project is currently in read-only mode and is no longer accepting write requests', description: 'You will need to manually override read-only mode and reduce the disk size to below 95%.', }, critical: { title: - 'Your project is currently in readonly mode and is no longer accepting write requests', + 'Your project is currently in read-only mode and is no longer accepting write requests', description: 'You will need to manually override read-only mode and reduce the disk size to below 95%.', }, @@ -22,15 +22,14 @@ export const RESOURCE_WARNING_MESSAGES = { content: { warning: { title: - 'Your project is about to deplete its Disk IO Budget, and your instance may become unresponsive once fully exhausted', + 'Your project is about to deplete its Disk IO Budget, and may become unresponsive once fully exhausted', description: - 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + 'You will need to either optimize your performance, or upgrade your compute to a larger plan.', }, critical: { - title: - 'Your project has depleted its Disk IO Budget, and your instance may become unresponsive', + title: 'Your project has depleted its Disk IO Budget, and may become unresponsive', description: - 'You will need to either optimize your performance, or upgrade your compute to a larger instance.', + 'You will need to either optimize your performance, or upgrade your compute to a larger plan.', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', @@ -41,15 +40,14 @@ export const RESOURCE_WARNING_MESSAGES = { content: { warning: { title: - 'Your project is about to exhaust its disk space budget, and your instance may become unresponsive once fully exhausted', + 'Your project is about to exhaust its disk space budget, and may become unresponsive once fully exhausted', description: - 'You can opt to increase your disk size up to 200GB if required on the database settings page.', + 'You can opt to increase your disk size up to 200GB on the database settings page.', }, critical: { - title: - 'Your project has exhausted its disk space budget, and your instance may become unresponsive', + title: 'Your project has exhausted its disk space budget, and may become unresponsive', description: - 'You can opt to increase your disk size up to 200GB if required on the database settings page.', + 'You can opt to increase your disk size up to 200GB on the database settings page.', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disk-management', @@ -59,15 +57,14 @@ export const RESOURCE_WARNING_MESSAGES = { cpu_exhaustion: { content: { warning: { - title: - "Your project is currently facing high CPU usage, and your instance's performance is affected", + title: 'Your project is currently facing high CPU usage, and its performance is affected', description: 'You will need to either optimize your performance or upgrade your compute to a larger instance', }, critical: { - title: "Your project's CPU usage is at 100% and your instance's performance is affected", + title: "Your project's CPU usage is at 100% and its performance is affected", description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + 'You will need to either optimize your performance or upgrade your compute to a larger plan', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', @@ -78,15 +75,14 @@ export const RESOURCE_WARNING_MESSAGES = { content: { warning: { title: - "Your project is currently facing high memory usage, and your instance's performance is affected", + 'Your project is currently facing high memory usage, and its performance is affected', description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + 'You will need to either optimize your performance or upgrade your compute to a larger plan', }, critical: { - title: - "Your project's memory usage is at 100%, and your instance's performance is affected", + title: "Your project's memory usage is at 100%, and its performance is affected", description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + 'You will need to either optimize your performance or upgrade your compute to a larger plan', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', @@ -97,13 +93,12 @@ export const RESOURCE_WARNING_MESSAGES = { content: { warning: { title: - "Your project is currently exhausting multiple resources, and your instance's performance is affected", + 'Your project is currently exhausting multiple resources, and its performance is affected', description: "Check which resources are reaching their threshold on your project's usage page.", }, critical: { - title: - "Your project has exhausted at least one resource, and your instance's performance is affected", + title: 'Your project has exhausted at least one resource, and its performance is affected', description: "Check which resources have reached their threshold on your project's usage page.", }, diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index 76a9dd279db..e406b1baaa8 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -110,7 +110,8 @@ const ResourceExhaustionWarningBanner = () => { return ( {title} diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index d7ecd7c350e..b1d56c12cfe 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -8,6 +8,7 @@ import { components } from 'data/api' export async function getResourceWarnings(signal?: AbortSignal) { const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) if (error) throw error + return data } From 1068773733ee107c2d1b034a3d75afceb6554b68 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Wed, 6 Sep 2023 17:08:27 -0230 Subject: [PATCH 15/27] Combine project status with resource warnings --- .../Home/ProjectList/ProjectCard.tsx | 111 +++++---------- .../Home/ProjectList/ProjectCardStatus.tsx | 134 ++++++++++++++++++ .../Home/ProjectList/ProjectCardWarnings.tsx | 129 ----------------- ...sourceExhaustionWarningBanner.constants.ts | 80 +++++++++-- .../ResourceExhaustionWarningBanner.tsx | 13 +- studio/data/usage/resource-warnings-query.ts | 19 +++ 6 files changed, 267 insertions(+), 219 deletions(-) create mode 100644 studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx delete mode 100644 studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index bf101db33cd..9d3f44c63ec 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -1,19 +1,12 @@ -import { - Badge, - IconAlertTriangle, - IconGitBranch, - IconGitHub, - IconLoader, - IconPauseCircle, -} from 'ui' +import { IconGitBranch, IconGitHub } from 'ui' import CardButton from 'components/ui/CardButton' import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' import { ResourceWarning } from 'data/usage/resource-warnings-query' -import ProjectCardWarnings from './ProjectCardWarnings' import { useFlag } from 'hooks' +import { ProjectCardStatus } from './ProjectCardStatus' export interface ProjectCardProps { project: Project @@ -23,6 +16,32 @@ export interface ProjectCardProps { resourceWarnings?: ResourceWarning } +const getProjetStatus = (project: Project) => { + let status + switch (project.status) { + case PROJECT_STATUS.ACTIVE_HEALTHY: + status = 'isHealthy' + break + case PROJECT_STATUS.GOING_DOWN: + case PROJECT_STATUS.PAUSING: + status = 'isPausing' + break + case PROJECT_STATUS.INACTIVE: + status = 'isPaused' + break + case PROJECT_STATUS.RESTORING: + status = 'isRestoring' + break + case PROJECT_STATUS.UNKNOWN: + case PROJECT_STATUS.COMING_UP: + status = 'isComingUp' + break + default: + status = '' + } + return status +} + const ProjectCard = ({ project, rewriteHref, @@ -33,21 +52,11 @@ const ProjectCard = ({ const { name, ref: projectRef } = project const desc = `${project.cloud_provider} | ${project.region}` - const showResourceExhaustionWarnings = useFlag('resourceExhaustionWarnings') - const isBranchingEnabled = project.preview_branch_refs.length > 0 const isGithubIntegrated = githubIntegration !== undefined const isVercelIntegrated = vercelIntegration !== undefined const githubRepository = githubIntegration?.metadata.name ?? undefined - - // Project status should supersede its read only status - const isHealthy = project.status === PROJECT_STATUS.ACTIVE_HEALTHY - const isPausing = - project.status === PROJECT_STATUS.GOING_DOWN || project.status === PROJECT_STATUS.PAUSING - const isPaused = project.status === PROJECT_STATUS.INACTIVE - const isRestoring = project.status === PROJECT_STATUS.RESTORING - const isComingUp = - project.status === PROJECT_STATUS.UNKNOWN || project.status === PROJECT_STATUS.COMING_UP + const projectStatus = getProjetStatus(project) return (
  • @@ -56,7 +65,8 @@ const ProjectCard = ({ containerHeightClassName="h-48" title={
    -

    {name}

    +

    {name}

    + {desc}
    {isVercelIntegrated && (
    @@ -84,61 +94,16 @@ const ProjectCard = ({
    } footer={ -
    - {desc} - - {isHealthy && ( - -
    - Active -
    -
    - )} - - {isRestoring && ( - -
    - - Restoring -
    -
    - )} - - {isPausing && ( - -
    - - Pausing -
    -
    - )} - - {isPaused && ( - -
    - - Paused -
    -
    - )} - - {isComingUp && ( - -
    - - Coming up -
    -
    +
    + {resourceWarnings && ( + )}
    } - > - {showResourceExhaustionWarnings && resourceWarnings ? ( - - ) : ( -
    - )} - + >
  • ) } diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx new file mode 100644 index 00000000000..e04a27d4bcb --- /dev/null +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx @@ -0,0 +1,134 @@ +import { RESOURCE_WARNING_MESSAGES } from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants' +import { ResourceWarning } from 'data/usage/resource-warnings-query' +import { + Alert_Shadcn_, + AlertDescription_Shadcn_, + AlertTitle_Shadcn_, + IconAlertTriangle, + IconLoader, + IconPauseCircle, +} from 'ui' + +export interface ProjectCardWarningsProps { + resourceWarnings: ResourceWarning + projectStatus: string +} + +export const ProjectCardStatus = ({ + resourceWarnings, + projectStatus, +}: ProjectCardWarningsProps) => { + //const showResourceExhaustionWarnings = useFlag('resourceExhaustionWarnings') + const showResourceExhaustionWarnings = false + + // [Joshen] Read only takes higher precedence over multiple resource warnings + const activeWarnings = resourceWarnings.is_readonly_mode_enabled + ? ['is_readonly_mode_enabled'] + : Object.keys(resourceWarnings).filter( + (property) => + property !== 'project' && + property !== 'is_readonly_mode_enabled' && + resourceWarnings[property as keyof typeof resourceWarnings] !== null + ) + + const getContent = (metric: string) => { + if (metric === 'is_readonly_mode_enabled') { + return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.cardContent.warning + } + + const severity = resourceWarnings[metric as keyof typeof resourceWarnings] + + if (typeof severity !== 'string') return undefined + + return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES][ + 'cardContent' + ][severity as 'warning' | 'critical'] + } + + const hasCriticalWarning = activeWarnings.some( + (x) => resourceWarnings[x as keyof typeof resourceWarnings] === 'critical' + ) + + const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning + + const getWarningType = () => { + let type = 'default' + + switch (true) { + case projectStatus === 'isPaused': + type = 'default' + break + case isCritical: + type = 'destructive' + break + default: + type = 'warning' + break + } + + return type as 'warning' | 'destructive' | 'default' + } + + const AlertIcon = () => { + switch (true) { + case projectStatus === 'isPaused' || projectStatus === 'isPausing': + return + case projectStatus === 'isRestoring' || projectStatus === 'isComingUp': + return + default: + return + } + } + const getTitle = () => { + if (projectStatus === 'isPaused') return 'Project is paused' + if (projectStatus === 'isPausing') return 'Project is pausing' + if (projectStatus === 'isComingUp') return 'Project is coming up' + if (projectStatus === 'isRestoring') return 'Project is restoring' + + // If none of the paused/restoring states match, proceed with the default logic + return activeWarnings.length > 1 + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.cardContent[ + hasCriticalWarning ? 'critical' : 'warning' + ].title + : getContent(activeWarnings[0])?.title + } + + const getDescription = () => { + if (projectStatus === 'isPaused') + return 'This project will not accept any requests until unpaused.' + if (projectStatus === 'isPausing') + return 'This project is entering a paused state. This may take a few minutes.' + if (projectStatus === 'isComingUp') + return 'This project is coming up. This may take a few minutes.' + if (projectStatus === 'isRestoring') + return 'This project is restoring. This may take a few minutes.' + + // If none of the paused/restoring states match, proceed with the default logic + return activeWarnings.length > 1 && showResourceExhaustionWarnings + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.cardContent[ + hasCriticalWarning ? 'critical' : 'warning' + ].description + : getContent(activeWarnings[0])?.description + } + + const alertTitle = getTitle() + const alertDescription = getDescription() + const alertType = getWarningType() + + if (activeWarnings.length === 0 && projectStatus === 'isHealthy') return
    + + return ( +
    + + + + {alertTitle} + {alertDescription} + +
    + ) +} diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx deleted file mode 100644 index a07d41ff3b1..00000000000 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardWarnings.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { ResourceWarning } from 'data/usage/resource-warnings-query' -import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, IconAlertTriangle } from 'ui' - -export interface ProjectCardWarningsProps { - resourceWarnings: ResourceWarning -} - -const RESOURCE_WARNING_MESSAGES = { - is_readonly_mode_enabled: { - warning: { - title: 'Project is in readonly mode', - description: 'Database is no longer accept write requests.', - }, - critical: { - title: 'Project is in readonly mode', - description: 'Database is no longer accept write requests.', - }, - }, - disk_io_exhaustion: { - warning: { - title: 'Project is depleting its Disk IO Budget', - description: 'Instance may become unresponsive if fully exhausted.', - }, - critical: { - title: 'Project has depleted its Disk IO Budget', - description: 'Instance may become unresponsive.', - }, - }, - disk_space_exhaustion: { - warning: { - title: 'Project is exhausting disk space budget', - description: 'Instance may become unresponsive if fully exhausted.', - }, - critical: { - title: 'Project has exhausted disk space budget', - description: 'Instance may become unresponsive.', - }, - }, - cpu_exhaustion: { - warning: { - title: 'Project has high CPU usage', - description: `Instance's performance is affected`, - }, - critical: { - title: 'Project CPU usage is at 100%', - description: `Instance's performance is affected`, - }, - }, - memory_and_swap_exhaustion: { - warning: { - title: 'Project has high memory usage', - description: `Instance's performance is affected.`, - }, - critical: { - title: 'Project memory usage is at 100%', - description: `Instance's performance is affected.`, - }, - }, - multiple_resource_warnings: { - warning: { - title: 'Project is exhausting multiple resources', - description: `Instance's performance is affected.`, - }, - critical: { - title: 'Project has exhausted at least one resource', - description: `Instance's performance is affected.`, - }, - }, -} - -const ProjectCardWarnings = ({ resourceWarnings }: ProjectCardWarningsProps) => { - // [Joshen] Read only takes higher precedence over multiple resource warnings - const activeWarnings = resourceWarnings.is_readonly_mode_enabled - ? ['is_readonly_mode_enabled'] - : Object.keys(resourceWarnings).filter( - (property) => - property !== 'project' && - property !== 'is_readonly_mode_enabled' && - resourceWarnings[property as keyof typeof resourceWarnings] !== null - ) - - const getContent = (metric: string) => { - if (metric === 'is_readonly_mode_enabled') { - return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.warning - } - const severity = resourceWarnings[metric as keyof typeof resourceWarnings] - if (typeof severity !== 'string') return undefined - return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES][ - severity as 'warning' | 'critical' - ] - } - - const hasCriticalWarning = activeWarnings.some( - (x) => resourceWarnings[x as keyof typeof resourceWarnings] === 'critical' - ) - - const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning - - if (activeWarnings.length === 0) return
    - - return ( -
    - - - - - {activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings[ - hasCriticalWarning ? 'critical' : 'warning' - ].title - : getContent(activeWarnings[0])?.title} - - - {activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings[ - hasCriticalWarning ? 'critical' : 'warning' - ].description - : getContent(activeWarnings[0])?.description} - - -
    - ) -} - -export default ProjectCardWarnings diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 5e54f833199..49fd1ada259 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -1,17 +1,27 @@ export const RESOURCE_WARNING_MESSAGES = { is_readonly_mode_enabled: { - content: { + bannerContent: { warning: { title: 'Your project is currently in read-only mode and is no longer accepting write requests', description: - 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + 'You will need to manually override read-only mode and reduce the disk size to below 95%', }, critical: { title: 'Your project is currently in read-only mode and is no longer accepting write requests', description: - 'You will need to manually override read-only mode and reduce the disk size to below 95%.', + 'You will need to manually override read-only mode and reduce the disk size to below 95%', + }, + }, + cardContent: { + warning: { + title: 'Project is in read-only mode', + description: 'Database is no longer accepting write requests.', + }, + critical: { + title: 'Project is in read-only mode', + description: 'Database is no longer accepting write requests.', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disabling-read-only-mode', @@ -19,7 +29,7 @@ export const RESOURCE_WARNING_MESSAGES = { metric: undefined, }, disk_io_exhaustion: { - content: { + bannerContent: { warning: { title: 'Your project is about to deplete its Disk IO Budget, and may become unresponsive once fully exhausted', @@ -32,12 +42,22 @@ export const RESOURCE_WARNING_MESSAGES = { 'You will need to either optimize your performance, or upgrade your compute to a larger plan.', }, }, + cardContent: { + warning: { + title: 'Project is depleting its Disk IO Budget', + description: 'Project may become unresponsive if fully exhausted.', + }, + critical: { + title: 'Project has depleted its Disk IO Budget', + description: 'Project may become unresponsive', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', buttonText: 'Check usage', metric: 'disk_io', }, disk_space_exhaustion: { - content: { + bannerContent: { warning: { title: 'Your project is about to exhaust its disk space budget, and may become unresponsive once fully exhausted', @@ -50,16 +70,26 @@ export const RESOURCE_WARNING_MESSAGES = { 'You can opt to increase your disk size up to 200GB on the database settings page.', }, }, + cardContent: { + warning: { + title: 'Project is exhausting disk space budget', + description: 'Project may become unresponsive if fully exhausted', + }, + critical: { + title: 'Project has exhausted disk space budget', + description: 'Project may become unresponsive', + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disk-management', buttonText: undefined, metric: 'disk_space', }, cpu_exhaustion: { - content: { + bannerContent: { warning: { title: 'Your project is currently facing high CPU usage, and its performance is affected', description: - 'You will need to either optimize your performance or upgrade your compute to a larger instance', + 'You will need to either optimize your performance or upgrade your compute to a larger Project', }, critical: { title: "Your project's CPU usage is at 100% and its performance is affected", @@ -67,12 +97,22 @@ export const RESOURCE_WARNING_MESSAGES = { 'You will need to either optimize your performance or upgrade your compute to a larger plan', }, }, + cardContent: { + warning: { + title: 'Project has high CPU usage', + description: `Project performance is affected`, + }, + critical: { + title: 'Project CPU usage is at 100%', + description: `Project performance is affected`, + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', buttonText: 'Check usage', metric: 'cpu', }, memory_and_swap_exhaustion: { - content: { + bannerContent: { warning: { title: 'Your project is currently facing high memory usage, and its performance is affected', @@ -85,12 +125,22 @@ export const RESOURCE_WARNING_MESSAGES = { 'You will need to either optimize your performance or upgrade your compute to a larger plan', }, }, + cardContent: { + warning: { + title: 'Project has high memory usage', + description: `Project performance is affected.`, + }, + critical: { + title: 'Project memory usage is at 100%', + description: `Project performance is affected.`, + }, + }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', buttonText: 'Check usage', metric: 'ram', }, multiple_resource_warnings: { - content: { + bannerContent: { warning: { title: 'Your project is currently exhausting multiple resources, and its performance is affected', @@ -98,11 +148,21 @@ export const RESOURCE_WARNING_MESSAGES = { "Check which resources are reaching their threshold on your project's usage page.", }, critical: { - title: 'Your project has exhausted at least one resource, and its performance is affected', + title: 'Your project has exhausted multiple resources, and its performance is affected', description: "Check which resources have reached their threshold on your project's usage page.", }, }, + cardContent: { + warning: { + title: 'Project is exhausting multiple resources', + description: `Project performance is affected.`, + }, + critical: { + title: 'Project has exhausted multiple resources', + description: `Project performance is affected.`, + }, + }, docsUrl: undefined, buttonText: 'Check usage', metric: null, diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index e406b1baaa8..8c0ac31cf89 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -49,24 +49,23 @@ const ResourceExhaustionWarningBanner = () => { const getContent = (metric: string) => { if (projectResourceWarnings === undefined) return undefined if (metric === 'is_readonly_mode_enabled') { - return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.content.warning + return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.bannerContent.warning } const severity = projectResourceWarnings[metric as keyof typeof projectResourceWarnings] if (typeof severity !== 'string') return undefined - return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES].content[ - severity as 'warning' | 'critical' - ] + return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES] + .bannerContent[severity as 'warning' | 'critical'] } const title = activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.content[ + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.bannerContent[ hasCriticalWarning ? 'critical' : 'warning' ].title : getContent(activeWarnings[0])?.title const description = activeWarnings.length > 1 - ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.content[ + ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.bannerContent[ hasCriticalWarning ? 'critical' : 'warning' ].description : getContent(activeWarnings[0])?.description @@ -129,7 +128,7 @@ const ResourceExhaustionWarningBanner = () => { {correctionUrl !== undefined && ( - + )} diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index b1d56c12cfe..897628e99af 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -9,6 +9,25 @@ export async function getResourceWarnings(signal?: AbortSignal) { const { data, error } = await get(`/platform/projects-resource-warnings`, { signal }) if (error) throw error + // return [ + // { + // project: 'qahowvikfwlkdqvvbfzd', + // is_readonly_mode_enabled: false, + // disk_io_exhaustion: null, + // disk_space_exhaustion: null, + // cpu_exhaustion: null, + // memory_and_swap_exhaustion: 'critical', + // }, + // { + // project: 'pkxlobkdgearimcwwgxk', + // is_readonly_mode_enabled: false, + // disk_io_exhaustion: 'critical', + // disk_space_exhaustion: null, + // cpu_exhaustion: null, + // memory_and_swap_exhaustion: null, + // }, + // ] as ResourceWarning[] + return data } From 4b71473e31f4997b5a5f4cbbf6d22bded95730e1 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Wed, 6 Sep 2023 21:07:53 -0230 Subject: [PATCH 16/27] Nudge banners --- .../ResourceExhaustionWarningBanner.tsx | 4 ++-- studio/data/usage/resource-warnings-query.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index 8c0ac31cf89..f0fcd6320ee 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -109,13 +109,13 @@ const ResourceExhaustionWarningBanner = () => { return ( svg]:left-6 px-6 [&>svg]:w-[26px] [&>svg]:h-[26px]" > {title} {description} -
    +
    {learnMoreUrl !== undefined && ( diff --git a/studio/data/usage/resource-warnings-query.ts b/studio/data/usage/resource-warnings-query.ts index 897628e99af..e85d8778fb7 100644 --- a/studio/data/usage/resource-warnings-query.ts +++ b/studio/data/usage/resource-warnings-query.ts @@ -11,7 +11,7 @@ export async function getResourceWarnings(signal?: AbortSignal) { // return [ // { - // project: 'qahowvikfwlkdqvvbfzd', + // project: 'hpzzzjznweoqiwwaxymv', // is_readonly_mode_enabled: false, // disk_io_exhaustion: null, // disk_space_exhaustion: null, From f3be5b202097da8c41d19ac72b6f47b44ca442c5 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Wed, 6 Sep 2023 21:17:09 -0230 Subject: [PATCH 17/27] Nudge card height --- studio/components/interfaces/Home/ProjectList/ProjectCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index 9d3f44c63ec..b9dcee111b2 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -62,7 +62,7 @@ const ProjectCard = ({
  • {name}

    From c37a4a7f41cad2031c5722ba82f142ce5ac42ae8 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:00:38 -0230 Subject: [PATCH 18/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 49fd1ada259..8f061122800 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -39,7 +39,7 @@ export const RESOURCE_WARNING_MESSAGES = { critical: { title: 'Your project has depleted its Disk IO Budget, and may become unresponsive', description: - 'You will need to either optimize your performance, or upgrade your compute to a larger plan.', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, }, cardContent: { From e33d28e57930f464d60f715d0794ecd4c038cbdf Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:01:01 -0230 Subject: [PATCH 19/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 8f061122800..f8a1acbc287 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -122,7 +122,7 @@ export const RESOURCE_WARNING_MESSAGES = { critical: { title: "Your project's memory usage is at 100%, and its performance is affected", description: - 'You will need to either optimize your performance or upgrade your compute to a larger plan', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, }, cardContent: { From 505e7203e0b9ea410cefe8f3439eb937b138fccb Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:01:12 -0230 Subject: [PATCH 20/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index f8a1acbc287..594086c5724 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -117,7 +117,7 @@ export const RESOURCE_WARNING_MESSAGES = { title: 'Your project is currently facing high memory usage, and its performance is affected', description: - 'You will need to either optimize your performance or upgrade your compute to a larger plan', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, critical: { title: "Your project's memory usage is at 100%, and its performance is affected", From ea8208daef5321c500e0598bb5157a1e28b674d0 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:01:23 -0230 Subject: [PATCH 21/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 594086c5724..4460166f534 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -89,7 +89,7 @@ export const RESOURCE_WARNING_MESSAGES = { warning: { title: 'Your project is currently facing high CPU usage, and its performance is affected', description: - 'You will need to either optimize your performance or upgrade your compute to a larger Project', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, critical: { title: "Your project's CPU usage is at 100% and its performance is affected", From 28642b1a16b5877cb0bed71de83ef44a90c40b76 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:01:31 -0230 Subject: [PATCH 22/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 4460166f534..5b1383c274b 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -94,7 +94,7 @@ export const RESOURCE_WARNING_MESSAGES = { critical: { title: "Your project's CPU usage is at 100% and its performance is affected", description: - 'You will need to either optimize your performance or upgrade your compute to a larger plan', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, }, cardContent: { From f2dc982a3a232fcf720a2cbbd0ba4cd60fd904d2 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Mon, 11 Sep 2023 10:01:39 -0230 Subject: [PATCH 23/27] Update studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts Co-authored-by: Long Hoang <1732217+loong@users.noreply.github.com> --- .../ResourceExhaustionWarningBanner.constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index 5b1383c274b..f0e5f2730ac 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -34,7 +34,7 @@ export const RESOURCE_WARNING_MESSAGES = { title: 'Your project is about to deplete its Disk IO Budget, and may become unresponsive once fully exhausted', description: - 'You will need to either optimize your performance, or upgrade your compute to a larger plan.', + 'You will need to optimize your performance or upgrade your compute. Check the usage page for more recent and detailed statistics.', }, critical: { title: 'Your project has depleted its Disk IO Budget, and may become unresponsive', From 31eb73684b0c697c9076dde9e188200ee13d5972 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 12 Sep 2023 22:10:08 -0230 Subject: [PATCH 24/27] Cleanup --- studio/components/interfaces/Home/ProjectList/ProjectList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 1cd3c9b70b5..c49ab30c9f8 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -13,7 +13,6 @@ import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useProjectsQuery } from 'data/projects/projects-query' import { ResourceWarning, useResourceWarningsQuery } from 'data/usage/resource-warnings-query' -import { useCheckPermissions } from 'hooks' import { IS_PLATFORM } from 'lib/constants' import { makeRandomString } from 'lib/helpers' import { Organization, Project, ResponseError } from 'types' From 2a1354448c86c5d18d5c9819b1fa83cb78876e26 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 12 Sep 2023 22:50:37 -0230 Subject: [PATCH 25/27] Language updates --- ...sourceExhaustionWarningBanner.constants.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts index f0e5f2730ac..5ed9d61b5f9 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.constants.ts @@ -45,11 +45,11 @@ export const RESOURCE_WARNING_MESSAGES = { cardContent: { warning: { title: 'Project is depleting its Disk IO Budget', - description: 'Project may become unresponsive if fully exhausted.', + description: 'It may become unresponsive if fully exhausted', }, critical: { title: 'Project has depleted its Disk IO Budget', - description: 'Project may become unresponsive', + description: 'It may become unresponsive', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-disk-io', @@ -73,11 +73,11 @@ export const RESOURCE_WARNING_MESSAGES = { cardContent: { warning: { title: 'Project is exhausting disk space budget', - description: 'Project may become unresponsive if fully exhausted', + description: 'It may become unresponsive if fully exhausted', }, critical: { title: 'Project has exhausted disk space budget', - description: 'Project may become unresponsive', + description: 'It may become unresponsive', }, }, docsUrl: 'https://supabase.com/docs/guides/platform/database-size#disk-management', @@ -100,11 +100,11 @@ export const RESOURCE_WARNING_MESSAGES = { cardContent: { warning: { title: 'Project has high CPU usage', - description: `Project performance is affected`, + description: `Performance is affected`, }, critical: { title: 'Project CPU usage is at 100%', - description: `Project performance is affected`, + description: `Performance is affected`, }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-cpu', @@ -128,11 +128,11 @@ export const RESOURCE_WARNING_MESSAGES = { cardContent: { warning: { title: 'Project has high memory usage', - description: `Project performance is affected.`, + description: `Performance is affected`, }, critical: { title: 'Project memory usage is at 100%', - description: `Project performance is affected.`, + description: `Performance is affected`, }, }, docsUrl: 'https://supabase.com/docs/guides/platform/exhaust-ram', @@ -156,11 +156,11 @@ export const RESOURCE_WARNING_MESSAGES = { cardContent: { warning: { title: 'Project is exhausting multiple resources', - description: `Project performance is affected.`, + description: `Performance is affected.`, }, critical: { title: 'Project has exhausted multiple resources', - description: `Project performance is affected.`, + description: `Performance is affected.`, }, }, docsUrl: undefined, From 7a6f7c7bc39628def46d783b510c9c28bc49fee2 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 13 Sep 2023 14:39:59 +0800 Subject: [PATCH 26/27] Small refactors --- .../Home/ProjectList/ProjectCard.tsx | 31 +-------- .../Home/ProjectList/ProjectCard.utils.ts | 34 +++++++++ .../Home/ProjectList/ProjectCardStatus.tsx | 69 +++++-------------- .../ResourceExhaustionWarningBanner.tsx | 23 +++---- .../ResourceExhaustionWarningBanner.utils.ts | 19 +++++ 5 files changed, 84 insertions(+), 92 deletions(-) create mode 100644 studio/components/interfaces/Home/ProjectList/ProjectCard.utils.ts create mode 100644 studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils.ts diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index d4306e2b5ea..a3251879130 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -1,11 +1,12 @@ import { IconGitBranch, IconGitHub } from 'ui' import CardButton from 'components/ui/CardButton' -import { BASE_PATH, PROJECT_STATUS } from 'lib/constants' +import { BASE_PATH } from 'lib/constants' import { Project } from 'types' import { IntegrationProjectConnection } from 'data/integrations/integrations.types' import { ResourceWarning } from 'data/usage/resource-warnings-query' import { ProjectCardStatus } from './ProjectCardStatus' +import { inferProjectStatus } from './ProjectCard.utils' export interface ProjectCardProps { project: Project @@ -15,32 +16,6 @@ export interface ProjectCardProps { resourceWarnings?: ResourceWarning } -const getProjetStatus = (project: Project) => { - let status - switch (project.status) { - case PROJECT_STATUS.ACTIVE_HEALTHY: - status = 'isHealthy' - break - case PROJECT_STATUS.GOING_DOWN: - case PROJECT_STATUS.PAUSING: - status = 'isPausing' - break - case PROJECT_STATUS.INACTIVE: - status = 'isPaused' - break - case PROJECT_STATUS.RESTORING: - status = 'isRestoring' - break - case PROJECT_STATUS.UNKNOWN: - case PROJECT_STATUS.COMING_UP: - status = 'isComingUp' - break - default: - status = '' - } - return status -} - const ProjectCard = ({ project, rewriteHref, @@ -55,7 +30,7 @@ const ProjectCard = ({ const isGithubIntegrated = githubIntegration !== undefined const isVercelIntegrated = vercelIntegration !== undefined const githubRepository = githubIntegration?.metadata.name ?? undefined - const projectStatus = getProjetStatus(project) + const projectStatus = inferProjectStatus(project) return (
  • diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.utils.ts b/studio/components/interfaces/Home/ProjectList/ProjectCard.utils.ts new file mode 100644 index 00000000000..ea1ab6b0872 --- /dev/null +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.utils.ts @@ -0,0 +1,34 @@ +import { PROJECT_STATUS } from 'lib/constants' +import { Project } from 'types' + +export const inferProjectStatus = (project: Project) => { + let status = undefined + switch (project.status) { + case PROJECT_STATUS.ACTIVE_HEALTHY: + status = 'isHealthy' + break + case PROJECT_STATUS.GOING_DOWN: + case PROJECT_STATUS.PAUSING: + status = 'isPausing' + break + case PROJECT_STATUS.INACTIVE: + status = 'isPaused' + break + case PROJECT_STATUS.RESTORING: + status = 'isRestoring' + break + case PROJECT_STATUS.UNKNOWN: + case PROJECT_STATUS.COMING_UP: + status = 'isComingUp' + break + } + return status as InferredProjectStatus +} + +export type InferredProjectStatus = + | 'isHealthy' + | 'isPausing' + | 'isPaused' + | 'isRestoring' + | 'isComingUp' + | undefined diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx index e04a27d4bcb..5a0ec87c02e 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx @@ -8,10 +8,12 @@ import { IconLoader, IconPauseCircle, } from 'ui' +import { InferredProjectStatus } from './ProjectCard.utils' +import { getWarningContent } from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils' export interface ProjectCardWarningsProps { resourceWarnings: ResourceWarning - projectStatus: string + projectStatus: InferredProjectStatus } export const ProjectCardStatus = ({ @@ -31,54 +33,11 @@ export const ProjectCardStatus = ({ resourceWarnings[property as keyof typeof resourceWarnings] !== null ) - const getContent = (metric: string) => { - if (metric === 'is_readonly_mode_enabled') { - return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.cardContent.warning - } - - const severity = resourceWarnings[metric as keyof typeof resourceWarnings] - - if (typeof severity !== 'string') return undefined - - return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES][ - 'cardContent' - ][severity as 'warning' | 'critical'] - } - const hasCriticalWarning = activeWarnings.some( (x) => resourceWarnings[x as keyof typeof resourceWarnings] === 'critical' ) - const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning - const getWarningType = () => { - let type = 'default' - - switch (true) { - case projectStatus === 'isPaused': - type = 'default' - break - case isCritical: - type = 'destructive' - break - default: - type = 'warning' - break - } - - return type as 'warning' | 'destructive' | 'default' - } - - const AlertIcon = () => { - switch (true) { - case projectStatus === 'isPaused' || projectStatus === 'isPausing': - return - case projectStatus === 'isRestoring' || projectStatus === 'isComingUp': - return - default: - return - } - } const getTitle = () => { if (projectStatus === 'isPaused') return 'Project is paused' if (projectStatus === 'isPausing') return 'Project is pausing' @@ -90,7 +49,7 @@ export const ProjectCardStatus = ({ ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.cardContent[ hasCriticalWarning ? 'critical' : 'warning' ].title - : getContent(activeWarnings[0])?.title + : getWarningContent(resourceWarnings, activeWarnings[0], 'cardContent')?.title } const getDescription = () => { @@ -108,12 +67,16 @@ export const ProjectCardStatus = ({ ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.cardContent[ hasCriticalWarning ? 'critical' : 'warning' ].description - : getContent(activeWarnings[0])?.description + : getWarningContent(resourceWarnings, activeWarnings[0], 'cardContent')?.description } const alertTitle = getTitle() const alertDescription = getDescription() - const alertType = getWarningType() + const alertType = isCritical + ? 'destructive' + : projectStatus === 'isPaused' + ? 'default' + : 'warning' if (activeWarnings.length === 0 && projectStatus === 'isHealthy') return
    @@ -121,11 +84,15 @@ export const ProjectCardStatus = ({
    - - + {projectStatus === 'isPaused' || projectStatus === 'isPausing' ? ( + + ) : projectStatus === 'isRestoring' || projectStatus === 'isComingUp' ? ( + + ) : ( + + )} {alertTitle} {alertDescription} diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx index f0fcd6320ee..37df8e4bb7a 100644 --- a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.tsx @@ -13,6 +13,7 @@ import { import { useResourceWarningsQuery } from 'data/usage/resource-warnings-query' import { useSelectedOrganization } from 'hooks' import { RESOURCE_WARNING_MESSAGES } from './ResourceExhaustionWarningBanner.constants' +import { getWarningContent } from './ResourceExhaustionWarningBanner.utils' const ResourceExhaustionWarningBanner = () => { const { ref } = useParams() @@ -46,34 +47,30 @@ const ResourceExhaustionWarningBanner = () => { : false const isCritical = activeWarnings.includes('is_readonly_mode_enabled') || hasCriticalWarning - const getContent = (metric: string) => { - if (projectResourceWarnings === undefined) return undefined - if (metric === 'is_readonly_mode_enabled') { - return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.bannerContent.warning - } - const severity = projectResourceWarnings[metric as keyof typeof projectResourceWarnings] - if (typeof severity !== 'string') return undefined - return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES] - .bannerContent[severity as 'warning' | 'critical'] - } - const title = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.bannerContent[ hasCriticalWarning ? 'critical' : 'warning' ].title - : getContent(activeWarnings[0])?.title + : projectResourceWarnings !== undefined + ? getWarningContent(projectResourceWarnings, activeWarnings[0], 'bannerContent')?.title + : null + const description = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.bannerContent[ hasCriticalWarning ? 'critical' : 'warning' ].description - : getContent(activeWarnings[0])?.description + : projectResourceWarnings !== undefined + ? getWarningContent(projectResourceWarnings, activeWarnings[0], 'bannerContent')?.description + : null + const learnMoreUrl = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.docsUrl : RESOURCE_WARNING_MESSAGES[activeWarnings[0] as keyof typeof RESOURCE_WARNING_MESSAGES] ?.docsUrl + const metric = activeWarnings.length > 1 ? RESOURCE_WARNING_MESSAGES.multiple_resource_warnings.metric diff --git a/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils.ts b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils.ts new file mode 100644 index 00000000000..0b3dd8b77e7 --- /dev/null +++ b/studio/components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils.ts @@ -0,0 +1,19 @@ +import { ResourceWarning } from 'data/usage/resource-warnings-query' +import { RESOURCE_WARNING_MESSAGES } from './ResourceExhaustionWarningBanner.constants' + +export const getWarningContent = ( + resourceWarnings: ResourceWarning, + metric: string, + contentType: 'cardContent' | 'bannerContent' +) => { + if (metric === 'is_readonly_mode_enabled') { + return RESOURCE_WARNING_MESSAGES.is_readonly_mode_enabled.cardContent.warning + } + + const severity = resourceWarnings[metric as keyof typeof resourceWarnings] + if (typeof severity !== 'string') return undefined + + return RESOURCE_WARNING_MESSAGES[metric as keyof typeof RESOURCE_WARNING_MESSAGES][contentType][ + severity as 'warning' | 'critical' + ] +} From 644e4f6a9b9934f386d27ee5f0555feaa69051fa Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 13 Sep 2023 15:10:13 +0800 Subject: [PATCH 27/27] More small fixes --- .../Home/ProjectList/ProjectCard.tsx | 6 +++--- .../Home/ProjectList/ProjectCardStatus.tsx | 19 ++++++++----------- .../Home/ProjectList/ProjectList.tsx | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx index a3251879130..a97bc84152e 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCard.tsx @@ -36,11 +36,11 @@ const ProjectCard = ({
  • -

    {name}

    - {desc} +

    {name}

    + {desc}
    {isVercelIntegrated && (
    diff --git a/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx index 5a0ec87c02e..bc889b3c721 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectCardStatus.tsx @@ -7,6 +7,7 @@ import { IconAlertTriangle, IconLoader, IconPauseCircle, + IconRefreshCw, } from 'ui' import { InferredProjectStatus } from './ProjectCard.utils' import { getWarningContent } from 'components/ui/ResourceExhaustionWarningBanner/ResourceExhaustionWarningBanner.utils' @@ -53,14 +54,10 @@ export const ProjectCardStatus = ({ } const getDescription = () => { - if (projectStatus === 'isPaused') - return 'This project will not accept any requests until unpaused.' - if (projectStatus === 'isPausing') - return 'This project is entering a paused state. This may take a few minutes.' - if (projectStatus === 'isComingUp') - return 'This project is coming up. This may take a few minutes.' - if (projectStatus === 'isRestoring') - return 'This project is restoring. This may take a few minutes.' + if (projectStatus === 'isPaused') return 'This project will not accept requests until resumed' + if (projectStatus === 'isPausing') return 'The pause process will complete in a few minutes' + if (projectStatus === 'isComingUp') return 'Your project will be ready in a few minutes' + if (projectStatus === 'isRestoring') return 'Your project will be ready in a few minutes' // If none of the paused/restoring states match, proceed with the default logic return activeWarnings.length > 1 && showResourceExhaustionWarnings @@ -84,16 +81,16 @@ export const ProjectCardStatus = ({
    {projectStatus === 'isPaused' || projectStatus === 'isPausing' ? ( ) : projectStatus === 'isRestoring' || projectStatus === 'isComingUp' ? ( - + ) : ( )} - {alertTitle} + {alertTitle} {alertDescription}
    diff --git a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx index 10d6bb07890..ab938e7de38 100644 --- a/studio/components/interfaces/Home/ProjectList/ProjectList.tsx +++ b/studio/components/interfaces/Home/ProjectList/ProjectList.tsx @@ -86,7 +86,7 @@ type OrganizationProjectsProps = { } const OrganizationProjects = ({ - organization: { id, name, slug, subscription_id }, + organization: { name, slug, subscription_id }, projects, overdueInvoices, resourceWarnings,