Commit 1ebf85a7 authored by Dimitrios Gogos's avatar Dimitrios Gogos
Browse files

fix: clean up, add API card

parent 4fc87416
Loading
Loading
Loading
Loading
+34 −9
Original line number Diff line number Diff line
@@ -44,6 +44,28 @@
  margin: 0.4rem 0;
}

.operationsLabel {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #999;
  margin-top: 1rem;
}

.operations {
  margin: 0.4rem 0 0 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;

  li {
    font-size: 0.85rem;
    color: var(--dark-grey);
    line-height: 1.4;
  }
}

.badge {
  background: linear-gradient(45deg, #f159225b 0%, #fdbb135b 100%);
  color: var(--blue-color);
@@ -102,36 +124,41 @@
  }
}

/* Hide top description block from Swagger UI */
.infos {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

:global(.swagger-ui button) {
  all: revert;
  cursor: pointer;
  box-sizing: border-box;
}

:global(.swagger-ui .information-container) {
  display: none;
}

/* Hide top header */
:global(.swagger-ui .topbar) {
  display: none;
}

/* Hide Schemas section */
:global(.swagger-ui .models) {
  display: none;
}

/* Hide server selection */
:global(.swagger-ui .scheme-container) {
  display: none;
}

/* Hide Filter Field */
:global(.swagger-ui .filter-container) {
  display: none;
}

/* JSON keys */
:global(.swagger-ui .hljs-attr) {
  color: #ffffff !important;
}
/* JSON punctuation like :, {}, [] */
:global(.swagger-ui .example) {
  color: #ffffff !important;
}
@@ -144,13 +171,11 @@
:global(.swagger-ui .highlight-code span) {
  color: #ffffff !important;
}
/* Colons and brackets */
:global(.swagger-ui .language-json) {
  color: #ffffff !important;
}


/* Make sure responsesare also affected */
:global(.swagger-ui .headerline) {
  color: #ffffff !important;
}
+23 −43
Original line number Diff line number Diff line
"use client";
import "swagger-ui-react/swagger-ui.css";
import styles from "./apiPage.module.scss";
import { global } from "styled-jsx/css";
import { useEffect, useState } from "react";
import { useParams, useRouter } from "next/navigation";

@@ -14,42 +13,26 @@ import ErrorComponent from "@/app/components/Error/Error";
import Loader from "@/app/components/Loader/Loader";

import { format } from "date-fns";
import { StatusChip } from "@/app/components/Chip/StatusChip";
// import { StatusChip } from "@/app/components/Chip/StatusChip";

import SwaggerUI from "swagger-ui-react";

// import { oeg } from "@/app/utils/constants";
import { filterOpenApiByTag } from "@/app/utils/openapi-utils";

const ApiPage = () => {
  const {id} = useParams();
  const router = useRouter();
  const [api, setApi] = useState<IApi | null>(null);
  // const [swaggerSpec, setSwaggerSpec] = useState<any | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchApi = async() => {
      try {
        // Fetch the hardcoded API details
        const res = await fetch(`/api/apis/${id}`);
        if (!res.ok) throw new Error("Not found");

        const data = await res.json();
        setApi(data);

        // Fetch the OpenAPI JSON
        // const specRes = await fetch("/api/swagger"); 
        // const spec = await specRes.json();

        // Filter spec by the API category (?)
        // const filteredSpec = filterOpenApiByTag(spec, data.category || data.title);
        // delete filteredSpec.info.description;
        // console.log("filteredSpec = " + filteredSpec);
        // setSwaggerSpec(filteredSpec);
      } catch (err) {
        setApi(null);
        // setSwaggerSpec(null);
      } finally {
        setIsLoading(false);
      }
@@ -76,16 +59,6 @@ const ApiPage = () => {

      <p className={styles.category}>{api.category}</p>

      <div className={styles.functionalities}>
        {api.functionalities.map((func: string, idx: number) => (
          // <span key={func} className={styles.badge}>
          //   {func.toLocaleUpperCase()}
          // </span>
          <StatusChip key={idx} status={func} />
        ))}
      </div>

      {/* <Button className={buttons.primary}>Activate</Button> */}
      <div className={styles.descriptionBox}>
        <h3>Description</h3>
        <p>{api.description}</p>
@@ -97,25 +70,32 @@ const ApiPage = () => {
            <strong>Version:</strong> {api.version}
          </p>
          <p className={styles.meta}>
            <strong>Protocol:</strong> {api.protocol}
          </p>
          <p className={styles.meta}>
            <strong>Live URL:</strong> {api.liveUrl}
            <strong>Protocol:</strong> REST
          </p>
          <p className={styles.meta}>
            <strong>Released Date:</strong>{" "}
            {format(api.published, "dd/MM/yyyy")}
          </p>
          <p className={styles.meta}>
            <strong>Status:</strong> {api.status}
            <strong>Status:</strong> Active
          </p>
        </div>
        <div className={styles.howItWorks}>
          <h3>How it works</h3>
          <p>{api.instructions}</p>
          <p className={styles.operationsLabel}>Available operations</p>
          {api.functionalities.some((f) => f.length > 30) ? (
            <ul className={styles.operations}>
              {api.functionalities.map((func: string, idx: number) => (
                <li key={idx}>{func}</li>
              ))}
            </ul>
          ) : null}
          {/* fallback chips — re-enable when short labels are needed
          {api.functionalities.some((f) => f.length <= 30) && (
            <div className={styles.functionalities}>
              {api.functionalities.map((func: string, idx: number) => (
                <StatusChip key={idx} status={func} />
              ))}
            </div>
          )} */}
        </div>

        
      </div>
      <Divider size="sm" label="API Documentation" color="#004a8d" />

@@ -124,8 +104,8 @@ const ApiPage = () => {
          <SwaggerUI 
            spec={swaggerSpec} 
            docExpansion="none" // collapse endpoints
            defaultModelsExpandDepth={-1} // hides Schemas section
            defaultModelExpandDepth={-1} // hides model details
            defaultModelsExpandDepth={-1}
            defaultModelExpandDepth={-1}
            displayRequestDuration={false} 
            displayOperationId={false} 
            tryItOutEnabled={true}
@@ -138,8 +118,8 @@ const ApiPage = () => {
        <SwaggerUI
          url="/api/swagger"
          docExpansion="list"
          defaultModelsExpandDepth={-1} // hides Schemas section
          defaultModelExpandDepth={-1} // hides model details
          defaultModelsExpandDepth={-1}
          defaultModelExpandDepth={-1}
          displayOperationId={false}
          displayRequestDuration={false}
          filter={api.tag}
+15 −15
Original line number Diff line number Diff line
@@ -8,7 +8,7 @@ import {
  Input,
  InputGroup,
  Pagination,
  SegmentedControl,
  // SegmentedControl,
} from "rsuite";
import styles from "../styles/page.module.scss";
import buttons from "../styles/buttons.module.scss";
@@ -17,7 +17,7 @@ import { IApi } from "../utils/interfaces";
import { FilterSection } from "../components/Filter/FilterSection";
import { FilterChips } from "../components/Filter/FilterChips";
import { ApiCard } from "../components/Card/ApiCard";
import { sortOptions } from "../utils/constants";
// import { sortOptions } from "../utils/constants";
import { SidebarFilter } from "../components/Filter/SidebarFilter";
import { useFilters } from "../hooks/useFilters";

@@ -34,21 +34,21 @@ export default function Home() {
    setPageSize,
    search,
    setSearch,
    sort,
    setSort,
    //sort,
    //setSort,
    selectedCategories,
    //selectedProviders,
    selectedFunctionalities,
    //selectedFunctionalities,
    setSelectedCategories,
    setSelectedProviders,
    setSelectedFunctionalities,
    //setSelectedFunctionalities,
    categoryCounts,
    //providerCounts,
    functionalityCounts,
    //functionalityCounts,
    resetFilters,
    categories,
    //providers,
    functionalities,
    //functionalities,
    removeFilter,
    sumSelectedFilters,
  } = useFilters(apis);
@@ -72,16 +72,16 @@ export default function Home() {
    <SidebarFilter
      categories={categories}
      //providers={providers}
      functionalities={functionalities}
      //functionalities={functionalities}
      selectedCategories={selectedCategories}
      //selectedProviders={selectedProviders}
      selectedFunctionalities={selectedFunctionalities}
      //selectedFunctionalities={selectedFunctionalities}
      setSelectedCategories={setSelectedCategories}
      //setSelectedProviders={setSelectedProviders}
      setSelectedFunctionalities={setSelectedFunctionalities}
      //setSelectedFunctionalities={setSelectedFunctionalities}
      categoryCounts={categoryCounts}
      //providerCounts={providerCounts}
      functionalityCounts={functionalityCounts}
      //functionalityCounts={functionalityCounts}
      resetFilters={resetFilters}
    />
  );
@@ -107,7 +107,7 @@ export default function Home() {
          filters={{
            categories: selectedCategories,
            //providers: selectedProviders,
            functionalities: selectedFunctionalities,
            //functionalities: selectedFunctionalities,
          }}
          removeFilter={removeFilter}
        />
@@ -126,7 +126,7 @@ export default function Home() {
              {filtersIcon} Filters
            </Button>
          </Badge>
          <div className={styles.sortBox}>
          {/* <div className={styles.sortBox}>
            <h6>Sort by:</h6>
            <SegmentedControl
              defaultValue={sort}
@@ -134,7 +134,7 @@ export default function Home() {
              data={sortOptions}
              onChange={(v) => setSort(v as string)}
            />
          </div>
          </div> */}
        </div>

        <div className={styles.cards}>
+43 −0
Original line number Diff line number Diff line
import { NextResponse } from "next/server";
import { apiData } from "@/app/utils/tableHelpers";
import { oeg } from "@/app/utils/constants";

export async function GET(
  _req: Request,
  { params }: { params: Promise<{ id: string }> }
) {
  try {
    const { id } = await params;

    const api = apiData.find((a) => a.id === id);

    if (!api) {
      return NextResponse.json({ error: "Not found" }, { status: 404 });
    }

    let liveFunctionalities: string[] = [];

    try {
      const specRes = await fetch(oeg.jsonUrl, { cache: "no-store" });
      const spec = await specRes.json();

      for (const methods of Object.values(spec.paths as Record<string, any>)) {
        for (const operation of Object.values(methods as Record<string, any>)) {
          const op = operation as any;
          if (op.tags?.includes(api.tag) && op.summary) {
            liveFunctionalities.push(op.summary);
          }
        }
      }
    } catch {
      liveFunctionalities = [];
    }

    const functionalities =
      liveFunctionalities.length > 0 ? liveFunctionalities : api.functionalities;

    return NextResponse.json({ ...api, functionalities });
  } catch {
    return NextResponse.json({ error: "Failed to load API" }, { status: 500 });
  }
}
+31 −52
Original line number Diff line number Diff line
// import { error } from "console";
import { NextResponse, NextRequest } from "next/server";

import { oeg } from "@/app/utils/constants";


async function proxy(
    req: NextRequest) 
{
  req: NextRequest,
  { params }: { params: Promise<{ path: string[] }> }
) {
  try {
       
        // console.log("Request = " + req.nextUrl);
        const urlparts1 = req.nextUrl.toString().split('/');
        const urlparts2 = urlparts1[urlparts1.length - 1].split('?');
        const tag = urlparts2[0];
        // console.log("Tag = " + tag);
  
    const { path } = await params;
    const search = req.nextUrl.search;
        // console.log("search = " + search);
        const targetUrl = `${oeg.baseUrl}${tag}${search}`;
        // console.log("Proxying to: " + targetUrl);
    const targetUrl = `${oeg.baseUrl}/${path.join("/")}${search}`;

        const backendRes = await fetch(
            targetUrl,
            {
    const backendRes = await fetch(targetUrl, {
      method: req.method,
      headers: {
        "Content-Type": req.headers.get("content-type") || "application/json",
                    Accept: req.headers.get("accept") || "application/json"
        Accept: req.headers.get("accept") || "application/json",
      },
      body:
        req.method !== "GET" && req.method !== "HEAD"
          ? await req.text()
                    : undefined
            } 
        );
          : undefined,
    });

        // Return raw response
    return new NextResponse(backendRes.body, {
      status: backendRes.status,
      headers: {
                "Content-Type": backendRes.headers.get("content-type") || "application/json"
            }
        "Content-Type": backendRes.headers.get("content-type") || "application/json",
      },
    });

  } catch (err) {
    console.error("Proxy error:", err);
        return NextResponse.json(
            { error: "Proxy failed" },
            { status: 500 }

        );
    };
    
    
    return NextResponse.json({ error: "Proxy failed" }, { status: 500 });
  }
}

export const GET = proxy;
Loading