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

feat: add API clients management page with listing, notices, and error handling

parent f581454b
Loading
Loading
Loading
Loading
+9 −0
Changes for portal-gui/src/app/config/navigation.ts: 9 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@ import {
  Rocket,
  UserIcon,
  Handshake,
  KeyRoundIcon,
} from 'lucide-react';

import { ROUTES } from './route';
@@ -30,6 +31,14 @@ export const mainNavItems: NavItem[] = [
    showInSidebar: true,
  },

  {
    title: 'API clients',
    to: ROUTES.API_CLIENTS,
    description: 'Credentials for calling the APIs from your own systems.',
    icon: KeyRoundIcon,
    showInSidebar: true,
    roles: ['app-provider'],
  },
  {
    title: 'Applications',
    to: ROUTES.APPLICATIONS,
+1 −0
Changes for portal-gui/src/app/config/route.ts: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ export const ROUTES = {

  PROFILE: '/profile',
  APIS: '/apis',
  API_CLIENTS: '/api-clients',
  API: '/apis/:id',
  APPLICATIONS: '/applications',
  APPLICATION_PROFILES: '/applications/profiles',
+9 −0
Changes for portal-gui/src/app/router.tsx: 9 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ import { RoleGuard } from './guards/role-guard';
import HomePage from '@/pages/home/index';
import ProfilePage from '@/pages/profile';
import LoginPage from '@/pages/auth/login';
import ApiClientsPage from '@/pages/api-clients';
import ApisPage from '@/pages/apis/';
import ApiDetailsPage from '@/pages/apis/api-details';
import ApplicationsPage from '@/pages/applications/index';
@@ -60,6 +61,14 @@ export const router = createBrowserRouter([
            path: ROUTES.APIS,
            element: <ApisPage />,
          },
          {
            path: ROUTES.API_CLIENTS,
            element: (
              <RoleGuard allowedRoles={['app-provider']}>
                <ApiClientsPage />
              </RoleGuard>
            ),
          },

          // API details MUST match /apis/:id
          {
+70 −0
Changes for portal-gui/src/components/api-clients/api-client-columns.tsx: 70 added lines, 0 removed lines.
Original line number Diff line number Diff line
import type { ColumnDef } from '@tanstack/react-table';

import { Badge } from '@/components/ui/badge';
import type { ApiClient } from '@/types/api-clients';

export const apiClientColumns: ColumnDef<ApiClient>[] = [
  {
    id: 'drag',
    header: () => null,
    enableHiding: false,
    size: 40,
  },
  {
    id: 'name',
    accessorKey: 'name',
    header: 'Name',
    cell: ({ row }) => {
      const client = row.original;
      return (
        <>
          <span
            className="block max-w-64 truncate font-medium"
            title={client.name}
          >
            {client.name}
          </span>
          {client.description && (
            <p className="mt-1 max-w-64 truncate text-xs text-muted-foreground">
              {client.description}
            </p>
          )}
        </>
      );
    },
    size: 220,
  },
  {
    id: 'client_id',
    accessorKey: 'client_id',
    header: 'Client ID',
    cell: ({ row }) => (
      <span className="font-mono text-xs">{row.original.client_id}</span>
    ),
    size: 140,
  },
  {
    id: 'products',
    header: 'Products',
    cell: ({ row }) => (
      <div className="flex flex-wrap gap-1">
        {row.original.products.map((product) => (
          <Badge key={product} variant="secondary">
            {product}
          </Badge>
        ))}
      </div>
    ),
    size: 280,
  },
  {
    id: 'created',
    accessorKey: 'created_at',
    header: 'Created',
    cell: ({ row }) =>
      row.original.created_at
        ? new Date(row.original.created_at).toLocaleDateString()
        : '—',
    size: 120,
  },
];
+24 −0
Changes for portal-gui/src/components/api-clients/api-clients-notices.tsx: 24 added lines, 0 removed lines.
Original line number Diff line number Diff line
import { InfoIcon } from 'lucide-react';

// R2 has no automatic clean-up, so these say what the portal does not do.
export function ApiClientsNotice() {
  return (
    <div className="mx-4 flex gap-3 rounded-md border bg-muted/40 p-3 text-sm lg:mx-6">
      <InfoIcon className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
      <p>
        API clients keep working until they are deleted. If you leave your
        organization, lose access to a product or no longer need an app, delete
        its API client, or ask an administrator to.
      </p>
    </div>
  );
}

export function ApiClientsFootnote() {
  return (
    <p className="px-4 text-sm text-muted-foreground lg:px-6">
      Changes an administrator makes in Keycloak may not show here. You can
      still delete any client from this page.
    </p>
  );
}
Loading