Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Refactoring Breadcrumbs #614

Merged
merged 33 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3e8258d
initial implementation for breadcrumbs
vardanbansal-harness Dec 19, 2024
aaffaf7
cleanup
vardanbansal-harness Dec 19, 2024
c4c780e
fix project selector in breadcrumbs
vardanbansal-harness Dec 19, 2024
e49ae37
cleanup
vardanbansal-harness Dec 19, 2024
b129ac7
fix lint check
vardanbansal-harness Dec 19, 2024
9754d97
Merge branch 'main' into PIPE-24188
vardanbansal-harness Dec 20, 2024
31ac1c0
refactor Breadcrumbs to use packages/ui components
vardanbansal-harness Dec 20, 2024
3c3e0a8
refactor Project selector for the breadcrumbs
vardanbansal-harness Dec 20, 2024
806ffa4
Integrating repo summary and listing pages into new router
vardanbansal-harness Dec 20, 2024
5881f35
Cleanup
vardanbansal-harness Dec 20, 2024
bde8e5a
rename dropdown component
vardanbansal-harness Dec 20, 2024
000fe98
adding repo layout
vardanbansal-harness Dec 20, 2024
f9f18fa
add settings layout
vardanbansal-harness Dec 20, 2024
991de24
add remaining routes from original App file
vardanbansal-harness Dec 20, 2024
c73f178
Merge branch 'main' into PIPE-24188
vardanbansal-harness Dec 21, 2024
95e1e15
integrated rules and webhook create page
vardanbansal-harness Dec 21, 2024
be7de3f
Merge branch 'main' into PIPE-24188
vardanbansal-harness Jan 2, 2025
c4efd24
Merge branch 'main' into PIPE-24188
vardanbansal-harness Jan 2, 2025
8e057aa
moving app routes to sepatate file
vardanbansal-harness Jan 3, 2025
59bc741
move logic for root wrapper to apps/gitness from packages/ui
vardanbansal-harness Jan 3, 2025
cc65c13
fixing scrolling for breadcrumbs along with page scroll - WIP
vardanbansal-harness Jan 3, 2025
df419fa
Merge branch 'main' into PIPE-24188
vardanbansal-harness Jan 3, 2025
4732d82
rebase after removal of old gitness pages
vardanbansal-harness Jan 3, 2025
f168f6c
remove duplicate pr layout
vardanbansal-harness Jan 3, 2025
3e5ef83
fixing scroll issue for repo layout
vardanbansal-harness Jan 3, 2025
4fbe514
cleanup
vardanbansal-harness Jan 6, 2025
3710d12
cleanup - remove commented code, fixing other layouts
vardanbansal-harness Jan 6, 2025
b5a67e3
cleanup - remove commented code
vardanbansal-harness Jan 6, 2025
80f44ca
Merge branch 'main' into PIPE-24188
vardanbansal-harness Jan 6, 2025
eba1e7e
fix browser console warning
vardanbansal-harness Jan 6, 2025
c6826fd
file rename
vardanbansal-harness Jan 6, 2025
4dd71bf
cleanup, add missing route
vardanbansal-harness Jan 6, 2025
591ace3
cleanup
vardanbansal-harness Jan 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/gitness/src/AppV2.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { I18nextProvider } from 'react-i18next'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'

import { QueryClientProvider } from '@tanstack/react-query'
import { NuqsAdapter } from 'nuqs/adapters/react-router'

import { TooltipProvider } from '@harnessio/canary'
import { CodeServiceAPIClient } from '@harnessio/code-service-client'

import { AppProvider } from './framework/context/AppContext'
import { ExitConfirmProvider } from './framework/context/ExitConfirmContext'
import { ThemeProvider } from './framework/context/ThemeContext'
import { queryClient } from './framework/queryClient'
import i18n from './i18n/i18n'
import { routes } from './routes'

const BASE_URL_PREFIX = `${window.apiUrl || ''}/api/v1`

