From a2e4a32d2824de48353f2610563341bbc994fb8d Mon Sep 17 00:00:00 2001 From: Trezy Date: Thu, 19 Feb 2026 11:11:20 -0600 Subject: [PATCH] fix: wrap dynamic page with a server component to fix build --- .../lexicons/[id]/lexicon-detail.tsx | 226 +++++++++++++++++ .../app/(dashboard)/lexicons/[id]/page.tsx | 229 +----------------- 2 files changed, 233 insertions(+), 222 deletions(-) create mode 100644 web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx diff --git a/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx b/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx new file mode 100644 index 0000000..75538ae --- /dev/null +++ b/web/src/app/(dashboard)/lexicons/[id]/lexicon-detail.tsx @@ -0,0 +1,226 @@ +"use client"; + +import { useCallback, useEffect, useState } from "react"; +import { useParams, useRouter } from "next/navigation"; + +import { useAuth } from "@/lib/auth-context"; +import { CodePanels } from "@/components/code-panels"; +import { + deleteLexicon, + deleteNetworkLexicon, + getLexicon, + uploadLexicon, + type LexiconDetail, +} from "@/lib/api"; +import { procedureScript, queryScript } from "@/lib/lua-templates"; +import { useLuaCompletions } from "@/hooks/use-lua-completions"; +import { SiteHeader } from "@/components/site-header"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; + +export default function LexiconDetailPage() { + const { id } = useParams<{ id: string }>(); + const { getToken } = useAuth(); + const router = useRouter(); + const [lexicon, setLexicon] = useState(null); + const [error, setError] = useState(null); + const [deleting, setDeleting] = useState(false); + const [saving, setSaving] = useState(false); + + // Editable text state + const [jsonText, setJsonText] = useState(""); + const [luaText, setLuaText] = useState(""); + const [originalJson, setOriginalJson] = useState(""); + const [originalLua, setOriginalLua] = useState(""); + const { luaCompletions, collections } = useLuaCompletions(jsonText); + + const load = useCallback(() => { + getLexicon(getToken, id) + .then((lex) => { + setLexicon(lex); + const json = JSON.stringify(lex.lexicon_json, null, 2); + setJsonText(json); + setOriginalJson(json); + + // If lexicon has no script but is a query/procedure, auto-generate one + if ( + !lex.script && + (lex.lexicon_type === "query" || lex.lexicon_type === "procedure") + ) { + const generated = + lex.lexicon_type === "procedure" + ? procedureScript(lex.target_collection ?? "") + : queryScript(lex.target_collection ?? ""); + setLuaText(generated); + // Set originalLua to "" so isDirty becomes true, prompting user to save + setOriginalLua(""); + } else { + setLuaText(lex.script ?? ""); + setOriginalLua(lex.script ?? ""); + } + }) + .catch((e) => setError(e instanceof Error ? e.message : String(e))); + }, [getToken, id]); + + useEffect(() => { + load(); + }, [load]); + + const isDirty = jsonText !== originalJson || luaText !== originalLua; + + async function handleSave() { + if (!lexicon) return; + setSaving(true); + setError(null); + try { + const lexiconJson = JSON.parse(jsonText); + await uploadLexicon(getToken, { + lexicon_json: lexiconJson, + backfill: lexicon.backfill, + script: luaText || undefined, + }); + load(); + } catch (e: unknown) { + setError(e instanceof Error ? e.message : String(e)); + } finally { + setSaving(false); + } + } + + async function handleDelete() { + if (!lexicon) return; + setDeleting(true); + try { + if (lexicon.source === "network") { + await deleteNetworkLexicon(getToken, lexicon.id); + } else { + await deleteLexicon(getToken, lexicon.id); + } + router.push("/lexicons"); + } catch (e: unknown) { + setError(e instanceof Error ? e.message : String(e)); + setDeleting(false); + } + } + + if (error && !lexicon) { + return ( + <> + +
+

{error}

+
+ + ); + } + + if (!lexicon) { + return ( + <> + +
+

Loading...

+
+ + ); + } + + const isNetwork = lexicon.source === "network"; + const showLua = + lexicon.has_script || + lexicon.lexicon_type === "query" || + lexicon.lexicon_type === "procedure"; + + return ( +
+ +
+
+ {error &&

{error}

} + + {/* Metadata */} +
+
+ +
+ {lexicon.lexicon_type} +
+
+
+ +
+ + {lexicon.source} + +
+
+
+ +

{lexicon.revision}

+
+
+ +

{lexicon.backfill ? "Yes" : "No"}

+
+ {lexicon.authority_did && ( +
+ +

+ {lexicon.authority_did} +

+
+ )} +
+ +

+ {new Date(lexicon.created_at).toLocaleString()} +

+
+
+ +

+ {new Date(lexicon.updated_at).toLocaleString()} +

+
+ {lexicon.last_fetched_at && ( +
+ +

+ {new Date(lexicon.last_fetched_at).toLocaleString()} +

+
+ )} +
+
+ + {/* Code Panels */} + + + {/* Actions */} +
+ + + +
+
+
+ ); +} diff --git a/web/src/app/(dashboard)/lexicons/[id]/page.tsx b/web/src/app/(dashboard)/lexicons/[id]/page.tsx index 75538ae..cc66f44 100644 --- a/web/src/app/(dashboard)/lexicons/[id]/page.tsx +++ b/web/src/app/(dashboard)/lexicons/[id]/page.tsx @@ -1,226 +1,11 @@ -"use client"; +import LexiconDetail from "./lexicon-detail"; -import { useCallback, useEffect, useState } from "react"; -import { useParams, useRouter } from "next/navigation"; - -import { useAuth } from "@/lib/auth-context"; -import { CodePanels } from "@/components/code-panels"; -import { - deleteLexicon, - deleteNetworkLexicon, - getLexicon, - uploadLexicon, - type LexiconDetail, -} from "@/lib/api"; -import { procedureScript, queryScript } from "@/lib/lua-templates"; -import { useLuaCompletions } from "@/hooks/use-lua-completions"; -import { SiteHeader } from "@/components/site-header"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; +// https://github.com/vercel/next.js/issues/71862 +// Returning [] fails with output:"export", so provide a dummy param. +export async function generateStaticParams() { + return [{ id: "_" }]; +} export default function LexiconDetailPage() { - const { id } = useParams<{ id: string }>(); - const { getToken } = useAuth(); - const router = useRouter(); - const [lexicon, setLexicon] = useState(null); - const [error, setError] = useState(null); - const [deleting, setDeleting] = useState(false); - const [saving, setSaving] = useState(false); - - // Editable text state - const [jsonText, setJsonText] = useState(""); - const [luaText, setLuaText] = useState(""); - const [originalJson, setOriginalJson] = useState(""); - const [originalLua, setOriginalLua] = useState(""); - const { luaCompletions, collections } = useLuaCompletions(jsonText); - - const load = useCallback(() => { - getLexicon(getToken, id) - .then((lex) => { - setLexicon(lex); - const json = JSON.stringify(lex.lexicon_json, null, 2); - setJsonText(json); - setOriginalJson(json); - - // If lexicon has no script but is a query/procedure, auto-generate one - if ( - !lex.script && - (lex.lexicon_type === "query" || lex.lexicon_type === "procedure") - ) { - const generated = - lex.lexicon_type === "procedure" - ? procedureScript(lex.target_collection ?? "") - : queryScript(lex.target_collection ?? ""); - setLuaText(generated); - // Set originalLua to "" so isDirty becomes true, prompting user to save - setOriginalLua(""); - } else { - setLuaText(lex.script ?? ""); - setOriginalLua(lex.script ?? ""); - } - }) - .catch((e) => setError(e instanceof Error ? e.message : String(e))); - }, [getToken, id]); - - useEffect(() => { - load(); - }, [load]); - - const isDirty = jsonText !== originalJson || luaText !== originalLua; - - async function handleSave() { - if (!lexicon) return; - setSaving(true); - setError(null); - try { - const lexiconJson = JSON.parse(jsonText); - await uploadLexicon(getToken, { - lexicon_json: lexiconJson, - backfill: lexicon.backfill, - script: luaText || undefined, - }); - load(); - } catch (e: unknown) { - setError(e instanceof Error ? e.message : String(e)); - } finally { - setSaving(false); - } - } - - async function handleDelete() { - if (!lexicon) return; - setDeleting(true); - try { - if (lexicon.source === "network") { - await deleteNetworkLexicon(getToken, lexicon.id); - } else { - await deleteLexicon(getToken, lexicon.id); - } - router.push("/lexicons"); - } catch (e: unknown) { - setError(e instanceof Error ? e.message : String(e)); - setDeleting(false); - } - } - - if (error && !lexicon) { - return ( - <> - -
-

{error}

-
- - ); - } - - if (!lexicon) { - return ( - <> - -
-

Loading...

-
- - ); - } - - const isNetwork = lexicon.source === "network"; - const showLua = - lexicon.has_script || - lexicon.lexicon_type === "query" || - lexicon.lexicon_type === "procedure"; - - return ( -
- -
-
- {error &&

{error}

} - - {/* Metadata */} -
-
- -
- {lexicon.lexicon_type} -
-
-
- -
- - {lexicon.source} - -
-
-
- -

{lexicon.revision}

-
-
- -

{lexicon.backfill ? "Yes" : "No"}

-
- {lexicon.authority_did && ( -
- -

- {lexicon.authority_did} -

-
- )} -
- -

- {new Date(lexicon.created_at).toLocaleString()} -

-
-
- -

- {new Date(lexicon.updated_at).toLocaleString()} -

-
- {lexicon.last_fetched_at && ( -
- -

- {new Date(lexicon.last_fetched_at).toLocaleString()} -

-
- )} -
-
- - {/* Code Panels */} - - - {/* Actions */} -
- - - -
-
-
- ); + return ; } -- 2.51.2