Files
mcpedia/apps/web/app/lib/api.ts
T
mytheclipsebotreviewandinternal-model 4330f47c65
CI / typecheck + tests (push) Canceled after 0s
CI / build + deploy (Nix) — api (push) Canceled after 0s
CI / build + deploy (Nix) — mcp (push) Canceled after 0s
CI / build + deploy (Nix) — worker (push) Canceled after 0s
CI / build + deploy (web) (push) Canceled after 0s
feat(web): FE overhaul — SWR centralized data layer + dashboard + islands
- Add SWR foundation (lib/fetcher, lib/swr, lib/api) with typed
  hooks: useSections/useDocuments/useSearch/useRelated/useRevisions/useQueueStatus
- Replace ad-hoc useEffect+fetch in Header/Sidebar/CommandMenu/search
  with SWR (dedup 4s, focus throttle 10s, debounced search 180ms)
- Doc page: RelatedGrid + RevisionList as SWR client islands (RSC hero stays)
- DocForm: SWR mutate on create/update for live sidebar invalidation
- New APIs: /api/related, /api/revisions, /api/tags, /api/queue/status,
  /api/docs filtering (?section=&status=)
- New UI: DocumentCard, SectionCard, SearchBar, TagCloud, QueueBadge,
  EmptyState, dashboard (/dashboard with QueueStrip live + section stats)
- Layout wraps SWRProvider; no TanStack code (SWR-only per spec)
- Verified: next build passes (16.3.4 Turbopack), no manual fetch sprawl remains

Co-Authored-By: internal-model
2026-09-08 19:37:27 +07:00

78 lines
2.4 KiB
TypeScript

import type { Document, DocumentMeta, SectionInfo } from "@mcpedia/types";
import { fetcher } from "./fetcher";
export interface SearchApiHit {
slug: string;
title: string;
section: string;
score: number;
snippet: string;
}
export interface SearchApiResponse {
results: SearchApiHit[];
error?: string;
}
export interface DocsApiResponse {
docs?: DocumentMeta[];
error?: string;
}
export interface SectionsApiResponse extends Array<SectionInfo> {}
export interface SingleDocResponse {
doc: Document;
}
export interface RelatedResponse {
results: DocumentMeta[];
}
export interface RevisionsResponse {
revisions: { id: string; slug: string; revisionNo: number; title: string; reason: string; createdAt: string; bodyLength: number }[];
}
export interface QueueStatusResponse {
waiting: number;
active: number;
completed: number;
failed: number;
delayed: number;
}
export interface TagsResponse {
tags: { tag: string; count: number }[];
}
export function docsUrl(section?: string, status = "published"): string {
const p = new URLSearchParams();
if (section) p.set("section", section);
if (status) p.set("status", status);
const qs = p.toString();
return qs ? `/api/docs?${qs}` : "/api/docs";
}
export function searchUrl(q: string, mode = "hybrid", limit = 30): string {
return `/api/search?q=${encodeURIComponent(q)}&mode=${mode}&limit=${limit}`;
}
export type FetchDocs = () => Promise<DocumentMeta[]>;
export const fetchDocs: FetchDocs = async () => {
const data = await fetcher<DocsApiResponse | DocumentMeta[]>("/api/docs");
if (Array.isArray(data)) return data;
return data.docs ?? [];
};
export const fetchSections = () => fetcher<SectionInfo[]>("/api/sections");
export const fetchSearch = (q: string, mode = "hybrid", limit = 30) =>
fetcher<SearchApiResponse>(searchUrl(q, mode, limit));
export const fetchRelated = (slug: string, limit = 5) =>
fetcher<RelatedResponse>(`/api/related?slug=${encodeURIComponent(slug)}&limit=${limit}`);
export const fetchRevisions = (slug: string, limit = 20) =>
fetcher<RevisionsResponse>(`/api/revisions?slug=${encodeURIComponent(slug)}&limit=${limit}`);
export const fetchSingleDoc = (slug: string) =>
fetcher<SingleDocResponse>(`/api/docs/${encodeURIComponent(slug)}`);
export const fetchQueueStatus = () => fetcher<QueueStatusResponse>("/api/queue/status");
export const fetchTags = () => fetcher<TagsResponse>("/api/tags");