export default function AppV1() {
new CodeServiceAPIClient({
urlInterceptor: (url: string) => `${BASE_URL_PREFIX}${url}`,
responseInterceptor: (response: Response) => {
switch (response.status) {
case 401:
window.location.href = '/signin'
break
}
return response
}
})

// Router Configuration
const router = createBrowserRouter(routes)

return (
<AppProvider>
<I18nextProvider i18n={i18n}>
<ThemeProvider defaultTheme="dark-std-std">
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<ExitConfirmProvider>
<NuqsAdapter>
<RouterProvider router={router} />
</NuqsAdapter>
</ExitConfirmProvider>
</TooltipProvider>
</QueryClientProvider>
</ThemeProvider>
</I18nextProvider>
</AppProvider>
)
}
200 changes: 200 additions & 0 deletions apps/gitness/src/components-v2/app-shell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import { Outlet, useLocation, useNavigate } from 'react-router-dom'

import {
ManageNavigation,
MenuGroupType,
MenuGroupTypes,
MoreSubmenu,
Navbar,
NavbarItemType,
SettingsMenu
} from '@harnessio/ui/components'
import { useLocationChange } from '@harnessio/ui/hooks'
import { SandboxLayout } from '@harnessio/ui/views'

import { useNav } from '../components/stores/recent-pinned-nav-links.store'
import { getNavbarMenuData } from '../data/navbar-menu-data'
import { getPinnedMenuItemsData } from '../data/pinned-menu-items-data'
import { useAppContext } from '../framework/context/AppContext'
import { useThemeStore } from '../framework/context/ThemeContext'
import { useTranslationStore } from '../i18n/stores/i18n-store'
import BreadcrumbsV1 from './breadcrumbs/breadcrumbs'

interface NavLinkStorageInterface {
state: {
recent: NavbarItemType[]
pinned: NavbarItemType[]
}
}

const AppShell = () => {
const { currentUser } = useAppContext()
const navigate = useNavigate()
const location = useLocation()
const { pinnedMenu, recentMenu, setPinned, setRecent, setNavLinks } = useNav()
const { t } = useTranslationStore()
const [showMoreMenu, setShowMoreMenu] = useState(false)
const [showSettingMenu, setShowSettingMenu] = useState(false)
const [showCustomNav, setShowCustomNav] = useState(false)

const pinnedMenuItemsData = useMemo(() => getPinnedMenuItemsData(t), [t])

useLocationChange({ t, onRouteChange: setRecent })

useEffect(() => {
const linksFromStorage = localStorage.getItem('nav-items')
let parsedLinksFromStorage: NavLinkStorageInterface | undefined

if (linksFromStorage) {
parsedLinksFromStorage = JSON.parse(linksFromStorage)
}

/**
* Logic for setting initial pinned links
*
* setting initial pinned link only if no pinned links are stored in local storage.
* Pinned links cannot be empty as we will have some links permanantly.
*/
if (parsedLinksFromStorage && !parsedLinksFromStorage?.state?.pinned?.length) {
const pinnedItems = pinnedMenu.filter(
item => !pinnedMenuItemsData.some(staticPinned => staticPinned.id === item.id)
)
setNavLinks({ pinnedMenu: [...pinnedMenuItemsData, ...pinnedItems] })
}
}, [])

/**
* Map mock data menu by type to Settings and More
*/
const { moreMenu, settingsMenu } = useMemo(() => {
const navbarMenuData = getNavbarMenuData(t)
return navbarMenuData.reduce<{
moreMenu: MenuGroupType[]
settingsMenu: MenuGroupType[]
}>(
(acc, item) => {
if (item.type === MenuGroupTypes.SETTINGS) {
acc.settingsMenu.push(item)
} else {
acc.moreMenu.push(item)
}

return acc
},
{
moreMenu: [],
settingsMenu: []
}
)
}, [t])

/**
* Handle logout
*/
const handleLogOut = () => navigate('/logout')

/**
* Toggle show more menu
*/
const handleMoreMenu = useCallback(() => {
setShowSettingMenu(false)
setShowMoreMenu(prevState => !prevState)
}, [])

/**
* Toggle system settings menu
*/
const handleSettingsMenu = useCallback(() => {
setShowMoreMenu(false)
setShowSettingMenu(prevState => !prevState)
}, [])

/**
* Toggle custom navigation modal
*/
const handleCustomNav = useCallback(() => {
setShowCustomNav(prevState => !prevState)
}, [])

/**
* Close all menu when location changed
*/
useEffect(() => {
setShowMoreMenu(false)
setShowSettingMenu(false)
setShowCustomNav(false)
}, [location])

/**
* Handle save recent and pinned items
*/
const handleSave = (nextRecentItems: NavbarItemType[], nextPinnedItems: NavbarItemType[]) => {
setNavLinks({
pinnedMenu: nextPinnedItems,
recentMenu: nextRecentItems
})
}

/**
* Remove recent menu item
*/
const handleRemoveRecentMenuItem = useCallback(
(item: NavbarItemType) => {
setRecent(item, true)
},
[setRecent]
)

/**
* Change pinned menu items
*/
const handleChangePinnedMenuItem = useCallback(
(item: NavbarItemType, pin: boolean) => {
setPinned(item, pin)
},
[setPinned]
)

return (
<SandboxLayout.Root>
<SandboxLayout.LeftPanel>
<Navbar
showMoreMenu={showMoreMenu}
showSettingMenu={showSettingMenu}
handleMoreMenu={handleMoreMenu}
handleSettingsMenu={handleSettingsMenu}
currentUser={currentUser}
handleCustomNav={handleCustomNav}
handleLogOut={handleLogOut}
recentMenuItems={recentMenu}
pinnedMenuItems={pinnedMenu}
handleChangePinnedMenuItem={handleChangePinnedMenuItem}
handleRemoveRecentMenuItem={handleRemoveRecentMenuItem}
useThemeStore={useThemeStore}
useTranslationStore={useTranslationStore}
/>
</SandboxLayout.LeftPanel>
<div className="flex flex-col">
<div className="sticky top-0 z-40 bg-background-1">
<BreadcrumbsV1 />
</div>
<Outlet />
</div>
<MoreSubmenu showMoreMenu={showMoreMenu} handleMoreMenu={handleMoreMenu} items={moreMenu} />
<SettingsMenu showSettingMenu={showSettingMenu} handleSettingsMenu={handleSettingsMenu} items={settingsMenu} />
<ManageNavigation
pinnedItems={pinnedMenu}
recentItems={recentMenu}
navbarMenuData={getNavbarMenuData(t)}
showManageNavigation={showCustomNav}
isSubmitting={false}
submitted={false}
onSave={handleSave}
onClose={handleCustomNav}
/>
</SandboxLayout.Root>
)
}

