AccessTokenList.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import React, { useState } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import {
  4. Modal, ModalHeader, ModalBody, ModalFooter, Button,
  5. } from 'reactstrap';
  6. import type { IResGetAccessToken } from '~/interfaces/access-token';
  7. type AccessTokenListProps = {
  8. accessTokens: IResGetAccessToken[];
  9. deleteHandler?: (tokenId: string) => void;
  10. }
  11. export const AccessTokenList = React.memo((props: AccessTokenListProps): JSX.Element => {
  12. const { t } = useTranslation();
  13. const { accessTokens, deleteHandler } = props;
  14. const [tokenToDelete, setTokenToDelete] = useState<string | null>(null);
  15. const handleDeleteClick = (tokenId: string) => {
  16. setTokenToDelete(tokenId);
  17. };
  18. const handleConfirmDelete = () => {
  19. if (tokenToDelete != null && deleteHandler != null) {
  20. deleteHandler(tokenToDelete);
  21. setTokenToDelete(null);
  22. }
  23. };
  24. const toggleModal = () => {
  25. setTokenToDelete(null);
  26. };
  27. return (
  28. <>
  29. <div className="table">
  30. <table className="table table-bordered">
  31. <thead>
  32. <tr>
  33. <th>{t('page_me_access_token.description')}</th>
  34. <th>{t('page_me_access_token.expiredAt')}</th>
  35. <th>{t('page_me_access_token.scope')}</th>
  36. <th>{t('page_me_access_token.action')}</th>
  37. </tr>
  38. </thead>
  39. <tbody>
  40. {(accessTokens.length === 0)
  41. ? (
  42. <tr>
  43. <td colSpan={4} className="text-center">
  44. {t('page_me_access_token.no_tokens_found')}
  45. </td>
  46. </tr>
  47. )
  48. : (
  49. <>{
  50. accessTokens.map(token => (
  51. <tr key={token._id}>
  52. <td className="text-break">{token.description}</td>
  53. <td>{token.expiredAt.toString().split('T')[0]}</td>
  54. <td>{token.scopes.join(', ')}</td>
  55. <td>
  56. <button
  57. className="btn btn-danger"
  58. type="button"
  59. onClick={() => handleDeleteClick(token._id)}
  60. data-testid="grw-accesstoken-delete-button"
  61. >
  62. {t('Delete')}
  63. </button>
  64. </td>
  65. </tr>
  66. ))
  67. }
  68. </>
  69. )}
  70. </tbody>
  71. </table>
  72. </div>
  73. {/* Confirmation Modal using Reactstrap */}
  74. <Modal isOpen={tokenToDelete !== null} toggle={toggleModal} centered>
  75. <ModalHeader tag="h4" toggle={toggleModal} className="bg-danger text-white">
  76. <span className="material-symbols-outlined me-1">warning</span>
  77. {t('Warning')}
  78. </ModalHeader>
  79. <ModalBody>
  80. <p>{t('page_me_access_token.modal.message')}</p>
  81. <p className="text-danger fw-bold">{t('page_me_access_token.modal.alert')}</p>
  82. </ModalBody>
  83. <ModalFooter>
  84. <Button color="secondary" onClick={toggleModal} data-testid="grw-accesstoken-cancel-button-in-modal">
  85. {t('Cancel')}
  86. </Button>
  87. <Button color="danger" onClick={handleConfirmDelete} data-testid="grw-accesstoken-delete-button-in-modal">
  88. {t('page_me_access_token.modal.delete_token')}
  89. </Button>
  90. </ModalFooter>
  91. </Modal>
  92. </>
  93. );
  94. });
  95. AccessTokenList.displayName = 'AccessTokenList';