new-project-button.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import { useCallback, useState } from 'react'
  2. import { Dropdown, MenuItem } from 'react-bootstrap'
  3. import { useTranslation } from 'react-i18next'
  4. import { ExposedSettings } from '../../../../../types/exposed-settings'
  5. import type { PortalTemplate } from '../../../../../types/portal-template'
  6. import ControlledDropdown from '../../../shared/components/controlled-dropdown'
  7. import getMeta from '../../../utils/meta'
  8. import NewProjectButtonModal, {
  9. NewProjectButtonModalVariant,
  10. } from './new-project-button/new-project-button-modal'
  11. import AddAffiliation, { useAddAffiliation } from './add-affiliation'
  12. import { Nullable } from '../../../../../types/utils'
  13. import { sendMB } from '../../../infrastructure/event-tracking'
  14. type SendTrackingEvent = {
  15. dropdownMenu: string
  16. dropdownOpen: boolean
  17. institutionTemplateName?: string
  18. }
  19. type Segmentation = SendTrackingEvent & {
  20. 'project-dashboard-react': 'enabled'
  21. }
  22. type ModalMenuClickOptions = {
  23. modalVariant: NewProjectButtonModalVariant
  24. dropdownMenuEvent: string
  25. }
  26. type NewProjectButtonProps = {
  27. id: string
  28. buttonText?: string
  29. className?: string
  30. menuClassName?: string
  31. trackingKey?: string
  32. showAddAffiliationWidget?: boolean
  33. }
  34. function NewProjectButton({
  35. id,
  36. buttonText,
  37. className,
  38. menuClassName,
  39. trackingKey,
  40. showAddAffiliationWidget,
  41. }: NewProjectButtonProps) {
  42. const { t } = useTranslation()
  43. const { templateLinks } = getMeta('ol-ExposedSettings') as ExposedSettings
  44. const [modal, setModal] =
  45. useState<Nullable<NewProjectButtonModalVariant>>(null)
  46. const portalTemplates = getMeta('ol-portalTemplates') as PortalTemplate[]
  47. const { show: enableAddAffiliationWidget } = useAddAffiliation()
  48. const sendTrackingEvent = useCallback(
  49. ({
  50. dropdownMenu,
  51. dropdownOpen,
  52. institutionTemplateName,
  53. }: SendTrackingEvent) => {
  54. if (trackingKey) {
  55. let segmentation: Segmentation = {
  56. 'project-dashboard-react': 'enabled',
  57. dropdownMenu,
  58. dropdownOpen,
  59. }
  60. if (institutionTemplateName) {
  61. segmentation = {
  62. ...segmentation,
  63. institutionTemplateName,
  64. }
  65. }
  66. sendMB(trackingKey, segmentation)
  67. }
  68. },
  69. [trackingKey]
  70. )
  71. const handleMainButtonClick = useCallback(
  72. (dropdownOpen: boolean) => {
  73. sendTrackingEvent({
  74. dropdownMenu: 'main-button',
  75. dropdownOpen,
  76. })
  77. },
  78. [sendTrackingEvent]
  79. )
  80. const handleModalMenuClick = useCallback(
  81. (
  82. e: React.MouseEvent<Record<string, unknown>>,
  83. { modalVariant, dropdownMenuEvent }: ModalMenuClickOptions
  84. ) => {
  85. // avoid invoking the "onClick" callback on the main dropdown button
  86. e.stopPropagation()
  87. sendTrackingEvent({
  88. dropdownMenu: dropdownMenuEvent,
  89. dropdownOpen: true,
  90. })
  91. setModal(modalVariant)
  92. },
  93. [sendTrackingEvent]
  94. )
  95. const handlePortalTemplateClick = useCallback(
  96. (
  97. e: React.MouseEvent<Record<string, unknown>>,
  98. institutionTemplateName: string
  99. ) => {
  100. // avoid invoking the "onClick" callback on the main dropdown button
  101. e.stopPropagation()
  102. sendTrackingEvent({
  103. dropdownMenu: 'institution-template',
  104. dropdownOpen: true,
  105. institutionTemplateName,
  106. })
  107. },
  108. [sendTrackingEvent]
  109. )
  110. const handleStaticTemplateClick = useCallback(
  111. (
  112. e: React.MouseEvent<Record<string, unknown>>,
  113. templateTrackingKey: string
  114. ) => {
  115. // avoid invoking the "onClick" callback on the main dropdown button
  116. e.stopPropagation()
  117. sendTrackingEvent({
  118. dropdownMenu: templateTrackingKey,
  119. dropdownOpen: true,
  120. })
  121. },
  122. [sendTrackingEvent]
  123. )
  124. return (
  125. <>
  126. <ControlledDropdown
  127. id={id}
  128. className={className}
  129. onMainButtonClick={handleMainButtonClick}
  130. >
  131. <Dropdown.Toggle
  132. noCaret
  133. className="new-project-button"
  134. bsStyle="primary"
  135. >
  136. {buttonText || t('new_project')}
  137. </Dropdown.Toggle>
  138. <Dropdown.Menu className={menuClassName}>
  139. <MenuItem
  140. onClick={e =>
  141. handleModalMenuClick(e, {
  142. modalVariant: 'blank_project',
  143. dropdownMenuEvent: 'blank-project',
  144. })
  145. }
  146. >
  147. {t('blank_project')}
  148. </MenuItem>
  149. <MenuItem
  150. onClick={e =>
  151. handleModalMenuClick(e, {
  152. modalVariant: 'example_project',
  153. dropdownMenuEvent: 'example-project',
  154. })
  155. }
  156. >
  157. {t('example_project')}
  158. </MenuItem>
  159. <MenuItem
  160. onClick={e =>
  161. handleModalMenuClick(e, {
  162. modalVariant: 'upload_project',
  163. dropdownMenuEvent: 'upload-project',
  164. })
  165. }
  166. >
  167. {t('upload_project')}
  168. </MenuItem>
  169. <MenuItem
  170. onClick={e =>
  171. handleModalMenuClick(e, {
  172. modalVariant: 'import_from_github',
  173. dropdownMenuEvent: 'import-from-github',
  174. })
  175. }
  176. >
  177. {t('import_from_github')}
  178. </MenuItem>
  179. {portalTemplates?.length > 0 ? (
  180. <>
  181. <MenuItem divider />
  182. <MenuItem header>
  183. {`${t('institution')} ${t('templates')}`}
  184. </MenuItem>
  185. {portalTemplates.map((portalTemplate, index) => (
  186. <MenuItem
  187. key={`portal-template-${index}`}
  188. href={`${portalTemplate.url}#templates`}
  189. onClick={e =>
  190. handlePortalTemplateClick(e, portalTemplate.name)
  191. }
  192. >
  193. {portalTemplate.name}
  194. </MenuItem>
  195. ))}
  196. </>
  197. ) : null}
  198. <MenuItem divider />
  199. <MenuItem header>{t('templates')}</MenuItem>
  200. {templateLinks.map((templateLink, index) => (
  201. <MenuItem
  202. key={`new-project-button-template-${index}`}
  203. href={templateLink.url}
  204. onClick={e =>
  205. handleStaticTemplateClick(e, templateLink.trackingKey)
  206. }
  207. >
  208. {templateLink.name === 'view_all'
  209. ? t('view_all')
  210. : templateLink.name}
  211. </MenuItem>
  212. ))}
  213. {showAddAffiliationWidget && enableAddAffiliationWidget ? (
  214. <>
  215. <MenuItem divider />
  216. <li className="add-affiliation-mobile-wrapper">
  217. <AddAffiliation className="is-mobile" />
  218. </li>
  219. </>
  220. ) : null}
  221. </Dropdown.Menu>
  222. </ControlledDropdown>
  223. <NewProjectButtonModal modal={modal} onHide={() => setModal(null)} />
  224. </>
  225. )
  226. }
  227. export default NewProjectButton