export default AppShell
47 changes: 47 additions & 0 deletions apps/gitness/src/components-v2/breadcrumbs/breadcrumbs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useMatches } from 'react-router-dom'

import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
Topbar
} from '@harnessio/ui/components'

import { CustomHandle } from '../../routes'

function Breadcrumbs() {
const matches = useMatches()

return (
<Topbar.Root>
<Topbar.Left>
<Breadcrumb className="select-none">
<BreadcrumbList>
{matches.map((match, index) => {
const { breadcrumb } = (match.handle || {}) as CustomHandle
const isFirst = index === 0
const isLast = index === matches.length - 1

if (!breadcrumb) return null

return (
<BreadcrumbItem key={index}>
{!isFirst ? <BreadcrumbSeparator>/</BreadcrumbSeparator> : null}
{isLast ? (
breadcrumb(match.params)
) : (
<BreadcrumbLink href={match.pathname}>{breadcrumb(match.params)}</BreadcrumbLink>
)}
</BreadcrumbItem>
)
})}
</BreadcrumbList>
</Breadcrumb>
</Topbar.Left>
</Topbar.Root>
)
}

export default Breadcrumbs
44 changes: 44 additions & 0 deletions apps/gitness/src/components-v2/breadcrumbs/project-dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useNavigate, useParams } from 'react-router-dom'

import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
Icon,
Text
} from '@harnessio/ui/components'

import { useAppContext } from '../../framework/context/AppContext'
import { PathParams } from '../../RouteDefinitions'

function ProjectDropdown(): JSX.Element {
const { spaceId } = useParams<PathParams>()
const navigate = useNavigate()
const { spaces } = useAppContext()
return (
<DropdownMenu>
<DropdownMenuTrigger className="flex items-center gap-x-1.5">
{spaceId ?? 'Select project'}
<Icon className="chevron-down text-icons-4" name="chevron-fill-down" size={6} />
</DropdownMenuTrigger>
<DropdownMenuContent className="w-[300px]">
{spaces.map(({ identifier }) => (
<DropdownMenuItem
className="flex flex-col"
key={identifier}
onClick={() => {
if (identifier) {
navigate(`/${identifier}/repos`)
}
}}
>
<Text className="inline-block w-full text-left">{identifier}</Text>
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}

export { ProjectDropdown }
Loading