| import { useEffect, useRef, useState } from "react"; |
| import { Link } from "wouter"; |
| import { ArrowLeft, Loader2 } from "lucide-react"; |
| |
| import SwaggerUIBundle from "swagger-ui-dist/swagger-ui-bundle.js"; |
| import "swagger-ui-dist/swagger-ui.css"; |
|
|
| export default function SwaggerPage() { |
| const containerRef = useRef<HTMLDivElement>(null); |
| const [loading, setLoading] = useState(true); |
| const [error, setError] = useState<string | null>(null); |
|
|
| useEffect(() => { |
| if (!containerRef.current) return; |
|
|
| |
| const base = (import.meta.env.VITE_API_BASE as string | undefined) ?? ""; |
| const url = `${base}/openapi.json`; |
|
|
| try { |
| SwaggerUIBundle({ |
| url, |
| domNode: containerRef.current, |
| presets: [SwaggerUIBundle.presets.apis, SwaggerUIBundle.SwaggerUIStandalonePreset], |
| layout: "BaseLayout", |
| deepLinking: true, |
| displayRequestDuration: true, |
| filter: true, |
| tryItOutEnabled: true, |
| onComplete: () => setLoading(false), |
| }); |
| setLoading(false); |
| } catch (e) { |
| setError(String(e)); |
| setLoading(false); |
| } |
| }, []); |
|
|
| return ( |
| <div className="flex flex-col h-screen" style={{ background: "var(--bg-base)", color: "var(--text-primary)" }}> |
| {/* Header */} |
| <header |
| className="flex items-center gap-3 px-4 py-2.5 flex-shrink-0" |
| style={{ borderBottom: "1px solid var(--border)", background: "var(--bg-surface)" }} |
| > |
| <Link href="/"> |
| <button |
| className="flex items-center gap-1.5 h-7 px-2 text-xs rounded transition-colors" |
| style={{ color: "var(--text-secondary)" }} |
| onMouseEnter={(e) => (e.currentTarget.style.color = "var(--text-primary)")} |
| onMouseLeave={(e) => (e.currentTarget.style.color = "var(--text-secondary)")} |
| > |
| <ArrowLeft size={12} /> |
| Back to Analyzer |
| </button> |
| </Link> |
| <span className="text-sm font-semibold">API Documentation</span> |
| <span |
| className="text-[10px] px-1.5 py-0.5 rounded" |
| style={{ |
| background: "rgba(88,166,255,0.12)", |
| color: "var(--accent-blue)", |
| border: "1px solid rgba(88,166,255,0.25)", |
| fontFamily: "var(--font-mono)", |
| }} |
| > |
| OpenAPI 3.1 |
| </span> |
| </header> |
| |
| {/* Swagger UI */} |
| <div className="flex-1 overflow-auto" style={{ background: "#fff" }}> |
| {loading && ( |
| <div className="flex items-center justify-center h-full gap-2" style={{ color: "#666" }}> |
| <Loader2 size={16} className="animate-spin" /> |
| <span className="text-sm">Loading API docs…</span> |
| </div> |
| )} |
| {error && ( |
| <div className="flex items-center justify-center h-full"> |
| <p className="text-sm text-red-600">Failed to load API docs: {error}</p> |
| </div> |
| )} |
| <div ref={containerRef} className={loading ? "hidden" : ""} /> |
| </div> |
| </div> |
| ); |
| } |
|
|