mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Small QoL improvemnets for branch management (#43633)
## Context Couple of small QoL improvements for branch management from internal feedback - Use `TimestampInfo` component for branch rows here <img width="786" height="395" alt="image" src="https://github.com/user-attachments/assets/639381e3-359b-4b11-9854-623d8cc7584f" /> - Workflow logs dialog will show branch name + use `TimestampInfo` component as well for workflow runs created at <img width="836" height="462" alt="image" src="https://github.com/user-attachments/assets/1d2af7fe-9445-47ae-a1a3-a5dcf0fecf4c" /> - Selecting a workflow run will then show the run created at in the header description + use `TimestampInfo` component <img width="829" height="256" alt="image" src="https://github.com/user-attachments/assets/9fe65f21-e948-4f49-a9ba-46acd9505f2f" />
This commit is contained in:
1 parent
2f9921bd81
commit
0fdfd8aaff
3 files changed
+57
-40
No files matched your search
@@ -1,6 +1,5 @@
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
import { ActionName, ActionStatus, type ActionRunStep } from 'data/actions/action-runs-query'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import { Badge, StatusIcon, Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
|
||||
export interface ActionStatusBadgeProps {
|
||||
@@ -44,15 +43,12 @@ export const ActionStatusBadgeCondensed = ({
|
||||
}
|
||||
|
||||
const isUnhealthy = UNHEALTHY_STATUES.includes(status)
|
||||
const isWaiting = WAITING_STATUSES.includes(status)
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<TooltipTrigger asChild>
|
||||
<Badge variant={isUnhealthy ? 'destructive' : 'default'} className="gap-1.5">
|
||||
{(isUnhealthy || isWaiting) && (
|
||||
<StatusIcon variant={isUnhealthy ? 'destructive' : 'default'} hideBackground />
|
||||
)}
|
||||
{isUnhealthy && <StatusIcon variant="destructive" hideBackground />}
|
||||
{children}
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { Github } from 'lucide-react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
|
||||
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
||||
import type { Branch } from 'data/branches/branches-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { Github } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { PropsWithChildren, ReactNode } from 'react'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
|
||||
import { TimestampInfo } from 'ui-patterns'
|
||||
import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { WorkflowLogs } from './WorkflowLogs'
|
||||
|
||||
interface BranchManagementSectionProps {
|
||||
@@ -137,12 +138,14 @@ export const BranchRow = ({
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-foreground-lighter">
|
||||
{daysFromNow > 1
|
||||
? `Updated on ${formattedUpdatedAt}`
|
||||
: `Updated ${formattedTimeFromNow}`}
|
||||
{daysFromNow > 1 ? 'Updated on' : 'Updated'}{' '}
|
||||
<TimestampInfo
|
||||
utcTimestamp={branch.updated_at}
|
||||
label={daysFromNow <= 1 ? formattedTimeFromNow : undefined}
|
||||
/>
|
||||
</p>
|
||||
)}
|
||||
<WorkflowLogs projectRef={branch.project_ref} status={branch.status} />
|
||||
<WorkflowLogs branch={branch} />
|
||||
{rowActions}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import dayjs from 'dayjs'
|
||||
import { groupBy } from 'lodash'
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
|
||||
import AlertError from 'components/ui/AlertError'
|
||||
import { ActionRunData } from 'data/actions/action-detail-query'
|
||||
import { useActionRunLogsQuery } from 'data/actions/action-logs-query'
|
||||
@@ -12,6 +7,10 @@ import {
|
||||
type ActionStatus,
|
||||
} from 'data/actions/action-runs-query'
|
||||
import type { Branch } from 'data/branches/branches-query'
|
||||
import dayjs from 'dayjs'
|
||||
import { groupBy } from 'lodash'
|
||||
import { ArrowLeft, ArrowRight } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import {
|
||||
Button,
|
||||
cn,
|
||||
@@ -25,13 +24,13 @@ import {
|
||||
DialogTrigger,
|
||||
StatusIcon,
|
||||
} from 'ui'
|
||||
import { GenericSkeletonLoader } from 'ui-patterns'
|
||||
import { GenericSkeletonLoader, TimestampInfo } from 'ui-patterns'
|
||||
|
||||
import { ActionStatusBadge, ActionStatusBadgeCondensed, STATUS_TO_LABEL } from './ActionStatusBadge'
|
||||
import BranchStatusBadge from './BranchStatusBadge'
|
||||
|
||||
interface WorkflowLogsProps {
|
||||
projectRef: string
|
||||
status: Branch['status']
|
||||
branch: Branch
|
||||
}
|
||||
|
||||
type StatusType = Branch['status']
|
||||
@@ -39,7 +38,8 @@ type StatusType = Branch['status']
|
||||
const HEALTHY_STATUSES: StatusType[] = ['FUNCTIONS_DEPLOYED', 'MIGRATIONS_PASSED']
|
||||
const UNHEALTHY_STATUSES: StatusType[] = ['MIGRATIONS_FAILED', 'FUNCTIONS_FAILED']
|
||||
|
||||
export const WorkflowLogs = ({ projectRef, status }: WorkflowLogsProps) => {
|
||||
export const WorkflowLogs = ({ branch }: WorkflowLogsProps) => {
|
||||
const { project_ref: projectRef, status, name } = branch
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
const {
|
||||
@@ -84,13 +84,22 @@ export const WorkflowLogs = ({ projectRef, status }: WorkflowLogsProps) => {
|
||||
|
||||
<DialogContent size="xlarge">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Workflow Logs</DialogTitle>
|
||||
<DialogDescription>Select a workflow run to view logs</DialogDescription>
|
||||
<DialogTitle>Workflow logs for {name}</DialogTitle>
|
||||
<DialogDescription>
|
||||
{!selectedWorkflowRun ? (
|
||||
'Select a workflow run to view logs'
|
||||
) : (
|
||||
<>
|
||||
Run created at{' '}
|
||||
<TimestampInfo className="text-sm" utcTimestamp={selectedWorkflowRun.created_at} />
|
||||
</>
|
||||
)}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<DialogSectionSeparator />
|
||||
|
||||
<DialogSection className={cn('px-0', isWorkflowRunLogsSuccess ? 'py-0 pt-2' : '!py-0')}>
|
||||
<DialogSection className={cn('!px-0', isWorkflowRunLogsSuccess ? 'py-0 pt-2' : '!py-0')}>
|
||||
{!selectedWorkflowRun ? (
|
||||
<>
|
||||
{isWorkflowRunsLoading && <GenericSkeletonLoader className="py-4" />}
|
||||
@@ -103,7 +112,7 @@ export const WorkflowLogs = ({ projectRef, status }: WorkflowLogsProps) => {
|
||||
(workflowRuns.length > 0 ? (
|
||||
<ul className="divide-y">
|
||||
{workflowRuns.map((workflowRun) => (
|
||||
<li key={workflowRun.id} className="py-3">
|
||||
<li key={workflowRun.id} className="px-4 py-3">
|
||||
<button
|
||||
type="button"
|
||||
disabled={workflowRun.id === projectRef}
|
||||
@@ -116,9 +125,11 @@ export const WorkflowLogs = ({ projectRef, status }: WorkflowLogsProps) => {
|
||||
) : (
|
||||
<BranchStatusBadge status={status} />
|
||||
)}
|
||||
<span className="text-sm">
|
||||
{dayjs(workflowRun.created_at).format('DD MMM, YYYY HH:mm')}
|
||||
</span>
|
||||
|
||||
<TimestampInfo
|
||||
className="text-sm"
|
||||
utcTimestamp={workflowRun.created_at}
|
||||
/>
|
||||
</div>
|
||||
{workflowRun.id !== projectRef && <ArrowRight size={16} />}
|
||||
</button>
|
||||
@@ -132,7 +143,7 @@ export const WorkflowLogs = ({ projectRef, status }: WorkflowLogsProps) => {
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-col gap-2 py-2">
|
||||
<div className="px-4 flex flex-col gap-2 py-2">
|
||||
<Button
|
||||
onClick={() => setSelectedWorkflowRun(undefined)}
|
||||
type="text"
|
||||
@@ -180,13 +191,20 @@ function RunSteps({ steps }: { steps: Array<ActionRunStep> }) {
|
||||
{numberFailedSteps - 1} more
|
||||
</ActionStatusBadgeCondensed>
|
||||
)}
|
||||
{(Object.keys(stepsByStatus) as Array<ActionStatus>)
|
||||
.filter((status) => status !== 'DEAD')
|
||||
.map((status) => (
|
||||
<ActionStatusBadgeCondensed key={status} status={status} details={stepsByStatus[status]}>
|
||||
{stepsByStatus[status].length} {STATUS_TO_LABEL[status]}
|
||||
</ActionStatusBadgeCondensed>
|
||||
))}
|
||||
|
||||
<div className="flex items-center gap-x-2">
|
||||
{(Object.keys(stepsByStatus) as Array<ActionStatus>)
|
||||
.filter((status) => status !== 'DEAD')
|
||||
.map((status) => (
|
||||
<ActionStatusBadgeCondensed
|
||||
key={status}
|
||||
status={status}
|
||||
details={stepsByStatus[status]}
|
||||
>
|
||||
{stepsByStatus[status].length} {STATUS_TO_LABEL[status]}
|
||||
</ActionStatusBadgeCondensed>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user