AccessTokenList.tsx 3.7 KB

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