ProjectBranchSelector.tsx117 lines · main
1import { useBreakpoint, useParams } from 'common'
2import { useRouter } from 'next/router'
3import { useState } from 'react'
4import {
5 Popover,
6 PopoverContent,
7 PopoverTrigger,
8 SidebarMenu,
9 SidebarMenuButton,
10 SidebarMenuItem,
11} from 'ui'
12import { ShimmeringLoader } from 'ui-patterns/ShimmeringLoader'
13
14import { getProjectBranchSelectorState } from './ProjectBranchSelector.utils'
15import { ProjectBranchSelectorPopover } from './ProjectBranchSelectorPopover'
16import { ProjectBranchSelectorSheet } from './ProjectBranchSelectorSheet'
17import { ProjectBranchSelectorTrigger } from './ProjectBranchSelectorTrigger'
18import { useBranchesQuery } from '@/data/branches/branches-query'
19import { useProjectDetailQuery } from '@/data/projects/project-detail-query'
20import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
21import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
22import { IS_PLATFORM } from '@/lib/constants'
23
24export function ProjectBranchSelector() {
25 const router = useRouter()
26 const { ref } = useParams()
27 const { data: selectedOrganization } = useSelectedOrganizationQuery()
28 const { data: project, isPending: isLoadingProject } = useSelectedProjectQuery()
29
30 const isBranch = project?.parentRef !== project?.ref
31 const isProductionBranch = !isBranch
32 const { data: parentProject } = useProjectDetailQuery(
33 { ref: project?.parent_project_ref },
34 { enabled: isBranch }
35 )
36 const displayProject = parentProject ?? project
37
38 const [open, setOpen] = useState(false)
39
40 const isBranchingEnabled = project?.is_branch_enabled === true
41 const { data: branches } = useBranchesQuery(
42 { projectRef: project?.parent_project_ref || ref },
43 { enabled: open && Boolean(project) }
44 )
45
46 const selectedBranch = branches?.find((b) => b.project_ref === ref)
47 const { isMainBranch, branchDisplayName, selectedOrgInitial, organizationHref } =
48 getProjectBranchSelectorState({
49 selectedBranch,
50 isBranchingEnabled,
51 selectedOrganization: selectedOrganization ?? undefined,
52 })
53
54 const goToOrganization = () => {
55 setOpen(false)
56 router.push(organizationHref)
57 }
58
59 const isMobile = useBreakpoint('md')
60
61 if (isLoadingProject || !displayProject)
62 return <ShimmeringLoader className="w-[120px] ml-1 md:py-3" />
63
64 if (!IS_PLATFORM) {
65 return (
66 <SidebarMenu>
67 <SidebarMenuItem>
68 <SidebarMenuButton className="grid flex-1 text-left text-sm leading-tight text-foreground">
69 <span className="truncate">{displayProject.name}</span>
70 </SidebarMenuButton>
71 </SidebarMenuItem>
72 </SidebarMenu>
73 )
74 }
75
76 const triggerProps = {
77 displayProjectName: displayProject.name,
78 selectedOrgInitial,
79 isBranch,
80 isProductionBranch,
81 branchDisplayName,
82 onGoToOrganization: goToOrganization,
83 }
84
85 if (isMobile) {
86 return (
87 <>
88 <ProjectBranchSelectorTrigger {...triggerProps} onClick={() => setOpen(true)} />
89 <ProjectBranchSelectorSheet
90 open={open}
91 onOpenChange={setOpen}
92 selectedRef={ref}
93 onClose={() => setOpen(false)}
94 selectedOrganization={selectedOrganization ?? null}
95 displayProject={displayProject ?? null}
96 selectedBranch={selectedBranch ?? null}
97 isMainBranch={isMainBranch}
98 />
99 </>
100 )
101 }
102
103 return (
104 <SidebarMenu>
105 <SidebarMenuItem>
106 <Popover open={open} onOpenChange={setOpen} modal={false}>
107 <PopoverTrigger asChild>
108 <ProjectBranchSelectorTrigger {...triggerProps} />
109 </PopoverTrigger>
110 <PopoverContent className="p-0 w-[780px]" side="bottom" align="start">
111 <ProjectBranchSelectorPopover onClose={() => setOpen(false)} />
112 </PopoverContent>
113 </Popover>
114 </SidebarMenuItem>
115 </SidebarMenu>
116 )
117}