Files
ea38c6d153 Review and merge branch (#36795)
* allow creating branching without git

* update branching modals

* add account connections

* edit branch

* copy

* update copy

* enable branch modal changes

* add gitless branching flag

* update account connections

* merge page

* merge experiment

* update merge

* update pull requests empty state

* use diff query

* branch diffing

* diff query

* Clean up

* refinements to gitless branching

* branching merge and status

* link

* branch function diffing

* update styling

* refactor

* remove hook

* error handling

* move

* remove enable branching modal

* re-add github linker

* abstract away enable and disable

* toggle fixes

* update logic to lean on connection status

* update form logic

* sheet layout

* gitless flag

* style and workflow updates

* fix side panel size

* fix duplicate onerror

* copy changes

* refetch

* merge mutation copy

* remove import

* add cost

* allow connection details on create

* initial queries

* push button

* merge cleanup

* Fix TS issues

* Fix TS issues

* Couple of clean ups

* Revert hardcode in useFlag

* Fix TS

* layout issues and github check

* refactor

* refactor to use new field

* cleanup

* style

* failed merge

* error positioning

* refactoring merge

* workflow refactor

* hook move

* clarification with github integration

* replace branch dropdown button

* update repo picker

* updates

* remove modal

* fix small nits

* change defaults

* clean up

* disable if not gitless and no connection

* clean up

* always show workflow run id

* optimistic

* fix branch query

* fix issues

* fetch edge diff

* confirm merge

* update edge functions key

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
Co-authored-by: Kevin Grüneberg <k.grueneberg1994@gmail.com>
Co-authored-by: Alaister Young <a@alaisteryoung.com>
2025-07-04 14:57:59 +10:00

151 lines
5.0 KiB
TypeScript

import Link from 'next/link'
import { useRouter } from 'next/router'
import { ReactNode } from 'react'
import { useParams } from 'common'
import { Badge, Button, cn, NavMenu, NavMenuItem } from 'ui'
import { ScaffoldContainer } from '../Scaffold'
import { PageHeader } from './PageHeader'
export interface NavigationItem {
id?: string
label: string
href?: string
icon?: ReactNode
onClick?: () => void
badge?: string
active?: boolean
}
interface PageLayoutProps {
children?: ReactNode
title?: string | ReactNode
subtitle?: string
icon?: ReactNode
breadcrumbs?: Array<{
label?: string
href?: string
element?: ReactNode
}>
primaryActions?: ReactNode
secondaryActions?: ReactNode
navigationItems?: NavigationItem[]
className?: string
size?: 'default' | 'full' | 'large' | 'small'
isCompact?: boolean
}
/**
* For rendering a general single column based UI (which covers most of the dashboard's use case)
*
* Pages that would use this would for example be: Auth Sign In / Up, Project settings, Advisors, etc
*
* Pages that would deviate from this are those with dedicated UI, for example: Table Editor, SQL Editor, etc
*
* Handles rendering the following UI behaviors:
* - Page padding depending on the size property
* - Top level breadcrumbs (If applicable)
* - Top level page header (If applicable)
* - Top level tab navigations (If applicable)
* @param title - Title rendered in page header
* @param subtitle - Subtitle rendered in page header, below title
* @param icon - Icon rendered in Page header, to the left of title and subtitle
* @param breadcrumbs - Breadcrumbs rendered in page header, above title. Can be string labels with hrefs or custom elements
* @param primaryActions - TBD
* @param secondaryActions - TBD
* @param navigationItems - Tab navigation rendered below the page header
* @param className - Optional additional class names to be applied
* @param size - Controls padding of the page header only, padding for the content to be controlled by PageContainer (Default: 'default')
* @param isCompact - TBD (Default: false)
*/
export const PageLayout = ({
children,
title,
subtitle,
icon,
breadcrumbs = [],
primaryActions,
secondaryActions,
navigationItems = [],
className,
size = 'default',
isCompact = false,
}: PageLayoutProps) => {
const { ref } = useParams()
const router = useRouter()
return (
<div className="w-full min-h-full flex flex-col items-stretch">
<ScaffoldContainer
size={size}
className={cn(
'w-full mx-auto',
size === 'full' &&
(isCompact ? 'max-w-none !px-6 border-b pt-4' : 'max-w-none pt-6 border-b'),
size !== 'full' && (isCompact ? 'pt-4' : 'pt-12'),
navigationItems.length === 0 && size === 'full' && (isCompact ? 'pb-4' : 'pb-8'),
className
)}
>
{/* Header section */}
{(title || subtitle || primaryActions || secondaryActions || breadcrumbs.length > 0) && (
<PageHeader
title={title}
subtitle={subtitle}
icon={icon}
breadcrumbs={breadcrumbs}
primaryActions={primaryActions}
secondaryActions={secondaryActions}
isCompact={isCompact}
/>
)}
{/* Navigation section */}
{navigationItems.length > 0 && (
<NavMenu className={cn(isCompact ? 'mt-2' : 'mt-4', size === 'full' && 'border-none')}>
{navigationItems.map((item) => {
const isActive =
item.active !== undefined ? item.active : router.asPath.split('?')[0] === item.href
return (
<NavMenuItem key={item.label} active={isActive}>
{item.href ? (
<Link
href={
item.href.includes('[ref]') && !!ref
? item.href.replace('[ref]', ref)
: item.href
}
className={cn(
'inline-flex items-center gap-2',
isActive && 'text-foreground'
)}
onClick={item.onClick}
>
{item.icon && <span>{item.icon}</span>}
{item.label}
{item.badge && <Badge variant="default">{item.badge}</Badge>}
</Link>
) : (
<Button
type="link"
onClick={item.onClick}
className={cn(isActive && 'text-foreground font-medium')}
>
{item.icon && <span className="mr-2">{item.icon}</span>}
{item.label}
{item.badge && <Badge variant="default">{item.badge}</Badge>}
</Button>
)}
</NavMenuItem>
)
})}
</NavMenu>
)}
</ScaffoldContainer>
{/* Content section */}
{children}
</div>
)
}