2
0

PersonalDropdown.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  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 {
  8. UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
  9. } from 'reactstrap';
  10. import { apiv3Post } from '~/client/util/apiv3-client';
  11. import { toastError } from '~/client/util/toastr';
  12. import { useCurrentUser } from '~/stores/context';
  13. import { SkeletonItem } from './SkeletonItem';
  14. const ProactiveQuestionnaireModal = dynamic(() => import('~/features/questionnaire/client/components/ProactiveQuestionnaireModal'), { ssr: false });
  15. export const PersonalDropdown = (): JSX.Element => {
  16. const { t } = useTranslation('commons');
  17. const { data: currentUser } = useCurrentUser();
  18. const [isQuestionnaireModalOpen, setQuestionnaireModalOpen] = useState(false);
  19. if (currentUser == null) {
  20. return <SkeletonItem />;
  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. <UncontrolledDropdown
  34. direction="end"
  35. >
  36. <DropdownToggle
  37. className="btn btn-primary"
  38. data-testid="personal-dropdown-button"
  39. >
  40. <UserPicture user={currentUser} noLink noTooltip />
  41. </DropdownToggle>
  42. <DropdownMenu
  43. container="body"
  44. data-testid="personal-dropdown-menu"
  45. >
  46. <DropdownItem header>
  47. <div className="mt-2 mb-3">
  48. <UserPicture user={currentUser} size="lg" noLink noTooltip />
  49. </div>
  50. <h5 className="ms-1">{currentUser.name}</h5>
  51. <div className="d-flex align-items-center">
  52. <span className="material-symbols-outlined me-1">person</span>
  53. {currentUser.username}
  54. </div>
  55. <div className="d-flex align-items-center">
  56. <span className="material-symbols-outlined me-1">mail</span>
  57. <span className="grw-email-sm">{currentUser.email}</span>
  58. </div>
  59. </DropdownItem>
  60. <DropdownItem divider />
  61. <DropdownItem>
  62. <Link
  63. href={pagePathUtils.userHomepagePath(currentUser)}
  64. data-testid="grw-personal-dropdown-menu-user-home"
  65. >
  66. <span className="text-muted">
  67. <span className="material-symbols-outlined me-1">home</span>{t('personal_dropdown.home')}
  68. </span>
  69. </Link>
  70. </DropdownItem>
  71. <DropdownItem>
  72. <Link
  73. href="/me"
  74. data-testid="grw-personal-dropdown-menu-user-settings"
  75. >
  76. <span className="text-muted">
  77. <span className="material-symbols-outlined me-1">build</span>{t('personal_dropdown.settings')}
  78. </span>
  79. </Link>
  80. </DropdownItem>
  81. <DropdownItem
  82. data-testid="grw-proactive-questionnaire-modal-toggle-btn"
  83. onClick={() => setQuestionnaireModalOpen(true)}
  84. >
  85. <span className="text-muted">
  86. <span className="material-symbols-outlined me-1">edit</span>{t('personal_dropdown.feedback')}
  87. </span>
  88. </DropdownItem>
  89. <DropdownItem onClick={logoutHandler}>
  90. <span className="text-muted">
  91. <span className="material-symbols-outlined me-1">logout</span>{t('Sign out')}
  92. </span>
  93. </DropdownItem>
  94. </DropdownMenu>
  95. </UncontrolledDropdown>
  96. <ProactiveQuestionnaireModal isOpen={isQuestionnaireModalOpen} onClose={() => setQuestionnaireModalOpen(false)} />
  97. </>
  98. );
  99. };