PersonalDropdown.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import { useState } from 'react';
  2. import { pagePathUtils } from '@growi/core/dist/utils';
  3. import { UserPicture } from '@growi/ui/dist/components';
  4. import { useTranslation } from 'next-i18next';
  5. import dynamic from 'next/dynamic';
  6. import Link from 'next/link';
  7. import { apiv3Post } from '~/client/util/apiv3-client';
  8. import { toastError } from '~/client/util/toastr';
  9. import { useCurrentUser } from '~/stores/context';
  10. const ProactiveQuestionnaireModal = dynamic(() => import('~/features/questionnaire/client/components/ProactiveQuestionnaireModal'), { ssr: false });
  11. export const PersonalDropdown = (): JSX.Element => {
  12. const { t } = useTranslation('commons');
  13. const { data: currentUser } = useCurrentUser();
  14. const [isQuestionnaireModalOpen, setQuestionnaireModalOpen] = useState(false);
  15. if (currentUser == null) {
  16. return (
  17. <div className="text-muted text-center mb-5">
  18. <i className="fa fa-2x fa-spinner fa-pulse me-1" />
  19. </div>
  20. );
  21. }
  22. const logoutHandler = async() => {
  23. try {
  24. await apiv3Post('/logout');
  25. window.location.reload();
  26. }
  27. catch (err) {
  28. toastError(err);
  29. }
  30. };
  31. return (
  32. <>
  33. <div className="dropend">
  34. {/* Button */}
  35. {/* remove .dropdown-toggle for hide caret */}
  36. {/* See https://stackoverflow.com/a/44577512/13183572 */}
  37. <button
  38. type="button"
  39. className="btn btn-primary"
  40. data-bs-toggle="dropdown"
  41. data-testid="personal-dropdown-button"
  42. aria-expanded="false"
  43. >
  44. <UserPicture user={currentUser} noLink noTooltip />
  45. </button>
  46. {/* Menu */}
  47. <ul className="dropdown-menu" data-testid="personal-dropdown-menu">
  48. <li className="px-4 pt-3 pb-2">
  49. <UserPicture user={currentUser} size="lg" noLink noTooltip />
  50. <h5>{currentUser.name}</h5>
  51. <div className="d-flex align-items-center">
  52. <i className="icon-user icon-fw"></i>{currentUser.username}
  53. </div>
  54. <div className="d-flex align-items-center">
  55. <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
  56. </div>
  57. </li>
  58. <li className="dropdown-divider"></li>
  59. <li>
  60. <Link
  61. href={pagePathUtils.userHomepagePath(currentUser)}
  62. className="dropdown-item"
  63. data-testid="grw-personal-dropdown-menu-user-home"
  64. >
  65. <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
  66. </Link>
  67. </li>
  68. <li>
  69. <Link
  70. href="/me"
  71. className="dropdown-item"
  72. data-testid="grw-personal-dropdown-menu-user-settings"
  73. >
  74. <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
  75. </Link>
  76. </li>
  77. <li>
  78. <button
  79. data-testid="grw-proactive-questionnaire-modal-toggle-btn"
  80. type="button"
  81. className="dropdown-item"
  82. onClick={() => setQuestionnaireModalOpen(true)}
  83. >
  84. <i className="icon-fw icon-pencil"></i>{t('personal_dropdown.feedback')}
  85. </button>
  86. </li>
  87. <li>
  88. <button type="button" className="dropdown-item" onClick={logoutHandler}>
  89. <i className="icon-fw icon-power"></i>{t('Sign out')}
  90. </button>
  91. </li>
  92. </ul>
  93. </div>
  94. <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />
  95. </>
  96. );
  97. };