Update warning on card buttons

This commit is contained in:
Joshen Lim committed 2023-08-23 15:54:14 +08:00
1 parent cccf9a7da5
commit 17d4445ce7
8 files changed
+110 -118

No files matched your search

@@ -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 (
<li className="col-span-1 list-none">
<CardButton
linkHref={rewriteHref ? rewriteHref : `/project/${projectRef}`}
title={
<div className="w-full justify-between space-y-1">
<div className="w-full justify-between space-y-1.5 px-6">
<p className="flex-shrink truncate">{name}</p>
<div className="flex items-center space-x-1.5">
{isVercelIntegrated && (
@@ -87,61 +91,66 @@ const ProjectCard = ({
</>
)}
</div>
{resourceWarnings && projectHasResourceWarnings && (
<ProjectCardWarnings resourceWarnings={resourceWarnings} />
)}
</div>
}
footer={
<div className="flex items-end justify-between">
<span className="text-sm lowercase text-scale-1000">{desc}</span>
<div className="flex items-end justify-between px-6">
<span className="text-xs lowercase text-scale-1000">{desc}</span>
{isHealthy && isReadonly && (
<div className="grow text-right">
<Badge color="yellow">
<div className="flex items-center gap-2">
<IconAlertTriangle size={14} strokeWidth={2} />
<span className="truncate">Read-only mode</span>
</div>
</Badge>
</div>
{isHealthy && (
<>
{isReadonly ? (
<Badge color="yellow">
<div className="flex items-center gap-2">
<IconAlertTriangle size={14} strokeWidth={2} />
<span className="truncate">Read-only mode</span>
</div>
</Badge>
) : (
<Badge color="green">
<div className="flex items-center gap-2">
<span className="truncate">Active</span>
</div>
</Badge>
)}
</>
)}
{isRestoring && (
<div className="grow text-right">
<Badge color="brand">
<div className="flex items-center gap-2">
<IconLoader className="animate-spin" size={14} strokeWidth={2} />
<span className="truncate">Restoring</span>
</div>
</Badge>
</div>
<Badge color="brand">
<div className="flex items-center gap-2">
<IconLoader className="animate-spin" size={14} strokeWidth={2} />
<span className="truncate">Restoring</span>
</div>
</Badge>
)}
{isPausing && (
<div className="grow text-right">
<Badge color="scale">
<div className="flex items-center gap-2">
<IconLoader className="animate-spin" size={14} strokeWidth={2} />
<span className="truncate">Pausing</span>
</div>
</Badge>
</div>
<Badge color="scale">
<div className="flex items-center gap-2">
<IconLoader className="animate-spin" size={14} strokeWidth={2} />
<span className="truncate">Pausing</span>
</div>
</Badge>
)}
{isPaused && (
<div className="grow text-right">
<Badge color="scale">
<div className="flex items-center gap-2">
<IconPauseCircle size={14} strokeWidth={2} />
<span className="truncate">Paused</span>
</div>
</Badge>
</div>
<Badge color="scale">
<div className="flex items-center gap-2">
<IconPauseCircle size={14} strokeWidth={2} />
<span className="truncate">Paused</span>
</div>
</Badge>
)}
</div>
}
/>
>
{resourceWarnings && projectHasResourceWarnings ? (
<ProjectCardWarnings resourceWarnings={resourceWarnings} />
) : (
<div className="py-2" />
)}
</CardButton>
</li>
)
}
@@ -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<ResourceWarningResponse, 'project'>,
'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 (
<div>
<Alert_Shadcn_ variant="warning">
<Alert_Shadcn_
variant={hasHighPriorityWarning ? 'destructive' : 'warning'}
className="border-r-0 border-l-0 rounded-none my-2 mb-2.5"
>
<IconAlertTriangle strokeWidth={2} />
<AlertTitle_Shadcn_>
<AlertTitle_Shadcn_ className="text-xs mb-0.5">
{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}
</AlertTitle_Shadcn_>
<AlertDescription_Shadcn_>
<AlertDescription_Shadcn_ className="text-xs">
{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}
</AlertDescription_Shadcn_>
</Alert_Shadcn_>
@@ -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 (
<div className="space-y-3" key={makeRandomString(5)}>
@@ -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
)}
/>
@@ -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 (
<>
+2 -2
View File
@@ -27,7 +27,7 @@ const CardButton = ({
imgAlt,
onClick,
icon,
containerHeightClassName = 'h-48',
containerHeightClassName = '',
loading = false,
}: PropsWithChildren<CardButtonProps>) => {
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,
]
+1 -1
View File
@@ -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,
}
+14 -28
View File
@@ -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<ResourceWarningResponse[]>(`${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<ReturnType<typeof getResourceWarnings>>
export type ResourceError = unknown
export type ResourceWarning = components['schemas']['ProjectResourceWarningsResponse']
export type ResourceWarningsData = Awaited<ReturnType<typeof getResourceWarnings>>
export type ResourceWarningsError = ResponseError
export const useResourceWarningQuery = <TData = ResourceWarningData>({
export const useResourceWarningsQuery = <TData = ResourceWarningsData>({
enabled = true,
...options
}: UseQueryOptions<ResourceWarningData, ResourceError, TData> = {}) =>
useQuery<ResourceWarningData, ResourceError, TData>(
}: UseQueryOptions<ResourceWarningsData, ResourceWarningsError, TData> = {}) =>
useQuery<ResourceWarningsData, ResourceWarningsError, TData>(
usageKeys.resourceWarnings(),
({ signal }) => getResourceWarnings(signal),
{
enabled: enabled,
...options,
}
{ enabled, ...options }
)
-4
View File
@@ -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 (
<div className="mx-auto my-16 w-full max-w-7xl space-y-16">
<div className="mx-6 flex items-center space-x-6">