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,15 @@ export default function CreateAppGroup({
157171 setAuthorizedAppList ( _authorizedAppList )
158172 }
159173
174+ const renderEmptyState = ( title ?: string ) : JSX . Element => {
175+ return < GenericEmptyState title = { title } image = { Info } imageClassName = "h-20" classname = "h-40vh" />
176+ }
177+
160178 const renderSelectedApps = ( ) : JSX . Element => {
179+ const filteredAuthList = authorizedAppList . filter (
180+ ( app ) =>
181+ selectedAppsMap [ app . id ] && ( ! selectedAppSearchText || app . appName . indexOf ( selectedAppSearchText ) >= 0 ) ,
182+ )
161183 return (
162184 < div >
163185 < SearchBar
@@ -168,26 +190,22 @@ export default function CreateAppGroup({
168190 setSearchApplied = { setSelectedAppSearchApplied }
169191 />
170192 < 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- } ) }
193+ { filteredAuthList . length <= 0
194+ ? renderEmptyState ( 'No matching results' )
195+ : filteredAuthList . map ( ( app ) => {
196+ return (
197+ < div
198+ key = { `selected-app-${ app . id } ` }
199+ className = "flex left dc__hover-n50 p-8 fs-13 fw-4 cn-9 selected-app-row cursor"
200+ data-app-id = { app . id }
201+ onClick = { removeAppSelection }
202+ >
203+ < CheckIcon className = "icon-dim-16 cursor check-icon scn-6 mr-8" />
204+ < Close className = "icon-dim-16 cursor delete-icon mr-8" />
205+ < span > { app . appName } </ span >
206+ </ div >
207+ )
208+ } ) }
191209 { unauthorizedAppList . length > 0 && (
192210 < div className = "dc__bold ml-4" >
193211 { `You don't have admin/manager pemission for the following ${ filterParentTypeMsg } .` }
@@ -282,12 +300,17 @@ export default function CreateAppGroup({
282300 )
283301 }
284302
303+ // called when showErrorMsg is true
285304 const nameErrorMessage = ( ) : string => {
286305 if ( ! appGroupName ) {
287306 return 'Group name is required field'
288- } else {
307+ }
308+ if ( appGroupName . length > 30 ) {
289309 return 'Max 30 char is allowed in name'
290310 }
311+ if ( ! FILTER_NAME_REGEX . test ( appGroupName ) ) {
312+ return 'Min 3 chars; Start with alphabet; End with alphanumeric; Use only lowercase; Allowed:(-); Do not use spaces'
313+ }
291314 }
292315
293316 const renderBodySection = ( ) : JSX . Element => {
@@ -310,7 +333,7 @@ export default function CreateAppGroup({
310333 disabled = { selectedAppGroup && ! ! selectedAppGroup . value }
311334 />
312335
313- { showErrorMsg && ( ! appGroupName || appGroupName . length > 30 ) && (
336+ { showErrorMsg && (
314337 < span className = "form__error" >
315338 < Error className = "form__icon form__icon--error" />
316339 { nameErrorMessage ( ) }
@@ -360,6 +383,10 @@ export default function CreateAppGroup({
360383
361384 const handleSave = async ( e ) : Promise < void > => {
362385 e . preventDefault ( )
386+ if ( showErrorMsg ) {
387+ toast . error ( 'Please fix the errors' )
388+ return
389+ }
363390 if ( ! appGroupName || appGroupDescription ?. length > 50 ) {
364391 setShowErrorMsg ( true )
365392 return
0 commit comments