diff --git a/frontend/app/[team]/layout.tsx b/frontend/app/[team]/layout.tsx
index c0c4a0e63..7718eef7b 100644
--- a/frontend/app/[team]/layout.tsx
+++ b/frontend/app/[team]/layout.tsx
@@ -52,7 +52,7 @@ export default function RootLayout({
)}
>
{activeOrganisation && }
- {showNav && }
+ {showNav && }
{showNav && }
diff --git a/frontend/app/webauth/[requestCode]/page.tsx b/frontend/app/webauth/[requestCode]/page.tsx
index eb0cc1312..9013555ed 100644
--- a/frontend/app/webauth/[requestCode]/page.tsx
+++ b/frontend/app/webauth/[requestCode]/page.tsx
@@ -2,7 +2,6 @@
import { OrganisationType } from '@/apollo/graphql'
import { Button } from '@/components/common/Button'
-import { HeroPattern } from '@/components/common/HeroPattern'
import { Input } from '@/components/common/Input'
import Spinner from '@/components/common/Spinner'
import OnboardingNavbar from '@/components/layout/OnboardingNavbar'
@@ -31,7 +30,6 @@ import { useSession } from 'next-auth/react'
import { useRouter } from 'next/navigation'
import { useContext, useEffect, useState } from 'react'
import { FaChevronRight, FaExclamationTriangle, FaCheckCircle, FaShieldAlt } from 'react-icons/fa'
-import { MdContentCopy } from 'react-icons/md'
import { SiGithub, SiGnometerminal, SiSlack } from 'react-icons/si'
import { toast } from 'react-toastify'
@@ -325,7 +323,8 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
CLI Authentication failed
- CLI authentication could not be completed from this page. Please follow these steps to retry the authentication:
+ CLI authentication could not be completed from this page. Please follow these steps to
+ retry the authentication:
@@ -336,7 +335,8 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
1
- Exit out of the CLI by pressing Ctrl+C
+ Exit out of the CLI by pressing{' '}
+ Ctrl+C
@@ -346,7 +346,10 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
2
- Retry authentication manually via the token mode:
+
+ Retry authentication manually via the{' '}
+ token mode:
+
@@ -356,7 +359,13 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
Choose your Phase instance type as: ☁️ Phase Cloud
- Enter your email address: handleCopy(session?.user?.email || '')}>{session?.user?.email}
+ Enter your email address:{' '}
+ handleCopy(session?.user?.email || '')}
+ >
+ {session?.user?.email}
+
) : (
@@ -365,10 +374,22 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
Choose your Phase instance type as: 🛠️ Self Hosted
- Enter the host: handleCopy(getHostname() || '')}>{getHostname()}
+ Enter the host:{' '}
+ handleCopy(getHostname() || '')}
+ >
+ {getHostname()}
+
- Enter your email address: handleCopy(session?.user?.email || '')}>{session?.user?.email}
+ Enter your email address:{' '}
+ handleCopy(session?.user?.email || '')}
+ >
+ {session?.user?.email}
+
)}
@@ -384,20 +405,16 @@ export default function WebAuth({ params }: { params: { requestCode: string } })
-
+
-
diff --git a/frontend/components/layout/Navbar.tsx b/frontend/components/layout/Navbar.tsx
index 728b0efcf..6350df6ef 100644
--- a/frontend/components/layout/Navbar.tsx
+++ b/frontend/components/layout/Navbar.tsx
@@ -1,21 +1,31 @@
-import UserMenu from '../UserMenu'
+'use client'
+
import { useLazyQuery, useQuery } from '@apollo/client'
+import { useContext, useEffect, useMemo } from 'react'
+import Link from 'next/link'
+import { startCase } from 'lodash'
+import { useSearchParams } from 'next/navigation'
+
import { GetApps } from '@/graphql/queries/getApps.gql'
import { GetAppEnvironments } from '@/graphql/queries/secrets/getAppEnvironments.gql'
-import { usePathname } from 'next/navigation'
-import { useContext, useEffect } from 'react'
import { AppType, EnvironmentType } from '@/apollo/graphql'
-import Link from 'next/link'
+import { organisationContext } from '@/contexts/organisationContext'
+import { userHasPermission } from '@/utils/access/permissions'
+import { generateBreadcrumbs, generatePageTitle, NavigationContext } from '@/utils/navigation'
+
import { Button } from '../common/Button'
import { StatusIndicator } from '../common/StatusIndicator'
-import { organisationContext } from '@/contexts/organisationContext'
-import clsx from 'clsx'
import { LogoMark } from '../common/LogoMark'
import CommandPalette from '../common/CommandPalette'
-import { userHasPermission } from '@/utils/access/permissions'
+import UserMenu from '../UserMenu'
+
+import { useParsedRoute } from '@/utils/route'
-export const NavBar = (props: { team: string }) => {
+export const NavBar = () => {
const { activeOrganisation: organisation } = useContext(organisationContext)
+ const { team, context, appId, envId, page, subPage } = useParsedRoute()
+ const searchParams = useSearchParams()
+ const tab = searchParams?.get('tab')
const userCanReadApps = userHasPermission(organisation?.role?.permissions, 'Apps', 'read')
@@ -25,87 +35,74 @@ export const NavBar = (props: { team: string }) => {
},
skip: !organisation || !userCanReadApps,
})
- const [getAppEnvs, { data: appEnvsData }] = useLazyQuery(GetAppEnvironments)
-
- const orgContext = usePathname()?.split('/')[2]
- const apps = appsData?.apps as AppType[]
+ const [getAppEnvs, { data: appEnvsData }] = useLazyQuery(GetAppEnvironments)
+ const apps = (appsData?.apps as AppType[]) || []
const envs: EnvironmentType[] = appEnvsData?.appEnvironments ?? []
- const appId = usePathname()?.split('/')[3]
-
- const envId = usePathname()?.split('/')[5]
-
- const appPage = usePathname()?.split('/')[4]
-
- const activeApp = orgContext === 'apps' ? apps?.find((app) => app.id === appId) : undefined
+ const activeApp = context === 'apps' ? apps.find((app) => app.id === appId) : undefined
+ const activeEnv = activeApp ? envs.find((env) => env.id === envId) : undefined
- useEffect(() => {
- if (activeApp) getAppEnvs({ variables: { appId: activeApp.id } })
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [activeApp])
+ // Create navigation context
+ const navigationContext: NavigationContext = useMemo(
+ () => ({
+ team,
+ context,
+ appId,
+ envId,
+ page,
+ subPage,
+ activeApp,
+ activeEnv,
+ }),
+ [team, context, appId, envId, page, subPage, activeApp, activeEnv]
+ )
- const activeEnv = activeApp ? envs.find((env) => env.id === envId) : undefined
+ const breadcrumbs = generateBreadcrumbs(navigationContext)
- return (
-
+ const BreadCrumbs = () => {
+ return (
-
/
-
-
- {props.team}
-
- {activeApp &&
/ }
-
- {activeApp &&
- (appPage ? (
-
- {activeApp.name}
-
- ) : (
-
- {activeApp.name}
-
- ))}
-
- {activeApp && appPage &&
/ }
-
- {activeApp && appPage && (
-
(
+
+ /
+ {crumb.isLink && crumb.href ? (
+
+ {startCase(crumb.label)}
+
+ ) : (
+
+ {startCase(crumb.label)}
+
)}
- >
- {appPage}
-
- )}
+
+ ))}
+
+ )
+ }
- {activeEnv && / }
+ useEffect(() => {
+ if (activeApp) {
+ getAppEnvs({ variables: { appId: activeApp.id } })
+ }
+ }, [activeApp, getAppEnvs])
- {activeEnv && (
-
- {activeEnv.name}
-
- )}
+ // Update page title using the utility
+ useEffect(() => {
+ document.title = generatePageTitle(navigationContext)
+ }, [navigationContext, tab])
- {!activeApp && orgContext && / }
- {!activeApp && {orgContext} }
-
+ return (
+