github.com/argoproj/argo-cd/v3@v3.2.1/ui/src/app/settings/components/projects-list/projects-list.tsx (about) 1 import {FormField, NotificationType, SlidingPanel} from 'argo-ui'; 2 import React, {useRef, useContext} from 'react'; 3 import {Form, FormApi, Text} from 'react-form'; 4 5 import {DataLoader, EmptyState, ErrorNotification, Page, Query} from '../../../shared/components'; 6 import {Context} from '../../../shared/context'; 7 import {Project} from '../../../shared/models'; 8 import {services} from '../../../shared/services'; 9 10 export function ProjectsList() { 11 const formApiRef = useRef<FormApi | null>(null); 12 const ctx = useContext(Context); 13 14 return ( 15 <Page 16 title='Projects' 17 toolbar={{ 18 breadcrumbs: [{title: 'Settings', path: '/settings'}, {title: 'Projects'}], 19 actionMenu: { 20 className: 'fa fa-plus', 21 items: [{title: 'New Project', iconClassName: 'fa fa-plus', action: () => ctx.navigation.goto('.', {add: true}, {replace: true})}] 22 } 23 }}> 24 <div className='projects argo-container'> 25 <DataLoader load={() => services.projects.list()}> 26 {projects => 27 (projects.length > 0 && ( 28 <div className='argo-table-list argo-table-list--clickable'> 29 <div className='argo-table-list__head'> 30 <div className='row'> 31 <div className='columns small-3'>NAME</div> 32 <div className='columns small-6'>DESCRIPTION</div> 33 </div> 34 </div> 35 {projects.map(proj => ( 36 <div className='argo-table-list__row' key={proj.metadata.name} onClick={() => ctx.navigation.goto(`./${proj.metadata.name}`)}> 37 <div className='row'> 38 <div className='columns small-3'> 39 <i className='fa fa-object-group' /> {proj.metadata.name} 40 </div> 41 <div className='columns small-6'>{proj.spec.description}</div> 42 </div> 43 </div> 44 ))} 45 </div> 46 )) || ( 47 <EmptyState icon='fa fa-object-group'> 48 <h4>No projects yet</h4> 49 <h5>Create new projects to group your applications</h5> 50 <button className='argo-button argo-button--base' onClick={() => ctx.navigation.goto('.', {add: true}, {replace: true})}> 51 New project 52 </button> 53 </EmptyState> 54 ) 55 } 56 </DataLoader> 57 </div> 58 <Query> 59 {params => ( 60 <SlidingPanel 61 isShown={params.get('add') === 'true'} 62 onClose={() => ctx.navigation.goto('.', {add: null}, {replace: true})} 63 isMiddle={true} 64 header={ 65 <div> 66 <button onClick={() => formApiRef.current && formApiRef.current.submitForm(null)} className='argo-button argo-button--base'> 67 Create 68 </button>{' '} 69 <button onClick={() => ctx.navigation.goto('.', {add: null}, {replace: true})} className='argo-button argo-button--base-o'> 70 Cancel 71 </button> 72 </div> 73 }> 74 <Form 75 defaultValues={{metadata: {}, spec: {}}} 76 getApi={api => (formApiRef.current = api)} 77 validateError={(p: Project) => ({ 78 'metadata.name': !p.metadata.name && 'Project Name is required' 79 })} 80 onSubmit={async (proj: Project) => { 81 try { 82 await services.projects.create(proj); 83 ctx.navigation.goto(`./${proj.metadata.name}`, {add: null}, {replace: true}); 84 } catch (e) { 85 ctx.notifications.show({ 86 content: <ErrorNotification title='Unable to create project' e={e} />, 87 type: NotificationType.Error 88 }); 89 } 90 }}> 91 {api => ( 92 <form onSubmit={api.submitForm} role='form' className='width-control'> 93 <div className='white-box'> 94 <p>GENERAL</p> 95 <div className='argo-form-row'> 96 <FormField formApi={api} label='Project Name' field='metadata.name' component={Text} /> 97 </div> 98 <div className='argo-form-row'> 99 <FormField formApi={api} label='Description' field='spec.description' component={Text} /> 100 </div> 101 </div> 102 </form> 103 )} 104 </Form> 105 </SlidingPanel> 106 )} 107 </Query> 108 </Page> 109 ); 110 }