@@ -20,6 +20,7 @@ import {
2020 PopoverTrigger ,
2121} from "components/Popover/Popover" ;
2222import { Stack } from "components/Stack/Stack" ;
23+ import { StatusIndicator } from "components/StatusIndicator/StatusIndicator" ;
2324import { type FC , useState } from "react" ;
2425import { createDayString } from "utils/createDayString" ;
2526import { docs } from "utils/docs" ;
@@ -31,7 +32,7 @@ interface ProvisionerGroupProps {
3132 readonly buildInfo ?: BuildInfoResponse ;
3233 readonly keyName ?: string ;
3334 readonly type : ProvisionerGroupType ;
34- readonly provisioners : ProvisionerDaemon [ ] ;
35+ readonly provisioners : readonly ProvisionerDaemon [ ] ;
3536}
3637
3738export const ProvisionerGroup : FC < ProvisionerGroupProps > = ( {
@@ -40,36 +41,65 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
4041 type,
4142 provisioners,
4243} ) => {
43- const [ provisioner ] = provisioners ;
4444 const theme = useTheme ( ) ;
4545
4646 const [ showDetails , setShowDetails ] = useState ( false ) ;
4747
48- const daemonScope = provisioner . tags . scope || "organization" ;
49- const iconScope = daemonScope === "organization" ? < Business /> : < Person /> ;
48+ const firstProvisioner = provisioners [ 0 ] ;
49+ if ( ! firstProvisioner ) {
50+ return null ;
51+ }
5052
51- const provisionerVersion = provisioner . version ;
53+ const daemonScope = firstProvisioner . tags . scope || "organization" ;
5254 const allProvisionersAreSameVersion = provisioners . every (
53- ( provisioner ) => provisioner . version === provisionerVersion ,
55+ ( it ) => it . version === firstProvisioner . version ,
5456 ) ;
55- const upToDate =
56- allProvisionersAreSameVersion && buildInfo ?. version === provisioner . version ;
57+ const provisionerVersion = allProvisionersAreSameVersion
58+ ? firstProvisioner . version
59+ : null ;
5760 const provisionerCount =
5861 provisioners . length === 1
5962 ? "1 provisioner"
6063 : `${ provisioners . length } provisioners` ;
61-
62- const extraTags = Object . entries ( provisioner . tags ) . filter (
64+ const extraTags = Object . entries ( firstProvisioner . tags ) . filter (
6365 ( [ key ] ) => key !== "scope" && key !== "owner" ,
6466 ) ;
6567
68+ let warnings = 0 ;
69+ let provisionersWithWarnings = 0 ;
70+ const provisionersWithWarningInfo = provisioners . map ( ( it ) => {
71+ const outOfDate = Boolean ( buildInfo ) && it . version !== buildInfo ?. version ;
72+ const warningCount = outOfDate ? 1 : 0 ;
73+ warnings += warningCount ;
74+ if ( warnings > 0 ) {
75+ provisionersWithWarnings ++ ;
76+ }
77+
78+ return { ...it , warningCount, outOfDate } ;
79+ } ) ;
80+
81+ const hasWarning = warnings > 0 ;
82+ const warningsCount =
83+ warnings === 0
84+ ? "No warnings"
85+ : warnings === 1
86+ ? "1 warning"
87+ : `${ warnings } warnings` ;
88+ const provisionersWithWarningsCount =
89+ provisionersWithWarnings === 1
90+ ? "1 provisioner"
91+ : `${ provisionersWithWarnings } provisioners` ;
92+
6693 return (
6794 < div
68- css = { {
69- borderRadius : 8 ,
70- border : `1px solid ${ theme . palette . divider } ` ,
71- fontSize : 14 ,
72- } }
95+ css = { [
96+ {
97+ borderRadius : 8 ,
98+ border : `1px solid ${ theme . palette . divider } ` ,
99+ fontSize : 14 ,
100+ } ,
101+ hasWarning && styles . warningBorder ,
102+ ] }
73103 >
74104 < header
75105 css = { {
@@ -80,48 +110,39 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
80110 gap : 24 ,
81111 } }
82112 >
83- < div
84- css = { {
85- display : "flex" ,
86- alignItems : "center" ,
87- gap : 24 ,
88- objectFit : "fill" ,
89- } }
90- >
91- { type === "builtin" && (
92- < div css = { { lineHeight : "160%" } } >
93- < BuiltinProvisionerTitle />
94- < span css = { { color : theme . palette . text . secondary } } >
95- { provisionerCount } — Built-in
96- </ span >
97- </ div >
98- ) }
99- { type === "psk" && (
100- < div css = { { lineHeight : "160%" } } >
101- < PskProvisionerTitle />
102- < span css = { { color : theme . palette . text . secondary } } >
103- { provisionerCount } —{ " " }
104- { allProvisionersAreSameVersion ? (
105- < code > { provisionerVersion } </ code >
106- ) : (
107- < span > Multiple versions</ span >
108- ) }
109- </ span >
110- </ div >
111- ) }
112- { type === "key" && (
113- < div css = { { lineHeight : "160%" } } >
113+ < div css = { { display : "flex" , alignItems : "center" , gap : 16 } } >
114+ < StatusIndicator color = { hasWarning ? "warning" : "success" } />
115+ < div
116+ css = { {
117+ display : "flex" ,
118+ flexDirection : "column" ,
119+ lineHeight : 1.5 ,
120+ } }
121+ >
122+ { type === "builtin" && (
123+ < >
124+ < BuiltinProvisionerTitle />
125+ < span css = { { color : theme . palette . text . secondary } } >
126+ { provisionerCount } — Built-in
127+ </ span >
128+ </ >
129+ ) }
130+
131+ { type === "psk" && < PskProvisionerTitle /> }
132+ { type === "key" && (
114133 < h4 css = { styles . groupTitle } > Key group – { keyName } </ h4 >
134+ ) }
135+ { type !== "builtin" && (
115136 < span css = { { color : theme . palette . text . secondary } } >
116137 { provisionerCount } —{ " " }
117- { allProvisionersAreSameVersion ? (
138+ { provisionerVersion ? (
118139 < code > { provisionerVersion } </ code >
119140 ) : (
120141 < span > Multiple versions</ span >
121142 ) }
122143 </ span >
123- </ div >
124- ) }
144+ ) }
145+ </ div >
125146 </ div >
126147 < div
127148 css = { {
@@ -133,7 +154,10 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
133154 } }
134155 >
135156 < Tooltip title = "Scope" >
136- < Pill size = "lg" icon = { iconScope } >
157+ < Pill
158+ size = "lg"
159+ icon = { daemonScope === "organization" ? < Business /> : < Person /> }
160+ >
137161 < span css = { { textTransform : "capitalize" } } > { daemonScope } </ span >
138162 </ Pill >
139163 </ Tooltip >
@@ -153,16 +177,19 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
153177 flexWrap : "wrap" ,
154178 } }
155179 >
156- { provisioners . map ( ( provisioner ) => (
180+ { provisionersWithWarningInfo . map ( ( provisioner ) => (
157181 < div
158182 key = { provisioner . id }
159- css = { {
160- borderRadius : 8 ,
161- border : `1px solid ${ theme . palette . divider } ` ,
162- fontSize : 14 ,
163- padding : "14px 18px" ,
164- width : 375 ,
165- } }
183+ css = { [
184+ {
185+ borderRadius : 8 ,
186+ border : `1px solid ${ theme . palette . divider } ` ,
187+ fontSize : 14 ,
188+ padding : "14px 18px" ,
189+ width : 375 ,
190+ } ,
191+ provisioner . warningCount > 0 && styles . warningBorder ,
192+ ] }
166193 >
167194 < Stack
168195 direction = "row"
@@ -215,7 +242,10 @@ export const ProvisionerGroup: FC<ProvisionerGroupProps> = ({
215242 color : theme . palette . text . secondary ,
216243 } }
217244 >
218- < span > No warnings from { provisionerCount } </ span >
245+ < span >
246+ { warningsCount } from{ " " }
247+ { hasWarning ? provisionersWithWarningsCount : provisionerCount }
248+ </ span >
219249 < Button
220250 variant = "text"
221251 css = { {
@@ -379,6 +409,10 @@ const PskProvisionerTitle: FC = () => {
379409} ;
380410
381411const styles = {
412+ warningBorder : ( theme ) => ( {
413+ borderColor : theme . roles . warning . fill . outline ,
414+ } ) ,
415+
382416 groupTitle : {
383417 fontWeight : 500 ,
384418 margin : 0 ,
@@ -389,7 +423,7 @@ const styles = {
389423 marginBottom : 0 ,
390424 color : theme . palette . text . primary ,
391425 fontSize : 14 ,
392- lineHeight : "150%" ,
426+ lineHeight : 1.5 ,
393427 fontWeight : 600 ,
394428 } ) ,
395429
0 commit comments