import { Loader } from 'lucide-react'; import clsx from 'clsx'; import UpdatesAvailable from '@/assets/ico/icon_updates-available.svg?c'; import UpToDate from '@/assets/ico/icon_up-to-date.svg?c'; import { Icon } from '@@/Icon'; import { ContainerImageStatusValue } from '../../queries/useContainerImageStatus'; export interface Props { status?: ContainerImageStatusValue; isLoading?: boolean; /** * When provided, the "Update available" badge becomes a button that triggers * an image update. Omit it to keep the plain, non-interactive span. */ onUpdateClick?: () => void; /** * When provided, the "Up to date" badge becomes a button that re-checks the * registry for a newer image. Omit it to keep the plain span. */ onRecheckClick?: () => void; /** Drives the spinner shown on the "Up to date" badge while a recheck runs. */ isRechecking?: boolean; /** * While an update is in flight the "Update available" button shows a pending * "Updating..." affordance and is disabled, preventing a double-submit (parity * with the details-view "Update now" button). */ isUpdating?: boolean; } const badgeClass = 'inline-flex items-center gap-1 whitespace-nowrap'; // Reset the native button chrome so the interactive badge looks identical to the // plain span, while staying keyboard-focusable with a visible focus ring. const interactiveClass = 'cursor-pointer border-0 bg-transparent p-0 hover:underline focus:outline-none focus-visible:rounded-sm focus-visible:ring-2 focus-visible:ring-blue-5'; /** * CE container image update badge. * * Renders "Update available" when the image is outdated, an up-to-date indicator * when it matches the registry, a spinner while the status is being fetched, and * nothing for neutral states (skipped/error/processing/preparing) so that * undetectable images don't clutter the UI. * * The two visible states become interactive when the matching handler is passed: * clicking "Update available" applies the image update, clicking "Up to date" * re-checks the registry (showing a spinner meanwhile). Without a handler each * stays a plain span so non-interactive callers are unaffected. */ export function UpdateStatusBadge({ status, isLoading, onUpdateClick, onRecheckClick, isRechecking, isUpdating, }: Props) { if (isLoading) { return ( ); } if (status === 'outdated') { const content = isUpdating ? ( <> Updating... ) : ( <> Update available ); if (onUpdateClick) { return ( ); } return {content}; } if (status === 'updated') { const content = ( <> Up to date ); if (onRecheckClick) { return ( ); } return {content}; } // skipped / error / processing / preparing / undefined -> neutral, nothing to show. return null; }