diff --git a/src/components/documents/secret/SecretForm.tsx b/src/components/documents/secret/SecretForm.tsx new file mode 100644 index 0000000..185f371 --- /dev/null +++ b/src/components/documents/secret/SecretForm.tsx @@ -0,0 +1,60 @@ +// SecretForm.tsx +// Form for adding a new secret to a session. +import { useState } from "react"; +import type { Session, Secret } from "@/lib/types"; +import { pb } from "@/lib/pocketbase"; + +/** + * Renders a form to add a new secret. Calls onCreate with the new secret document. + */ +export const SecretForm = ({ session, onCreate }: { session: Session; onCreate: (secret: Secret) => Promise; }) => { + const [newSecret, setNewSecret] = useState(""); + const [adding, setAdding] = useState(false); + const [error, setError] = useState(null); + + async function handleSubmit(e: React.FormEvent) { + e.preventDefault(); + if (!newSecret.trim()) return; + setAdding(true); + setError(null); + try { + const secretDoc: Secret = await pb.collection("documents").create({ + campaign: session.campaign, + data: { + secret: { + text: newSecret, + discovered: false, + }, + }, + }); + setNewSecret(""); + await onCreate(secretDoc); + } catch (e: any) { + setError(e?.message || "Failed to add secret."); + } finally { + setAdding(false); + } + } + + return ( +
+ setNewSecret(e.target.value)} + disabled={adding} + aria-label="Add new secret" + /> + {error &&
{error}
} + +
+ ); +}; diff --git a/src/components/documents/secret/SecretRow.tsx b/src/components/documents/secret/SecretRow.tsx new file mode 100644 index 0000000..68a46ed --- /dev/null +++ b/src/components/documents/secret/SecretRow.tsx @@ -0,0 +1,65 @@ +// SecretRow.tsx +// Displays a single secret with discovered checkbox and text. +import type { Secret, Session } from "@/lib/types"; +import { pb } from "@/lib/pocketbase"; +import { useState } from "react"; + +/** + * Renders a secret row with a discovered checkbox and secret text. + * Handles updating the discovered state and discoveredIn relationship. + */ +export const SecretRow = ({ secret, session }: { secret: Secret; session: Session }) => { + const [checked, setChecked] = useState(!!(secret.data as any)?.secret?.discovered); + const [loading, setLoading] = useState(false); + + async function handleChange(e: React.ChangeEvent) { + const newChecked = e.target.checked; + setLoading(true); + setChecked(newChecked); + try { + await pb.collection("documents").update(secret.id, { + data: { + ...secret.data, + secret: { + ...(secret.data as any).secret, + discovered: newChecked, + }, + }, + }); + // Remove any existing discoveredIn relationship + const rels = await pb.collection("relationships").getList(1, 1, { + filter: `primary = "${secret.id}" && type = "discoveredIn"`, + }); + if (rels.items.length > 0) { + await pb.collection("relationships").delete(rels.items[0].id); + } + if (newChecked) { + await pb.collection("relationships").create({ + primary: secret.id, + secondary: [session.id], + type: "discoveredIn", + }); + } + } finally { + setLoading(false); + } + } + + return ( +
+ + + {(secret.data as any)?.secret?.text || ( + (No secret text) + )} + +
+ ); +}; diff --git a/src/routes/_authenticated/document.$documentId.tsx b/src/routes/_authenticated/document.$documentId.tsx index 0ba70a0..5e3f62d 100644 --- a/src/routes/_authenticated/document.$documentId.tsx +++ b/src/routes/_authenticated/document.$documentId.tsx @@ -1,9 +1,10 @@ import { createFileRoute } from "@tanstack/react-router"; import { pb } from "@/lib/pocketbase"; -import { useState } from "react"; -import { RelationshipType, type Secret, type Session } from "@/lib/types"; +import { RelationshipType, type Session } from "@/lib/types"; import { RelationshipList } from "@/components/RelationshipList"; import { SessionForm } from "@/components/documents/session/SessionForm"; +import { SecretRow } from "@/components/documents/secret/SecretRow"; +import { SecretForm } from "@/components/documents/secret/SecretForm"; export const Route = createFileRoute("/_authenticated/document/$documentId")({ loader: async ({ params }) => { @@ -15,9 +16,6 @@ export const Route = createFileRoute("/_authenticated/document/$documentId")({ function RouteComponent() { const { document: session }: { document: Session } = Route.useLoaderData(); - const [newSecret, setNewSecret] = useState(""); - const [adding, setAdding] = useState(false); - const [error, setError] = useState(null); async function handleSaveSession(data: Session["data"]) { await pb.collection("documents").update(session.id, { @@ -31,97 +29,12 @@ function RouteComponent() { ( -
- { - const checked = e.target.checked; - await pb.collection("documents").update(secret.id, { - data: { - ...secret.data, - secret: { - ...(secret.data as any).secret, - discovered: checked, - }, - }, - }); - // Remove any existing discoveredIn relationship - const rels = await pb - .collection("relationships") - .getList(1, 1, { - filter: `primary = "${secret.id}" && type = "discoveredIn"`, - }); - if (rels.items.length > 0) { - await pb.collection("relationships").delete(rels.items[0].id); - } - if (checked) { - await pb.collection("relationships").create({ - primary: secret.id, - secondary: [session.id], - type: "discoveredIn", - }); - } - }} - className="accent-emerald-500 w-5 h-5" - aria-label="Discovered" - /> - - {(secret.data as any)?.secret?.text || ( - (No secret text) - )} - -
- )} + renderRow={(secret) => { + if (!(secret.data as any)?.secret) return null; + return ; + }} newItemForm={(onCreate) => ( -
{ - e.preventDefault(); - if (!newSecret.trim()) return; - setAdding(true); - setError(null); - try { - console.debug("Creating new secret"); - // Create the secret document - const secretDoc: Secret = await pb - .collection("documents") - .create({ - campaign: session.campaign, - data: { - secret: { - text: newSecret, - discovered: false, - }, - }, - }); - setNewSecret(""); - await onCreate(secretDoc); - } catch (e: any) { - setError(e?.message || "Failed to add secret."); - } finally { - setAdding(false); - } - }} - > - setNewSecret(e.target.value)} - disabled={adding} - /> - {error &&
{error}
} - -
+ )} />