Commit a4c48e92 authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

feat: normalize pathname handling across navigation and API components

parent 0d025c8f
Loading
Loading
Loading
Loading
+7 −1
Changes for portal-gui/src/app/config/navigation.ts: 7 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -114,6 +114,7 @@ export function canSeeNavItem(
}

export function isActivePath(pathname: string, to: string) {
  pathname = normalizePathname(pathname);
  if (to === '/') {
    return pathname === '/';
  }
@@ -122,10 +123,14 @@ export function isActivePath(pathname: string, to: string) {
}

export function getActiveNavItem(pathname: string): NavItem | undefined {
  const navPath = findNavPath(mainNavItems, pathname);
  const navPath = findNavPath(mainNavItems, normalizePathname(pathname));
  return navPath?.at(-1);
}

export function normalizePathname(pathname: string) {
  return pathname.replace(/\/+$/, '') || '/';
}

function findNavPath(items: NavItem[], pathname: string): NavItem[] | null {
  for (const item of items) {
    if (item.to === pathname) {
@@ -145,6 +150,7 @@ export function getBreadcrumbItems(
  pathname: string,
  api?: Pick<Api, 'id' | 'title'> | null,
): BreadcrumbItem[] {
  pathname = normalizePathname(pathname);
  // Detail pages such as /apis/:id are not in the nav, so walk up to the
  // nearest page that is.
  let navPath: NavItem[] | null = null;
+3 −2
Changes for portal-gui/src/components/common/app-breadcrumbs.tsx: 3 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -9,11 +9,12 @@ import {
  BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';

import { getBreadcrumbItems } from '@/app/config/navigation';
import { getBreadcrumbItems, normalizePathname } from '@/app/config/navigation';
import type { Api } from '@/types/api';

export function AppBreadcrumbs({ api }: { api?: Api | null }) {
  const { pathname } = useLocation();
  const activePath = normalizePathname(pathname);
  const items = getBreadcrumbItems(pathname, api);

  return (
@@ -21,7 +22,7 @@ export function AppBreadcrumbs({ api }: { api?: Api | null }) {
      <BreadcrumbList>
        {items.map((item, index) => {
          const isLast = index === items.length - 1;
          const isCurrent = item.to === pathname;
          const isCurrent = item.to === activePath;

          return (
            <Fragment key={item.to}>
+7 −0
Changes for portal-gui/src/components/data-table/data-table.tsx: 7 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -106,6 +106,13 @@ export function DataTable<TData>({
    getSortedRowModel: getSortedRowModel(),
  });
  const rows = table.getRowModel().rows;
  const lastPageIndex = Math.max(0, table.getPageCount() - 1);

  useEffect(() => {
    if (pagination.pageIndex > lastPageIndex) {
      table.setPageIndex(lastPageIndex);
    }
  }, [lastPageIndex, pagination.pageIndex, table]);

  const visibleIds = rows.map((row) => row.id).join(',');
  useEffect(() => {
+8 −4
Changes for portal-gui/src/features/apis/api-workspace.tsx: 8 added lines, 4 removed lines.
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ import {
  useParams,
} from 'react-router-dom';
import { ROUTES } from '@/app/config/route';
import { normalizePathname } from '@/app/config/navigation';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { PageState } from '@/components/common/page-state';
@@ -16,6 +17,7 @@ export function ApiWorkspacePage() {
  const { id } = useParams<{ id: string }>();
  const { api, loading, error } = useOutletContext<ApiState>();
  const { pathname } = useLocation();
  const activePath = normalizePathname(pathname);
  const basePath = `${ROUTES.APIS}/${id}`;
  const hasExplore = basePath === ROUTES.EDGE_APPLICATION_MANAGEMENT;

@@ -51,12 +53,14 @@ export function ApiWorkspacePage() {
            <nav aria-label="API sections" className="flex gap-2">
              <Button
                asChild
                variant={pathname.endsWith('/overview') ? 'secondary' : 'ghost'}
                variant={
                  activePath.endsWith('/overview') ? 'secondary' : 'ghost'
                }
              >
                <Link
                  to={`${basePath}/overview`}
                  aria-current={
                    pathname.endsWith('/overview') ? 'page' : undefined
                    activePath.endsWith('/overview') ? 'page' : undefined
                  }
                >
                  <BookOpen aria-hidden />
@@ -67,7 +71,7 @@ export function ApiWorkspacePage() {
                <Button
                  asChild
                  variant={
                    pathname.startsWith(`${basePath}/explore`)
                    activePath.startsWith(`${basePath}/explore`)
                      ? 'secondary'
                      : 'ghost'
                  }
@@ -75,7 +79,7 @@ export function ApiWorkspacePage() {
                  <Link
                    to={`${basePath}/explore`}
                    aria-current={
                      pathname.startsWith(`${basePath}/explore`)
                      activePath.startsWith(`${basePath}/explore`)
                        ? 'page'
                        : undefined
                    }
+16 −4
Changes for portal-gui/src/features/applications/applications.tsx: 16 added lines, 4 removed lines.
Original line number Diff line number Diff line
import { useEffect, useRef } from 'react';
import {
  Navigate,
  Outlet,
@@ -5,7 +6,10 @@ import {
  useNavigate,
  useOutletContext,
} from 'react-router-dom';
import { applicationNavItems } from '@/app/config/navigation';
import {
  applicationNavItems,
  normalizePathname,
} from '@/app/config/navigation';
import { ROUTES } from '@/app/config/route';
import { EmptyState } from '@/components/common/empty-state';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
@@ -15,18 +19,26 @@ export function ApplicationsPage() {
  const api = useOutletContext<Api>();
  const { pathname } = useLocation();
  const navigate = useNavigate();
  const activePath = normalizePathname(pathname);
  const tabsRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    tabsRef.current
      ?.querySelector('[aria-selected="true"]')
      ?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
  }, [activePath]);

  if (`${ROUTES.APIS}/${api.id}` !== ROUTES.EDGE_APPLICATION_MANAGEMENT) {
    return <EmptyState title="Explore is not available for this API" />;
  }
  if (pathname === ROUTES.APPLICATIONS) {
  if (activePath === ROUTES.APPLICATIONS) {
    return <Navigate to={ROUTES.APPLICATION_PROFILES} replace />;
  }

  return (
    <Tabs value={pathname} onValueChange={navigate} className="gap-4 py-4">
    <Tabs value={activePath} onValueChange={navigate} className="gap-4 py-4">
      <div className="overflow-x-auto px-4 lg:px-6">
        <TabsList aria-label="Edge application views">
        <TabsList ref={tabsRef} aria-label="Edge application views">
          {applicationNavItems.map((item) => (
            <TabsTrigger key={item.to} value={item.to}>
              <item.icon aria-hidden />
Loading