github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/web/src/ClusterStatusDialog.tsx (about)

     1  import React from "react"
     2  import styled from "styled-components"
     3  import { ReactComponent as HealthySvg } from "./assets/svg/checkmark-small.svg"
     4  import { ReactComponent as UnhealthySvg } from "./assets/svg/close.svg"
     5  import FloatDialog, {
     6    FloatDialogProps,
     7    Title as FloatDialogTitle,
     8  } from "./FloatDialog"
     9  import SrOnly from "./SrOnly"
    10  import { Color, FontSize, SizeUnit } from "./style-helpers"
    11  import { Cluster } from "./types"
    12  
    13  export type ClusterStatusDialogProps = {
    14    clusterConnection?: Cluster
    15  } & Pick<FloatDialogProps, "open" | "onClose" | "anchorEl">
    16  
    17  export const DEFAULT_CLUSTER_NAME = "default"
    18  export const CLUSTER_STATUS_HEALTHY = "Healthy"
    19  
    20  const ClusterHeading = styled.figure`
    21    align-items: top;
    22    display: flex;
    23    flex-direction: row;
    24    justify-content: flex-start;
    25    margin: 0;
    26  `
    27  
    28  const statusIconMixin = `
    29  flex-shrink: 0;
    30  height: ${SizeUnit(0.4)};
    31  padding-right: ${SizeUnit(0.4)};
    32  padding-top: ${SizeUnit(1 / 4)};
    33  width: ${SizeUnit(0.4)};
    34  `
    35  
    36  const HealthyIcon = styled(HealthySvg)`
    37    ${statusIconMixin}
    38    .fillStd {
    39      fill: ${Color.green};
    40    }
    41  `
    42  
    43  const UnhealthyIcon = styled(UnhealthySvg)`
    44    ${statusIconMixin}
    45    .fillStd {
    46      fill: ${Color.red};
    47    }
    48  `
    49  
    50  const ClusterText = styled(FloatDialogTitle)`
    51    line-height: unset;
    52  `
    53  
    54  const ClusterType = styled.span`
    55    display: block;
    56    text-decoration: underline;
    57    text-underline-position: under;
    58  `
    59  
    60  const ClusterStatus = styled.span`
    61    display: block;
    62    font-size: ${FontSize.small};
    63    text-decoration: none;
    64  `
    65  
    66  const ClusterPropertyName = styled.dt``
    67  
    68  const ClusterPropertyDetail = styled.dd`
    69    margin-left: unset;
    70  `
    71  
    72  const ClusterPropertyList = styled.dl`
    73    margin: unset;
    74  
    75    ${ClusterPropertyName},
    76    ${ClusterPropertyDetail} {
    77      display: inline-block;
    78      width: 50%;
    79    }
    80  `
    81  
    82  export function getDefaultCluster(clusters?: Cluster[]): Cluster | undefined {
    83    if (!clusters || !clusters.length) {
    84      return
    85    }
    86  
    87    return clusters.find(
    88      (connection) => connection.metadata?.name === DEFAULT_CLUSTER_NAME
    89    )
    90  }
    91  
    92  function ClusterProperty({
    93    displayName,
    94    details,
    95  }: {
    96    displayName: string
    97    details?: string
    98  }) {
    99    if (!details) {
   100      return null
   101    }
   102  
   103    return (
   104      <>
   105        <ClusterPropertyName>{displayName}</ClusterPropertyName>
   106        <ClusterPropertyDetail>{details}</ClusterPropertyDetail>
   107      </>
   108    )
   109  }
   110  
   111  function K8sClusterProperties({
   112    clusterStatus,
   113  }: {
   114    clusterStatus?: Cluster["status"]
   115  }) {
   116    if (!clusterStatus) {
   117      return null
   118    }
   119  
   120    const k8sInfo = clusterStatus?.connection?.kubernetes
   121    return (
   122      <ClusterPropertyList>
   123        <ClusterProperty displayName="Product" details={k8sInfo?.product} />
   124        <ClusterProperty displayName="Context" details={k8sInfo?.context} />
   125        <ClusterProperty displayName="Namespace" details={k8sInfo?.namespace} />
   126        <ClusterProperty
   127          displayName="Architecture"
   128          details={clusterStatus?.arch}
   129        />
   130        <ClusterProperty displayName="Version" details={clusterStatus?.version} />
   131        {/* TODO (lizz): Decide how to display different registry info, ie fromContainerRuntime */}
   132        <ClusterProperty
   133          displayName="Local registry"
   134          details={clusterStatus?.registry?.host}
   135        />
   136      </ClusterPropertyList>
   137    )
   138  }
   139  
   140  export function ClusterStatusDialog(props: ClusterStatusDialogProps) {
   141    const { open, onClose, anchorEl, clusterConnection } = props
   142  
   143    if (!clusterConnection) {
   144      return null
   145    }
   146  
   147    const clusterStatus =
   148      clusterConnection.status?.error ?? CLUSTER_STATUS_HEALTHY
   149    const clusterStatusIcon =
   150      clusterStatus === CLUSTER_STATUS_HEALTHY ? (
   151        <HealthyIcon role="presentation" data-testid="healthy-icon" />
   152      ) : (
   153        <UnhealthyIcon role="presentation" data-testid="unhealthy-icon" />
   154      )
   155  
   156    const clusterTitle = (
   157      <ClusterHeading>
   158        {clusterStatusIcon}
   159        <ClusterText>
   160          <ClusterType>Kubernetes</ClusterType>
   161          <ClusterStatus>
   162            <SrOnly>Status:</SrOnly> {clusterStatus}
   163          </ClusterStatus>
   164        </ClusterText>
   165      </ClusterHeading>
   166    )
   167  
   168    return (
   169      <FloatDialog
   170        id="cluster"
   171        title={clusterTitle}
   172        open={open}
   173        onClose={onClose}
   174        anchorEl={anchorEl}
   175      >
   176        <K8sClusterProperties clusterStatus={clusterConnection.status} />
   177      </FloatDialog>
   178    )
   179  }