project-list-root.tsx 6.3 KB

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