44 CHECKBOX_VALUE ,
55 ConditionalWrap ,
66 Drawer ,
7+ GenericEmptyState ,
78 Progressing ,
89 showError ,
910 stopPropagation ,
@@ -12,9 +13,10 @@ import { ReactComponent as Close } from '../../assets/icons/ic-close.svg'
1213import { ReactComponent as Error } from '../../assets/icons/ic-warning.svg'
1314import { ReactComponent as CheckIcon } from '../../assets/icons/ic-check.svg'
1415import { ReactComponent as Abort } from '../../assets/icons/ic-abort.svg'
16+ import Info from '../../assets/icons/ic-info-outline-grey.svg'
1517import { CreateGroupType , CreateTypeOfAppListType , FilterParentType } from './AppGroup.types'
1618import SearchBar from './SearchBar'
17- import { CreateGroupTabs , CREATE_GROUP_TABS } from './Constants'
19+ import { CreateGroupTabs , CREATE_GROUP_TABS , FILTER_NAME_REGEX } from './Constants'
1820import { toast } from 'react-toastify'
1921import { createEnvGroup } from './AppGroup.service'
2022import { useParams } from 'react-router-dom'
@@ -57,6 +59,18 @@ export default function CreateAppGroup({
5759 }
5860 }
5961
62+ const validateName = ( name : string ) => {
63+ if ( ! name ) {
64+ return
65+ }
66+ if ( ! FILTER_NAME_REGEX . test ( name ) || name . length > 30 ) {
67+ // regex doesnt check of max length = 30
68+ setShowErrorMsg ( true )
69+ } else {
70+ setShowErrorMsg ( false )
71+ }
72+ }
73+
6074 useEffect ( ( ) => {
6175 document . addEventListener ( 'click' , outsideClickHandler )
6276 return ( ) : void => {
@@ -97,8 +111,8 @@ export default function CreateAppGroup({
97111 }
98112
99113 const onInputChange = ( event ) : void => {
100- setShowErrorMsg ( true )
101114 if ( event . target . name === 'name' ) {
115+ validateName ( event . target . value )
102116 setAppGroupName ( event . target . value )
103117 } else {
104118 setAppGroupDescription ( event . target . value )
@@ -157,7 +171,22 @@ export default function CreateAppGroup({
157171 setAuthorizedAppList ( _authorizedAppList )
158172 }
159173
174+ const renderEmptyState = ( title ?: string ) : JSX . Element => {
175+ return (
176+ < GenericEmptyState
177+ title = { title }
178+ image = { Info }
179+ imageClassName = "h-20"
180+ styles = { { height : 'calc(100vh - 420px)' } }
181+ />
182+ )
183+ }
184+
160185 const renderSelectedApps = ( ) : JSX . Element => {
186+ const filteredAuthList = authorizedAppList . filter (
187+ ( app ) =>
188+ selectedAppsMap [ app . id ] && ( ! selectedAppSearchText || app . appName . indexOf ( selectedAppSearchText ) >= 0 ) ,
189+ )
161190 return (
162191 < div >
163192 < SearchBar
@@ -168,26 +197,22 @@ export default function CreateAppGroup({
168197 setSearchApplied = { setSelectedAppSearchApplied }
169198 />
170199 < div >
171- { authorizedAppList
172- . filter (
173- ( app ) =>
174- selectedAppsMap [ app . id ] &&
175- ( ! selectedAppSearchText || app . appName . indexOf ( selectedAppSearchText ) >= 0 ) ,
176- )
177- . map ( ( app ) => {
178- return (
179- < div
180- key = { `selected-app-${ app . id } ` }
181- className = "flex left dc__hover-n50 p-8 fs-13 fw-4 cn-9 selected-app-row cursor"
182- data-app-id = { app . id }
183- onClick = { removeAppSelection }
184- >
185- < CheckIcon className = "icon-dim-16 cursor check-icon scn-6 mr-8" />
186- < Close className = "icon-dim-16 cursor delete-icon mr-8" />
187- < span > { app . appName } </ span >
188- </ div >
189- )
190- } ) }
200+ { filteredAuthList . length <= 0
201+ ? renderEmptyState ( 'No matching results' )
202+ : filteredAuthList . map ( ( app ) => {
203+ return (
204+ < div
205+ key = { `selected-app-${ app . id } ` }
206+ className = "flex left dc__hover-n50 p-8 fs-13 fw-4 cn-9 selected-app-row cursor"
207+ data-app-id = { app . id }
208+ onClick = { removeAppSelection }
209+ >
210+ < CheckIcon className = "icon-dim-16 cursor check-icon scn-6 mr-8" />
211+ < Close className = "icon-dim-16 cursor delete-icon mr-8" />
212+ < span > { app . appName } </ span >
213+ </ div >
214+ )
215+ } ) }
191216 { unauthorizedAppList . length > 0 && (
192217 < div className = "dc__bold ml-4" >
193218 { `You don't have admin/manager pemission for the following ${ filterParentTypeMsg } .` }
@@ -217,6 +242,7 @@ export default function CreateAppGroup({
217242 }
218243
219244 const renderAllApps = ( ) : JSX . Element => {
245+ const filteredAllApps = appList . filter ( ( app ) => ! allAppSearchText || app . appName . indexOf ( allAppSearchText ) >= 0 )
220246 return (
221247 < div >
222248 < SearchBar
@@ -227,36 +253,36 @@ export default function CreateAppGroup({
227253 setSearchApplied = { setAllAppSearchApplied }
228254 />
229255 < div >
230- { appList
231- . filter ( ( app ) => ! allAppSearchText || app . appName . indexOf ( allAppSearchText ) >= 0 )
232- . map ( ( app ) => (
233- < ConditionalWrap
234- condition = { unAuthorizedApps . get ( app . appName ) === true }
235- wrap = { ( children ) => (
236- < Tippy
237- key = { `selected-app-${ app . id } ` }
238- data-testid = "env-tippy"
239- className = "default-tt w-200"
240- arrow = { false }
241- placement = "bottom-start"
242- content = { `You don't have admin/manager pemission for this ${ filterParentTypeMsg } .` }
243- >
244- < div > { children } </ div >
245- </ Tippy >
246- ) }
247- >
248- < Checkbox
249- key = { `app-${ app . id } ` }
250- rootClassName = "fs-13 pt-8 pr-8 pb-8 mb-0-imp dc__hover-n50"
251- isChecked = { unAuthorizedApps . get ( app . appName ) ? false : selectedAppsMap [ app . id ] }
252- value = { CHECKBOX_VALUE . CHECKED }
253- onChange = { ( ) => toggleAppSelection ( app . id ) }
254- disabled = { unAuthorizedApps . get ( app . appName ) ? true : false }
255- >
256- { app . appName }
257- </ Checkbox >
258- </ ConditionalWrap >
259- ) ) }
256+ { filteredAllApps . length <= 0
257+ ? renderEmptyState ( 'No matching results' )
258+ : filteredAllApps . map ( ( app ) => (
259+ < ConditionalWrap
260+ condition = { unAuthorizedApps . get ( app . appName ) === true }
261+ wrap = { ( children ) => (
262+ < Tippy
263+ key = { `selected-app-${ app . id } ` }
264+ data-testid = "env-tippy"
265+ className = "default-tt w-200"
266+ arrow = { false }
267+ placement = "bottom-start"
268+ content = { `You don't have admin/manager pemission for this ${ filterParentTypeMsg } .` }
269+ >
270+ < div > { children } </ div >
271+ </ Tippy >
272+ ) }
273+ >
274+ < Checkbox
275+ key = { `app-${ app . id } ` }
276+ rootClassName = "fs-13 pt-8 pr-8 pb-8 mb-0-imp dc__hover-n50"
277+ isChecked = { unAuthorizedApps . get ( app . appName ) ? false : selectedAppsMap [ app . id ] }
278+ value = { CHECKBOX_VALUE . CHECKED }
279+ onChange = { ( ) => toggleAppSelection ( app . id ) }
280+ disabled = { unAuthorizedApps . get ( app . appName ) ? true : false }
281+ >
282+ { app . appName }
283+ </ Checkbox >
284+ </ ConditionalWrap >
285+ ) ) }
260286 </ div >
261287 </ div >
262288 )
@@ -282,12 +308,17 @@ export default function CreateAppGroup({
282308 )
283309 }
284310
311+ // called when showErrorMsg is true
285312 const nameErrorMessage = ( ) : string => {
286313 if ( ! appGroupName ) {
287314 return 'Group name is required field'
288- } else {
315+ }
316+ if ( appGroupName . length > 30 ) {
289317 return 'Max 30 char is allowed in name'
290318 }
319+ if ( ! FILTER_NAME_REGEX . test ( appGroupName ) ) {
320+ return 'Min 3 chars; Start with alphabet; End with alphanumeric; Use only lowercase; Allowed:(-); Do not use spaces'
321+ }
291322 }
292323
293324 const renderBodySection = ( ) : JSX . Element => {
@@ -310,7 +341,7 @@ export default function CreateAppGroup({
310341 disabled = { selectedAppGroup && ! ! selectedAppGroup . value }
311342 />
312343
313- { showErrorMsg && ( ! appGroupName || appGroupName . length > 30 ) && (
344+ { showErrorMsg && (
314345 < span className = "form__error" >
315346 < Error className = "form__icon form__icon--error" />
316347 { nameErrorMessage ( ) }
@@ -360,6 +391,10 @@ export default function CreateAppGroup({
360391
361392 const handleSave = async ( e ) : Promise < void > => {
362393 e . preventDefault ( )
394+ if ( showErrorMsg ) {
395+ toast . error ( 'Please fix the errors' )
396+ return
397+ }
363398 if ( ! appGroupName || appGroupDescription ?. length > 50 ) {
364399 setShowErrorMsg ( true )
365400 return
0 commit comments