PersonalDropdown.tsx 3.5 KB

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