Skip to content
56 linesCodeBlameRaw
1/**
2 * How Docs pages change things: a JSON request to `-/docs/api`, then the
3 * sidebar and page load again so they show it. Returns the service's
4 * `{ ok, value }` or `{ ok: false, error }`.
5 */
6import type { Result } from "@g1t/contracts";
7import { useCallback, useState } from "react";
8import { useRevalidator, useRouteLoaderData } from "react-router";
9
10import type { DocsLayoutData } from "../../routes/workspace/docs/layout";
11
12export function useDocsData(): DocsLayoutData | undefined {
13 return useRouteLoaderData("routes/workspace/docs/layout") as DocsLayoutData | undefined;
14}
15
16export async function docsRequest<T>(slug: string, intent: string, body: Record<string, unknown> = {}): Promise<Result<T>> {
17 try {
18 const response = await fetch(`/${slug}/-/docs/api`, {
19 method: "POST",
20 headers: { "content-type": "application/json", accept: "application/json" },
21 body: JSON.stringify({ intent, ...body }),
22 });
23 return (await response.json()) as Result<T>;
24 } catch {
25 return { ok: false, error: { code: "conflict", message: "Docs didn't answer. Check your connection and try again." } };
26 }
27}
28
29export async function docsQuery<T>(slug: string, query: Record<string, string>): Promise<Result<T>> {
30 try {
31 const response = await fetch(`/${slug}/-/docs/api?${new URLSearchParams(query)}`, { headers: { accept: "application/json" } });
32 return (await response.json()) as Result<T>;
33 } catch {
34 return { ok: false, error: { code: "conflict", message: "Docs didn't answer. Check your connection and try again." } };
35 }
36}
37
38/** `send(intent, body)`: makes the change, then reloads what shows it. `busy` while it works; `error` when it failed. */
39export function useDocsAction(slug: string) {
40 const revalidator = useRevalidator();
41 const [busy, setBusy] = useState(false);
42 const [error, setError] = useState<string | null>(null);
43 const send = useCallback(
44 async <T>(intent: string, body: Record<string, unknown> = {}, options: { reload?: boolean } = {}): Promise<Result<T>> => {
45 setBusy(true);
46 setError(null);
47 const result = await docsRequest<T>(slug, intent, body);
48 setBusy(false);
49 if (!result.ok) setError(result.error.message);
50 else if (options.reload !== false) void revalidator.revalidate();
51 return result;
52 },
53 [slug, revalidator],
54 );
55 return { send, busy, error, setError };
56}