feat: branching statuses first pass (#20631)

* feat: branching statuses first pass

* handle error

* use warning icon
This commit is contained in:
Alaister Young authored and GitHub committed 2024-02-29 20:18:00 +11:00
1 parent 1bff39b956
commit a8ee3a212b
4 files changed
+114 -10

No files matched your search

@@ -1,7 +1,14 @@
import { useParams } from 'common'
import dayjs from 'dayjs'
import { GitPullRequest } from 'lucide-react'
import Link from 'next/link'
import { PropsWithChildren, ReactNode } from 'react'
import { useInView } from 'react-intersection-observer'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { useBranchQuery } from 'data/branches/branch-query'
import { Branch } from 'data/branches/branches-query'
import { GitHubPullRequest } from 'data/integrations/integrations-github-pull-requests-query'
import {
Badge,
Button,
@@ -15,11 +22,7 @@ import {
IconShield,
IconTrash,
} from 'ui'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import { Branch } from 'data/branches/branches-query'
import { GitHubPullRequest } from 'data/integrations/integrations-github-pull-requests-query'
import { GitPullRequest } from 'lucide-react'
import BranchStatusBadge from './BranchStatusBadge'
interface BranchManagementSectionProps {
header: string
@@ -86,8 +89,8 @@ export const BranchRow = ({
generateCreatePullRequestURL,
onSelectDeleteBranch,
}: BranchRowProps) => {
const { ref } = useParams()
const isActive = ref === branch?.project_ref
const { ref: projectRef } = useParams()
const isActive = projectRef === branch?.project_ref
const daysFromNow = dayjs().diff(dayjs(branch.updated_at), 'day')
const formattedTimeFromNow = dayjs(branch.updated_at).fromNow()
@@ -96,8 +99,23 @@ export const BranchRow = ({
const createPullRequestURL =
generateCreatePullRequestURL?.(branch.git_branch) ?? 'https://github.com'
const { ref, inView } = useInView()
const { data } = useBranchQuery(
{ projectRef, id: branch.id },
{
enabled: inView,
refetchInterval(data) {
if (data?.status !== 'ACTIVE_HEALTHY') {
return 1000 * 3 // 3 seconds
}
return false
},
}
)
return (
<div className="w-full flex items-center justify-between px-6 py-2.5">
<div className="w-full flex items-center justify-between px-6 py-2.5" ref={ref}>
<div className="flex items-center gap-x-4">
<Button
asChild
@@ -110,6 +128,7 @@ export const BranchRow = ({
</Link>
</Button>
{isActive && <Badge color="slate">Current</Badge>}
{data?.status !== undefined && <BranchStatusBadge status={data.status} />}
<p className="text-xs text-foreground-lighter">
{daysFromNow > 1 ? `Updated on ${formattedUpdatedAt}` : `Updated ${formattedTimeFromNow}`}
</p>
@@ -155,7 +174,7 @@ export const BranchRow = ({
<Button type="text" icon={<IconMoreVertical />} className="px-1" />
</DropdownMenuTrigger>
<DropdownMenuContent className="p-0 w-56" side="bottom" align="end">
<Link passHref href={`/project/${ref}/settings/integrations`}>
<Link passHref href={`/project/${projectRef}/settings/integrations`}>
<DropdownMenuItem asChild className="gap-x-2">
<a>Change production branch</a>
</DropdownMenuItem>
@@ -0,0 +1,46 @@
import { WarningIcon } from 'components/ui/Icons'
import { BranchData } from 'data/branches/branch-query'
import { ClockIcon } from 'lucide-react'
import { Badge } from 'ui'
type Status = BranchData['status']
export interface BranchStatusBadgeProps {
status: Status
}
const UNHEALTHY_STATUES: Status[] = ['ACTIVE_UNHEALTHY', 'INIT_FAILED', 'UNKNOWN']
const WAITING_STATUSES: Status[] = ['COMING_UP', 'GOING_DOWN', 'PAUSING', 'RESTORING', 'UPGRADING']
const STATUS_TO_LABEL: Record<Status, string> = {
ACTIVE_HEALTHY: 'Healthy',
ACTIVE_UNHEALTHY: 'Unhealthy',
INIT_FAILED: 'Init failed',
UNKNOWN: 'Unknown',
COMING_UP: 'Coming up',
GOING_DOWN: 'Going down',
INACTIVE: 'Inactive',
PAUSING: 'Pausing',
REMOVED: 'Removed',
RESTORING: 'Restoring',
UPGRADING: 'Upgrading',
}
const BranchStatusBadge = ({ status }: BranchStatusBadgeProps) => {
if (status === 'ACTIVE_HEALTHY') {
return null
}
const isUnhealthy = UNHEALTHY_STATUES.includes(status)
const isWaiting = WAITING_STATUSES.includes(status)
return (
<Badge color={isUnhealthy ? 'red' : 'slate'} className="flex items-center gap-1.5">
{isUnhealthy && <WarningIcon className="w-3 h-3" />}
{isWaiting && <ClockIcon size={12} />}
<span>{STATUS_TO_LABEL[status]}</span>
</Badge>
)
}
export default BranchStatusBadge
+38
View File
@@ -0,0 +1,38 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query'
import { get, handleError } from 'data/fetchers'
import { ResponseError } from 'types'
import { branchKeys } from './keys'
export type BranchVariables = {
projectRef?: string
id?: string
}
export async function getBranch({ id }: BranchVariables, signal?: AbortSignal) {
if (!id) throw new Error('id is required')
const { data, error } = await get(`/v1/branches/{branch_id}`, {
params: { path: { branch_id: id } },
signal,
})
if (error) handleError(error)
return data
}
export type BranchData = Awaited<ReturnType<typeof getBranch>>
export type BranchError = ResponseError
export const useBranchQuery = <TData = BranchData>(
{ projectRef, id }: BranchVariables,
{ enabled = true, ...options }: UseQueryOptions<BranchData, BranchError, TData> = {}
) =>
useQuery<BranchData, BranchError, TData>(
branchKeys.detail(projectRef, id),
({ signal }) => getBranch({ id }, signal),
{
enabled: enabled && typeof id !== 'undefined',
...options,
}
)
+2 -1
View File
@@ -1,4 +1,5 @@
export const branchKeys = {
list: (projectRef: string | undefined) => ['projects', projectRef, 'branches'] as const,
detail: (id: string | undefined) => ['branches', id] as const,
detail: (projectRef: string | undefined, id: string | undefined) =>
['projects', projectRef, 'branches', id] as const,
}