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 }