github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/webapp/javascript/components/Settings/APIKeys/index.tsx (about) 1 import React, { useEffect } from 'react'; 2 import { useHistory } from 'react-router-dom'; 3 import { formatDistance, formatRelative } from 'date-fns/fp'; 4 import { faTimes } from '@fortawesome/free-solid-svg-icons/faTimes'; 5 import { faPlus } from '@fortawesome/free-solid-svg-icons/faPlus'; 6 7 import Button from '@webapp/ui/Button'; 8 import Icon from '@webapp/ui/Icon'; 9 import TableUI, { BodyRow } from '@webapp/ui/Table'; 10 import type { APIKey, APIKeys } from '@webapp/models/apikeys'; 11 import { useAppDispatch, useAppSelector } from '@webapp/redux/hooks'; 12 import { 13 reloadApiKeys, 14 selectAPIKeys, 15 deleteAPIKey, 16 } from '@webapp/redux/reducers/settings'; 17 import confirmDelete from '@webapp/components/Modals/ConfirmDelete'; 18 import styles from '../SettingsTable.module.scss'; 19 20 const getBodyRows = ( 21 keys: APIKeys, 22 onDelete: (k: APIKey) => void 23 ): BodyRow[] => { 24 const now = new Date(); 25 26 const handleDeleteClick = (key: APIKey) => { 27 confirmDelete({ 28 objectType: 'key', 29 objectName: key.name, 30 onConfirm: () => onDelete(key), 31 }); 32 }; 33 34 return keys.reduce((acc, k) => { 35 acc.push({ 36 cells: [ 37 { value: k.id }, 38 { value: k.name }, 39 { value: k.role }, 40 { value: formatRelative(k.createdAt, now) }, 41 { 42 value: k.expiresAt 43 ? `in ${formatDistance(k.expiresAt, now)}` 44 : 'never', 45 title: k?.expiresAt?.toString(), 46 }, 47 { 48 value: ( 49 <Button 50 type="submit" 51 kind="danger" 52 aria-label="Delete key" 53 onClick={() => handleDeleteClick(k)} 54 > 55 <Icon icon={faTimes} /> 56 </Button> 57 ), 58 align: 'center', 59 }, 60 ], 61 }); 62 return acc; 63 }, [] as BodyRow[]); 64 }; 65 66 const headRow = [ 67 { name: '', label: 'Id', sortable: 0 }, 68 { name: '', label: 'Name', sortable: 0 }, 69 { name: '', label: 'Role', sortable: 0 }, 70 { name: '', label: 'Creation date', sortable: 0 }, 71 { name: '', label: 'Expiration date', sortable: 0 }, 72 ]; 73 74 const ApiKeys = () => { 75 const dispatch = useAppDispatch(); 76 const apiKeys = useAppSelector(selectAPIKeys); 77 const history = useHistory(); 78 79 useEffect(() => { 80 dispatch(reloadApiKeys()); 81 }, []); 82 83 const onDelete = (key: APIKey) => { 84 dispatch(deleteAPIKey(key)) 85 .unwrap() 86 .then(() => { 87 dispatch(reloadApiKeys()); 88 }); 89 }; 90 91 const tableBodyProps = apiKeys 92 ? { type: 'filled' as const, bodyRows: getBodyRows(apiKeys, onDelete) } 93 : { type: 'not-filled' as const, value: '' }; 94 95 return ( 96 <> 97 <h2>API keys</h2> 98 <div> 99 <Button 100 type="submit" 101 kind="secondary" 102 icon={faPlus} 103 onClick={() => history.push('/settings/api-keys/add')} 104 > 105 Add Key 106 </Button> 107 </div> 108 <TableUI 109 table={{ headRow, ...tableBodyProps }} 110 className={styles.settingsTable} 111 /> 112 </> 113 ); 114 }; 115 116 export default ApiKeys;