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

fix: enhance api catalog

parent af2dce9b
Loading
Loading
Loading
Loading
+14 −1
Changes for portal-gui/src/app/config/navigation.ts: 14 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@ import {
import { ROUTES } from './route';

import type { BreadcrumbItem } from '@/types/common';
import type { Api } from '@/types/api';

export const applicationNavItems: NavItem[] = [
  {
@@ -130,7 +131,10 @@ function findNavPath(items: NavItem[], pathname: string): NavItem[] | null {
  return null;
}

export function getBreadcrumbItems(pathname: string): BreadcrumbItem[] {
export function getBreadcrumbItems(
  pathname: string,
  api?: Pick<Api, 'id' | 'title'> | null,
): BreadcrumbItem[] {
  // 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;
@@ -148,6 +152,15 @@ export function getBreadcrumbItems(pathname: string): BreadcrumbItem[] {
  if (pathname.startsWith(`${ROUTES.APIS}/`) && trail[0]?.to !== ROUTES.APIS) {
    trail.unshift({ title: 'API Catalog', to: ROUTES.APIS });
  }
  if (api && trail.at(-1)?.to === ROUTES.APIS) {
    const basePath = `${ROUTES.APIS}/${api.id}`;
    if (pathname === basePath || pathname.startsWith(`${basePath}/`)) {
      trail.push({ title: api.title, to: basePath });
      if (pathname === `${basePath}/overview`) {
        trail.push({ title: 'Overview', to: pathname });
      }
    }
  }
  return trail[0]?.to === ROUTES.HOME
    ? trail
    : [{ title: 'Home', to: ROUTES.HOME }, ...trail];
+4 −3
Changes for portal-gui/src/components/common/app-breadcrumbs.tsx: 4 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -10,10 +10,11 @@ import {
} from '@/components/ui/breadcrumb';

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

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

  return (
    <Breadcrumb>
@@ -23,7 +24,7 @@ export function AppBreadcrumbs() {
          const isCurrent = item.to === pathname;

          return (
            <Fragment key={`{item.title}-${index}`}>
            <Fragment key={item.to}>
              <BreadcrumbItem>
                {isCurrent ? (
                  <BreadcrumbPage>{item.title}</BreadcrumbPage>
+3 −2
Changes for portal-gui/src/components/common/site-header.tsx: 3 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -4,8 +4,9 @@ import { Separator } from '@/components/ui/separator';
import { SidebarTrigger } from '@/components/ui/sidebar';
import { LogoIcon } from '@/lib/icons';
import { ThemeToggle } from './theme-toggle';
import type { Api } from '@/types/api';

export function SiteHeader() {
export function SiteHeader({ api }: { api?: Api | null }) {
  return (
    <header className="sticky top-0 z-50 flex h-(--header-height) shrink-0 items-center border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
      <div className="flex w-full items-center gap-2 px-3 lg:px-6">
@@ -20,7 +21,7 @@ export function SiteHeader() {
        <Separator orientation="vertical" className="hidden h-4 md:block" />
        <div className="min-w-0 flex-1">
          <div className="hidden md:block">
            <AppBreadcrumbs />
            <AppBreadcrumbs api={api} />
          </div>
        </div>
        <ThemeToggle />
+42 −0
Changes for portal-gui/src/components/common/workflow.tsx: 42 added lines, 0 removed lines.
Original line number Diff line number Diff line
import { ArrowRight } from 'lucide-react';
import type { WorkflowStep } from '@/types/common';

export function Workflow({ steps }: { steps: WorkflowStep[] }) {
  if (steps.length === 0) return null;

  return (
    <figure aria-label="How it works" className="@container border-b pb-6">
      <figcaption className="mb-5 text-sm font-semibold text-foreground">
        How it works
      </figcaption>
      <ol
        role="list"
        aria-label="Workflow steps"
        className="grid grid-cols-2 gap-x-8 gap-y-6 @2xl:grid-cols-4"
      >
        {steps.map(({ title, description, icon: Icon }, index) => (
          <li key={title} className="relative min-w-0 text-center">
            <div className="mx-auto mb-3 flex size-12 items-center justify-center rounded-xl bg-primary/10 text-primary">
              <Icon aria-hidden className="size-6" strokeWidth={1.75} />
            </div>
            <p className="text-sm leading-5 font-semibold text-foreground">
              <span aria-hidden className="text-muted-foreground">
                {index + 1}.
              </span>{' '}
              {title}
            </p>
            <p className="mx-auto mt-1 max-w-40 text-xs leading-5 text-muted-foreground">
              {description}
            </p>
            {index < steps.length - 1 && (
              <ArrowRight
                aria-hidden
                className="absolute top-3 -right-4 hidden size-5 translate-x-1/2 text-muted-foreground/60 @2xl:block"
              />
            )}
          </li>
        ))}
      </ol>
    </figure>
  );
}
+7 −64
Changes for portal-gui/src/features/apis/api-details-page.tsx: 7 added lines, 64 removed lines.
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@ import { SectionTitle } from '@/components/common/section-title';
import { PageContent } from '@/components/common/page-content';
import { Markdown } from '@/components/common/markdown';
import { ExpandableMarkdown } from '@/components/common/expandable-markdown';
import { Workflow } from '@/components/common/workflow';
import {
  Card,
  CardContent,
@@ -10,7 +11,6 @@ import {
  CardHeader,
  CardTitle,
} from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';

import type { Api } from '@/types/api';
import { apiDocumentation } from '@/lib/data/api-documentation';
@@ -34,6 +34,9 @@ export function ApiDetailsPage() {
              </CardTitle>
            </CardHeader>
            <CardContent className="space-y-4 px-6 py-6">
              {documentation?.workflow && (
                <Workflow steps={documentation.workflow} />
              )}
              <ExpandableMarkdown key={api.id}>
                {documentation?.description ?? api.description}
              </ExpandableMarkdown>
@@ -112,10 +115,9 @@ export function ApiDetailsPage() {
            </section>
          ) : null}

          <section className="space-y-4">
            <SectionTitle title="API Usage" />
            <div className="space-y-6">
          {documentation?.instructions && (
            <section className="space-y-4">
              <SectionTitle title="How to use" />
              <Card>
                <CardHeader>
                  <CardTitle className="text-xl font-semibold text-sidebar-primary">
@@ -126,67 +128,8 @@ export function ApiDetailsPage() {
                  <Markdown>{documentation.instructions}</Markdown>
                </CardContent>
              </Card>
              )}
              <Card>
                <CardHeader>
                  <CardTitle className="text-xl font-semibold text-sidebar-primary">
                    API operations
                  </CardTitle>
                  <CardDescription>
                    Operations documented in the API specification.
                  </CardDescription>
                </CardHeader>

                <CardContent className="space-y-5">
                  {api.operations && api.operations.length > 0 ? (
                    <div>
                      <ul className="grid gap-3 md:grid-cols-2">
                        {api.operations.map((operation) => (
                          <li
                            key={`${operation.method} ${operation.path}`}
                            className="rounded-md border bg-muted/30 p-3"
                          >
                            <div className="flex flex-wrap items-center gap-2">
                              <Badge>{operation.method}</Badge>

                              <code className="text-sm font-medium">
                                {operation.path}
                              </code>
                            </div>

                            <p className="mt-2 text-sm font-medium">
                              {operation.name}
                            </p>

                            <p className="mt-1 text-sm text-muted-foreground">
                              {operation.description}
                            </p>
                          </li>
                        ))}
                      </ul>
                    </div>
                  ) : (
                    api.functionalities.length > 0 && (
                      <div>
                        <h3 className="text-sm font-semibold">Operations</h3>

                        <ul className="mt-3 space-y-2">
                          {api.functionalities.map((operation) => (
                            <li
                              key={operation}
                              className="rounded-md border bg-muted/30 px-3 py-2 text-sm text-muted-foreground"
                            >
                              {operation}
                            </li>
                          ))}
                        </ul>
                      </div>
                    )
                  )}
                </CardContent>
              </Card>
            </div>
            </section>
          )}

          <section className="space-y-4">
            <SectionTitle title="API reference" />
Loading