github.com/argoproj/argo-cd@v1.8.7/ui/src/app/settings/components/account-details/account-details.tsx (about) 1 import {ErrorNotification, FormField, NotificationType} from 'argo-ui'; 2 import * as React from 'react'; 3 import {Form, Text} from 'react-form'; 4 import {RouteComponentProps} from 'react-router'; 5 6 import {DataLoader, Page, Timestamp} from '../../../shared/components'; 7 import {Context} from '../../../shared/context'; 8 import {Account, Token} from '../../../shared/models'; 9 import {services} from '../../../shared/services'; 10 11 import {convertExpiresInToSeconds, validExpiresIn} from '../utils'; 12 13 require('./account-details.scss'); 14 15 export const AccountDetails = (props: RouteComponentProps<{name: string}>) => { 16 const ctx = React.useContext(Context); 17 const [newToken, setNewToken] = React.useState(null); 18 const tokensLoaderRef = React.useRef<DataLoader>(); 19 return ( 20 <Page 21 title={props.match.params.name} 22 toolbar={{ 23 breadcrumbs: [{title: 'Settings', path: '/settings'}, {title: 'Accounts', path: '/settings/accounts'}, {title: props.match.params.name}] 24 }}> 25 <p /> 26 <div className='argo-container account-details'> 27 <DataLoader input={props.match.params.name} load={(name: string) => services.accounts.get(name)}> 28 {(account: Account) => ( 29 <React.Fragment> 30 <div className='white-box'> 31 <div className='white-box__details'> 32 <div className='row white-box__details-row'> 33 <div className='columns small-3'>NAME:</div> 34 <div className='columns small-9'>{account.name}</div> 35 </div> 36 <div className='row white-box__details-row'> 37 <div className='columns small-3'>ENABLED:</div> 38 <div className='columns small-9'>{(account.enabled && 'true') || 'false'}</div> 39 </div> 40 <div className='row white-box__details-row'> 41 <div className='columns small-3'>CAPABILITIES:</div> 42 <div className='columns small-9'>{account.capabilities.join(', ')}</div> 43 </div> 44 </div> 45 </div> 46 47 <h4>Tokens</h4> 48 <Form 49 onSubmit={async (params, event, api) => { 50 const expiresIn = convertExpiresInToSeconds(params.expiresIn); 51 const confirmed = await ctx.popup.confirm('Generate new token?', 'Are you sure you want to generate new token?'); 52 if (!confirmed) { 53 return; 54 } 55 try { 56 setNewToken(await services.accounts.createToken(props.match.params.name, params.id, expiresIn)); 57 api.resetAll(); 58 if (tokensLoaderRef.current) { 59 tokensLoaderRef.current.reload(); 60 } 61 } catch (e) { 62 ctx.notifications.show({ 63 content: <ErrorNotification title='Unable to generate new token' e={e} />, 64 type: NotificationType.Error 65 }); 66 } 67 }} 68 validateError={params => ({ 69 expiresIn: !validExpiresIn(params.expiresIn) && 'Must be in the "[0-9]+[smhd]" format' 70 })}> 71 {api => ( 72 <form onSubmit={api.submitForm}> 73 <div className='row argo-table-list__row'> 74 <div className='columns small-10'> 75 <div className='argo-form-row'> 76 <FormField formApi={api} label='Token ID' field='id' component={Text} /> 77 </div> 78 <div className='argo-form-row'> 79 <FormField formApi={api} label='Expires In' field='expiresIn' component={Text} /> 80 </div> 81 </div> 82 <div className='columns small-2'> 83 <div className='argo-form-row'> 84 <a onClick={() => api.submitForm(null)}>Generate New</a> 85 </div> 86 </div> 87 </div> 88 </form> 89 )} 90 </Form> 91 {newToken && ( 92 <div className='white-box account-details__new-token'> 93 <h5>New Token:</h5> 94 <p>{newToken}</p> 95 <i className='fa fa-times account-details__remove-token' title='Remove' onClick={() => setNewToken(null)} /> 96 </div> 97 )} 98 <DataLoader ref={tokensLoaderRef} input={props.match.params.name} load={(name: string) => services.accounts.get(name).then(acc => acc.tokens || [])}> 99 {(tokens: Token[]) => 100 (tokens.length > 0 && ( 101 <div className='argo-table-list'> 102 <div className='argo-table-list__head'> 103 <div className='row'> 104 <div className='columns small-4'>ID</div> 105 <div className='columns small-4'>ISSUED AT</div> 106 <div className='columns small-4'>EXPIRES AT</div> 107 </div> 108 </div> 109 {tokens.map(token => ( 110 <div className='argo-table-list__row' key={token.id}> 111 <div className='row'> 112 <div className='columns small-4'>{token.id}</div> 113 <div className='columns small-4'> 114 <Timestamp date={token.issuedAt * 1000} /> 115 </div> 116 <div className='columns small-4'> 117 {(token.expiresAt && <Timestamp date={token.expiresAt * 1000} />) || <span>Never</span>} 118 <i 119 className='fa fa-times account-details__remove-token' 120 title='Delete' 121 onClick={async () => { 122 const confirmed = await ctx.popup.confirm( 123 'Delete Token?', 124 `Are you sure you want to delete token '${token.id}?'` 125 ); 126 if (!confirmed) { 127 return; 128 } 129 130 try { 131 await services.accounts.deleteToken(props.match.params.name, token.id); 132 if (tokensLoaderRef.current) { 133 tokensLoaderRef.current.reload(); 134 } 135 } catch (e) { 136 ctx.notifications.show({ 137 content: <ErrorNotification title='Unable to delete token token' e={e} />, 138 type: NotificationType.Error 139 }); 140 } 141 }} 142 /> 143 </div> 144 </div> 145 </div> 146 ))} 147 </div> 148 )) || ( 149 <div className='white-box'> 150 <p>Account has no tokens. Click 'Generate New' to create one.</p> 151 </div> 152 ) 153 } 154 </DataLoader> 155 </React.Fragment> 156 )} 157 </DataLoader> 158 </div> 159 </Page> 160 ); 161 };