Home.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import { DndContext, DragEndEvent, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
  2. import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
  3. import { IS_PLATFORM, useFlag, useParams } from 'common'
  4. import dayjs from 'dayjs'
  5. import { useEffect, useRef } from 'react'
  6. import { cn } from 'ui'
  7. import { AdvisorSection } from './AdvisorSection'
  8. import { ConnectSection } from './ConnectSection'
  9. import { CustomReportSection } from './CustomReportSection'
  10. import { DEFAULT_SECTION_ORDER, mergeSectionOrder } from './Home.utils'
  11. import { ProjectUsageSection as ProjectUsageSectionV2 } from './ProjectUsageSection'
  12. import { ProjectUsageSection as ProjectUsageSectionV1 } from '@/components/interfaces/Home/ProjectUsageSection'
  13. import { SortableSection } from '@/components/interfaces/ProjectHome/SortableSection'
  14. import { TopSection } from '@/components/interfaces/ProjectHome/TopSection'
  15. import { ProjectNeedsSecuring } from '@/components/layouts/ProjectNeedsSecuring/ProjectNeedsSecuring'
  16. import { ScaffoldContainer, ScaffoldSection } from '@/components/layouts/Scaffold'
  17. import { useLocalStorage } from '@/hooks/misc/useLocalStorage'
  18. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  19. import { PROJECT_STATUS } from '@/lib/constants'
  20. import { useTrack } from '@/lib/telemetry/track'
  21. import { useAppStateSnapshot } from '@/state/app-state'
  22. export const ProjectHome = () => {
  23. const { enableBranching } = useParams()
  24. const snap = useAppStateSnapshot()
  25. const { data: project } = useSelectedProjectQuery()
  26. const track = useTrack()
  27. const showHomepageUsageV2 = useFlag('newHomepageUsageV2')
  28. const isMatureProject = dayjs(project?.inserted_at).isBefore(dayjs().subtract(10, 'day'))
  29. const hasShownEnableBranchingModalRef = useRef(false)
  30. const isPaused = project?.status === PROJECT_STATUS.INACTIVE
  31. const isComingUp = project?.status === PROJECT_STATUS.COMING_UP
  32. const [sectionOrder, setSectionOrder] = useLocalStorage<string[]>(
  33. `home-section-order-${project?.ref || 'default'}`,
  34. DEFAULT_SECTION_ORDER
  35. )
  36. const UsageSection = showHomepageUsageV2 ? ProjectUsageSectionV2 : ProjectUsageSectionV1
  37. const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }))
  38. const handleDragEnd = (event: DragEndEvent) => {
  39. const { active, over } = event
  40. if (!over || active.id === over.id) return
  41. setSectionOrder((items) => {
  42. const oldIndex = items.indexOf(String(active.id))
  43. const newIndex = items.indexOf(String(over.id))
  44. if (oldIndex === -1 || newIndex === -1) return items
  45. track('home_section_rows_moved', {
  46. section_moved: String(active.id),
  47. old_position: oldIndex,
  48. new_position: newIndex,
  49. })
  50. return arrayMove(items, oldIndex, newIndex)
  51. })
  52. }
  53. useEffect(() => {
  54. if (enableBranching && !hasShownEnableBranchingModalRef.current) {
  55. hasShownEnableBranchingModalRef.current = true
  56. snap.setShowCreateBranchModal(true)
  57. }
  58. }, [enableBranching, snap])
  59. useEffect(() => {
  60. setSectionOrder(mergeSectionOrder)
  61. }, [setSectionOrder])
  62. const showConnectSection = !isMatureProject && !!project
  63. const renderOrder = mergeSectionOrder(sectionOrder).filter((id) => {
  64. if (id === 'connect') return showConnectSection
  65. return true
  66. })
  67. return (
  68. <ProjectNeedsSecuring>
  69. <div className="w-full h-full">
  70. <ScaffoldContainer size="large" className={cn(isPaused && 'h-full')}>
  71. <ScaffoldSection
  72. isFullWidth
  73. className={cn(isPaused ? 'h-full flex justify-center p-0!' : 'pb-0')}
  74. >
  75. <TopSection />
  76. </ScaffoldSection>
  77. </ScaffoldContainer>
  78. {!isPaused && (
  79. <ScaffoldContainer size="large">
  80. <ScaffoldSection isFullWidth className="gap-12 pb-32">
  81. <DndContext sensors={sensors} onDragEnd={handleDragEnd}>
  82. <SortableContext items={renderOrder} strategy={verticalListSortingStrategy}>
  83. {renderOrder.map((id) => {
  84. if (IS_PLATFORM && id === 'usage') {
  85. return (
  86. <div
  87. key={id}
  88. className={cn(isComingUp && 'opacity-60 pointer-events-none')}
  89. >
  90. <SortableSection id={id}>
  91. <UsageSection />
  92. </SortableSection>
  93. </div>
  94. )
  95. }
  96. if (id === 'connect' && showConnectSection) {
  97. return (
  98. <SortableSection key={id} id={id}>
  99. <ConnectSection />
  100. </SortableSection>
  101. )
  102. }
  103. if (id === 'advisor') {
  104. return (
  105. <div
  106. key={id}
  107. className={cn(isComingUp && 'opacity-60 pointer-events-none')}
  108. >
  109. <SortableSection id={id}>
  110. <AdvisorSection showEmptyState={isComingUp} />
  111. </SortableSection>
  112. </div>
  113. )
  114. }
  115. if (id === 'custom-report') {
  116. return (
  117. <div
  118. key={id}
  119. className={cn(isComingUp && 'opacity-60 pointer-events-none')}
  120. >
  121. <SortableSection id={id}>
  122. <CustomReportSection />
  123. </SortableSection>
  124. </div>
  125. )
  126. }
  127. })}
  128. </SortableContext>
  129. </DndContext>
  130. </ScaffoldSection>
  131. </ScaffoldContainer>
  132. )}
  133. </div>
  134. </ProjectNeedsSecuring>
  135. )
  136. }