PersonalDropdown.tsx 3.7 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 /><span className="ms-1 d-none d-lg-inline-block">&nbsp;{currentUser.name}</span>
  45. </button>
  46. {/* Menu */}
  47. <div className="dropdown-menu" data-testid="personal-dropdown-menu">
  48. <div className="px-4 pt-3 pb-2 text-center">
  49. <UserPicture user={currentUser} size="lg" noLink noTooltip />
  50. <h5 className="mt-2">
  51. {currentUser.name}
  52. </h5>
  53. <div className="my-2">
  54. <i className="icon-user icon-fw"></i>{currentUser.username}<br />
  55. <i className="icon-envelope icon-fw"></i><span className="grw-email-sm">{currentUser.email}</span>
  56. </div>
  57. <div className="btn-group btn-block mt-2" role="group">
  58. <Link
  59. href={pagePathUtils.userHomepagePath(currentUser)}
  60. className="btn btn-sm btn-outline-secondary col"
  61. data-testid="grw-personal-dropdown-menu-user-home"
  62. >
  63. <i className="icon-fw icon-home"></i>{t('personal_dropdown.home')}
  64. </Link>
  65. <Link
  66. href="/me"
  67. className="btn btn-sm btn-outline-secondary col"
  68. data-testid="grw-personal-dropdown-menu-user-settings"
  69. >
  70. <i className="icon-fw icon-wrench"></i>{t('personal_dropdown.settings')}
  71. </Link>
  72. </div>
  73. </div>
  74. <div className="dropdown-divider"></div>
  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. <div className="dropdown-divider"></div>
  84. <button type="button" className="dropdown-item" onClick={logoutHandler}>
  85. <i className="icon-fw icon-power"></i>{t('Sign out')}
  86. </button>
  87. </div>
  88. </div>
  89. <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />
  90. </>
  91. );
  92. };