project-list-root.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {
  2. ProjectListProvider,
  3. useProjectListContext,
  4. } from '../context/project-list-context'
  5. import { ColorPickerProvider } from '../context/color-picker-context'
  6. import * as eventTracking from '../../../infrastructure/event-tracking'
  7. import { Col, Row } from 'react-bootstrap'
  8. import { useTranslation } from 'react-i18next'
  9. import useWaitForI18n from '../../../shared/hooks/use-wait-for-i18n'
  10. import CurrentPlanWidget from './current-plan-widget/current-plan-widget'
  11. import NewProjectButton from './new-project-button'
  12. import ProjectListTable from './table/project-list-table'
  13. import SurveyWidget from './survey-widget'
  14. import WelcomeMessage from './welcome-message'
  15. import LoadingBranded from '../../../shared/components/loading-branded'
  16. import SystemMessages from '../../../shared/components/system-messages'
  17. import UserNotifications from './notifications/user-notifications'
  18. import SearchForm from './search-form'
  19. import ProjectsDropdown from './dropdown/projects-dropdown'
  20. import SortByDropdown from './dropdown/sort-by-dropdown'
  21. import ProjectTools from './table/project-tools/project-tools'
  22. import ProjectListTitle from './title/project-list-title'
  23. import Sidebar from './sidebar/sidebar'
  24. import LoadMore from './load-more'
  25. import { useEffect } from 'react'
  26. import withErrorBoundary from '../../../infrastructure/error-boundary'
  27. import { GenericErrorBoundaryFallback } from '../../../shared/components/generic-error-boundary-fallback'
  28. import { SplitTestProvider } from '@/shared/context/split-test-context'
  29. function ProjectListRoot() {
  30. const { isReady } = useWaitForI18n()
  31. if (!isReady) {
  32. return null
  33. }
  34. return <ProjectListRootInner />
  35. }
  36. export function ProjectListRootInner() {
  37. return (
  38. <ProjectListProvider>
  39. <ColorPickerProvider>
  40. <SplitTestProvider>
  41. <ProjectListPageContent />
  42. </SplitTestProvider>
  43. </ColorPickerProvider>
  44. </ProjectListProvider>
  45. )
  46. }
  47. function ProjectListPageContent() {
  48. const {
  49. totalProjectsCount,
  50. error,
  51. isLoading,
  52. loadProgress,
  53. searchText,
  54. setSearchText,
  55. selectedProjects,
  56. filter,
  57. tags,
  58. selectedTagId,
  59. } = useProjectListContext()
  60. const selectedTag = tags.find(tag => tag._id === selectedTagId)
  61. useEffect(() => {
  62. eventTracking.sendMB('loads_v2_dash', {})
  63. }, [])
  64. const { t } = useTranslation()
  65. return isLoading ? (
  66. <div className="loading-container">
  67. <LoadingBranded loadProgress={loadProgress} label={t('loading')} />
  68. </div>
  69. ) : (
  70. <>
  71. <SystemMessages />
  72. <div className="project-list-wrapper clearfix container mx-0 px-0">
  73. {totalProjectsCount > 0 ? (
  74. <>
  75. <Sidebar />
  76. <div className="project-list-main-react">
  77. {error ? <DashApiError /> : ''}
  78. <Row>
  79. <Col xs={12}>
  80. <UserNotifications />
  81. </Col>
  82. </Row>
  83. <div className="project-list-header-row">
  84. <ProjectListTitle
  85. filter={filter}
  86. selectedTag={selectedTag}
  87. className="hidden-xs text-truncate"
  88. />
  89. <div className="project-tools">
  90. <div className="hidden-xs">
  91. {selectedProjects.length === 0 ? (
  92. <CurrentPlanWidget />
  93. ) : (
  94. <ProjectTools />
  95. )}
  96. </div>
  97. <div className="visible-xs">
  98. <CurrentPlanWidget />
  99. </div>
  100. </div>
  101. </div>
  102. <Row className="hidden-xs">
  103. <Col md={7}>
  104. <SearchForm
  105. inputValue={searchText}
  106. setInputValue={setSearchText}
  107. filter={filter}
  108. selectedTag={selectedTag}
  109. />
  110. </Col>
  111. </Row>
  112. <div className="project-list-sidebar-survey-wrapper visible-xs">
  113. <SurveyWidget />
  114. </div>
  115. <div className="visible-xs mt-1">
  116. <div role="toolbar" className="projects-toolbar">
  117. <ProjectsDropdown />
  118. <SortByDropdown />
  119. </div>
  120. </div>
  121. <Row className="row-spaced">
  122. <Col xs={12}>
  123. <div className="card project-list-card">
  124. <div className="visible-xs pt-2 pb-3">
  125. <div className="clearfix">
  126. <NewProjectButton
  127. id="new-project-button-projects-table"
  128. className="pull-left me-2"
  129. showAddAffiliationWidget
  130. />
  131. <SearchForm
  132. inputValue={searchText}
  133. setInputValue={setSearchText}
  134. filter={filter}
  135. selectedTag={selectedTag}
  136. className="overflow-hidden"
  137. formGroupProps={{ className: 'mb-0' }}
  138. />
  139. </div>
  140. </div>
  141. <ProjectListTable />
  142. </div>
  143. </Col>
  144. </Row>
  145. <Row className="row-spaced">
  146. <Col xs={12}>
  147. <LoadMore />
  148. </Col>
  149. </Row>
  150. </div>
  151. </>
  152. ) : (
  153. <div className="project-list-welcome-wrapper">
  154. {error ? <DashApiError /> : ''}
  155. <Row className="row-spaced mx-0">
  156. <Col
  157. sm={10}
  158. smOffset={1}
  159. md={8}
  160. mdOffset={2}
  161. className="project-list-empty-col"
  162. >
  163. <Row>
  164. <Col xs={12}>
  165. <UserNotifications />
  166. </Col>
  167. </Row>
  168. <WelcomeMessage />
  169. </Col>
  170. </Row>
  171. </div>
  172. )}
  173. </div>
  174. </>
  175. )
  176. }
  177. function DashApiError() {
  178. const { t } = useTranslation()
  179. return (
  180. <Row className="row-spaced">
  181. <Col xs={8} xsOffset={2} aria-live="polite" className="text-center">
  182. <div className="alert alert-danger">
  183. {t('generic_something_went_wrong')}
  184. </div>
  185. </Col>
  186. </Row>
  187. )
  188. }
  189. export default withErrorBoundary(ProjectListRoot, GenericErrorBoundaryFallback)