Refresh report and status update (#39473)

* refresh report

* project status

* prettier

* Minor refactors

* add back refreshing

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Saxon FletcherandJoshen Lim authored and GitHub committed 2025-10-14 08:09:48 +10:00
1 parent b36792aa49
commit 622486f750
3 files changed
+114 -28

No files matched your search

@@ -9,7 +9,7 @@ import {
import { SortableContext, arrayMove, rectSortingStrategy, useSortable } from '@dnd-kit/sortable'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import dayjs from 'dayjs'
import { Plus } from 'lucide-react'
import { Plus, RefreshCw } from 'lucide-react'
import type { CSSProperties, ReactNode } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { toast } from 'sonner'
@@ -18,8 +18,10 @@ import { useParams } from 'common'
import { SnippetDropdown } from 'components/interfaces/HomeNew/SnippetDropdown'
import { ReportBlock } from 'components/interfaces/Reports/ReportBlock/ReportBlock'
import type { ChartConfig } from 'components/interfaces/SQLEditor/UtilityPanel/ChartConfig'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { DEFAULT_CHART_CONFIG } from 'components/ui/QueryBlock/QueryBlock'
import { AnalyticsInterval } from 'data/analytics/constants'
import { useInvalidateAnalyticsQuery } from 'data/analytics/utils'
import { useContentInfiniteQuery } from 'data/content/content-infinite-query'
import { Content } from 'data/content/content-query'
import { useContentUpsertMutation } from 'data/content/content-upsert-mutation'
@@ -28,6 +30,7 @@ import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { uuidv4 } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import { useDatabaseSelectorStateSnapshot } from 'state/database-selector'
import type { Dashboards } from 'types'
import { Button } from 'ui'
import { Row } from 'ui-patterns'
@@ -35,10 +38,15 @@ import { Row } from 'ui-patterns'
export function CustomReportSection() {
const startDate = dayjs().subtract(7, 'day').toISOString()
const endDate = dayjs().toISOString()
const { ref } = useParams()
const { profile } = useProfile()
const { mutate: sendEvent } = useSendEventMutation()
const state = useDatabaseSelectorStateSnapshot()
const { data: organization } = useSelectedOrganizationQuery()
const { mutate: sendEvent } = useSendEventMutation()
const { invalidateInfraMonitoringQuery } = useInvalidateAnalyticsQuery()
const [isRefreshing, setIsRefreshing] = useState<boolean>(false)
const { data: reportsData } = useContentInfiniteQuery(
{ projectRef: ref, type: 'report', name: 'Home', limit: 1 },
@@ -269,6 +277,25 @@ export function CustomReportSection() {
persistReport(updated)
}
const onRefreshReport = () => {
if (!ref) return
setIsRefreshing(true)
const monitoringCharts = editableReport?.layout.filter(
(x) => x.provider === 'infra-monitoring' || x.provider === 'daily-stats'
)
monitoringCharts?.forEach((x) => {
invalidateInfraMonitoringQuery(ref, {
attribute: x.attribute,
startDate,
endDate,
interval: editableReport?.interval || '1d',
databaseIdentifier: state.selectedDatabaseId,
})
})
setTimeout(() => setIsRefreshing(false), 1000)
}
const layout = useMemo(() => editableReport?.layout ?? [], [editableReport])
useEffect(() => {
@@ -279,20 +306,32 @@ export function CustomReportSection() {
<div className="space-y-6">
<div className="flex items-center justify-between">
<h3 className="heading-section">Reports</h3>
{canUpdateReport || canCreateReport ? (
<SnippetDropdown
projectRef={ref}
onSelect={addSnippetToReport}
trigger={
<Button type="default" icon={<Plus />}>
Add block
</Button>
}
side="bottom"
align="end"
autoFocus
/>
) : null}
<div className="flex items-center gap-x-2">
{layout.length > 0 && (
<ButtonTooltip
type="default"
icon={<RefreshCw className={isRefreshing ? 'animate-spin' : ''} />}
className="w-7"
disabled={isRefreshing}
tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }}
onClick={onRefreshReport}
/>
)}
{canUpdateReport || canCreateReport ? (
<SnippetDropdown
projectRef={ref}
onSelect={addSnippetToReport}
trigger={
<Button type="default" icon={<Plus />}>
Add block
</Button>
}
side="bottom"
align="end"
autoFocus
/>
) : null}
</div>
</div>
<div className="relative">
{layout.length === 0 ? (
@@ -343,7 +382,7 @@ export function CustomReportSection() {
('1d' as AnalyticsInterval)
}
disableUpdate={false}
isRefreshing={false}
isRefreshing={isRefreshing}
onRemoveChart={handleRemoveChart}
onUpdateChart={(config) => handleUpdateChart(item.id, config)}
/>
@@ -37,16 +37,17 @@ const StatusMessage = ({
status,
isLoading,
isHealthy,
isProjectNew,
}: {
isLoading: boolean
isHealthy: boolean
isProjectNew: boolean
status?: ProjectServiceStatus
}) => {
if (isHealthy) return 'Healthy'
if (isHealthy || status === 'ACTIVE_HEALTHY') return 'Healthy'
if (isLoading) return 'Checking status'
if (status === 'UNHEALTHY') return 'Unhealthy'
if (status === 'COMING_UP') return 'Coming up...'
if (status === 'ACTIVE_HEALTHY') return 'Healthy'
if (isProjectNew || status === 'COMING_UP') return 'Coming up...'
if (status) return status
return 'Unable to connect'
}
@@ -62,17 +63,16 @@ const CheckIcon = () => <CheckCircle2 {...iconProps} className="text-brand" />
const StatusIcon = ({
isLoading,
isHealthy,
isProjectNew,
projectStatus,
}: {
isLoading: boolean
isHealthy: boolean
isProjectNew: boolean
projectStatus?: ProjectServiceStatus
}) => {
if (isHealthy) return <CheckIcon />
if (isLoading) return <LoaderIcon />
if (projectStatus === 'UNHEALTHY') return <AlertIcon />
if (projectStatus === 'COMING_UP') return <LoaderIcon />
if (projectStatus === 'ACTIVE_HEALTHY') return <CheckIcon />
if (isHealthy || projectStatus === 'ACTIVE_HEALTHY') return <CheckIcon />
if (isLoading || isProjectNew || projectStatus === 'COMING_UP') return <LoaderIcon />
return <AlertIcon />
}
@@ -242,15 +242,23 @@ export const ServiceStatus = () => {
const isLoadingChecks = services.some((service) => service.isLoading)
const allServicesOperational = services.every((service) => service.isHealthy)
// Check if project or branch is in a startup state
const isProjectNew =
project?.status === 'COMING_UP' ||
(isBranch &&
(currentBranch?.status === 'CREATING_PROJECT' ||
currentBranch?.status === 'RUNNING_MIGRATIONS' ||
isMigrationLoading))
const anyUnhealthy = services.some(
(service) => !service.isHealthy && service.status !== 'COMING_UP'
)
const anyComingUp = services.some((service) => service.status === 'COMING_UP')
const overallStatusLabel = isLoadingChecks
? 'Checking...'
: anyUnhealthy
: anyUnhealthy && !isProjectNew
? 'Unhealthy'
: anyComingUp || isMigrationLoading
: anyComingUp || isMigrationLoading || (isProjectNew && !allServicesOperational)
? 'Coming up...'
: 'Healthy'
@@ -265,7 +273,9 @@ export const ServiceStatus = () => {
key={`${service.name}-${index}`}
className={cn(
'w-1.5 h-1.5 rounded-full',
service.isLoading || service.status === 'COMING_UP'
service.isLoading ||
service.status === 'COMING_UP' ||
(isProjectNew && !service.isHealthy)
? 'bg-foreground-lighter animate-pulse'
: service.isHealthy
? 'bg-brand'
@@ -290,6 +300,7 @@ export const ServiceStatus = () => {
<StatusIcon
isLoading={service.isLoading}
isHealthy={!!service.isHealthy}
isProjectNew={isProjectNew}
projectStatus={service.status}
/>
<div className="flex-1">
@@ -298,6 +309,7 @@ export const ServiceStatus = () => {
<StatusMessage
isLoading={service.isLoading}
isHealthy={!!service.isHealthy}
isProjectNew={isProjectNew}
status={service.status}
/>
</p>
+35
View File
@@ -0,0 +1,35 @@
import { useQueryClient } from '@tanstack/react-query'
import { analyticsKeys } from './keys'
export const useInvalidateAnalyticsQuery = () => {
const queryClient = useQueryClient()
const invalidateInfraMonitoringQuery = (
ref: string,
{
attribute,
startDate,
endDate,
interval,
databaseIdentifier,
}: {
attribute?: string
startDate?: string
endDate?: string
interval?: string
databaseIdentifier?: string
}
) => {
queryClient.invalidateQueries(
analyticsKeys.infraMonitoring(ref, {
attribute,
startDate,
endDate,
interval,
databaseIdentifier,
})
)
}
return { invalidateInfraMonitoringQuery }
}