Adds secret check-offs
This commit is contained in:
@@ -37,7 +37,7 @@ export type Secret = Document &
|
|||||||
"secret",
|
"secret",
|
||||||
{
|
{
|
||||||
text: string;
|
text: string;
|
||||||
discoveredOn: ISO8601Date | null;
|
discovered: boolean;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
import { AutoSaveTextarea } from "@/components/AutoSaveTextarea";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import type { Secret } from "@/lib/types";
|
||||||
|
|
||||||
export const Route = createFileRoute(
|
export const Route = createFileRoute("/_authenticated/document/$documentId")({
|
||||||
'/_authenticated/document/$documentId',
|
|
||||||
)({
|
|
||||||
loader: async ({ params }) => {
|
loader: async ({ params }) => {
|
||||||
const doc = await pb.collection("documents").getOne(params.documentId);
|
const doc = await pb.collection("documents").getOne(params.documentId);
|
||||||
// Fetch the unique relationship where this document is the primary and type is "plannedSecrets"
|
// Fetch the unique relationship where this document is the primary and type is "plannedSecrets"
|
||||||
@@ -13,12 +12,13 @@ export const Route = createFileRoute(
|
|||||||
filter: `primary = "${params.documentId}" && type = "plannedSecrets"`,
|
filter: `primary = "${params.documentId}" && type = "plannedSecrets"`,
|
||||||
});
|
});
|
||||||
// Get all related secret document IDs from the secondary field
|
// Get all related secret document IDs from the secondary field
|
||||||
const secretIds = relationships.items.length > 0 ? relationships.items[0].secondary : [];
|
const secretIds =
|
||||||
|
relationships.items.length > 0 ? relationships.items[0].secondary : [];
|
||||||
// Fetch all related secret documents
|
// Fetch all related secret documents
|
||||||
let secrets: any[] = [];
|
let secrets: any[] = [];
|
||||||
if (Array.isArray(secretIds) && secretIds.length > 0) {
|
if (Array.isArray(secretIds) && secretIds.length > 0) {
|
||||||
secrets = await pb.collection("documents").getFullList({
|
secrets = await pb.collection("documents").getFullList({
|
||||||
filter: secretIds.map(id => `id = "${id}"`).join(" || "),
|
filter: secretIds.map((id) => `id = "${id}"`).join(" || "),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { document: doc, secrets };
|
return { document: doc, secrets };
|
||||||
@@ -27,19 +27,19 @@ export const Route = createFileRoute(
|
|||||||
});
|
});
|
||||||
|
|
||||||
function RouteComponent() {
|
function RouteComponent() {
|
||||||
const { document, secrets } = Route.useLoaderData();
|
const { document: session, secrets } = Route.useLoaderData();
|
||||||
const strongStart = document?.data?.session?.strongStart || "";
|
const strongStart = session?.data?.session?.strongStart || "";
|
||||||
const [newSecret, setNewSecret] = useState("");
|
const [newSecret, setNewSecret] = useState("");
|
||||||
const [adding, setAdding] = useState(false);
|
const [adding, setAdding] = useState(false);
|
||||||
const [secretList, setSecretList] = useState(secrets);
|
const [secretList, setSecretList] = useState(secrets);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
async function handleSaveStrongStart(newValue: string) {
|
async function handleSaveStrongStart(newValue: string) {
|
||||||
await pb.collection("documents").update(document.id, {
|
await pb.collection("documents").update(session.id, {
|
||||||
data: {
|
data: {
|
||||||
...document.data,
|
...session.data,
|
||||||
session: {
|
session: {
|
||||||
...document.data.session,
|
...session.data.session,
|
||||||
strongStart: newValue,
|
strongStart: newValue,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -53,7 +53,7 @@ function RouteComponent() {
|
|||||||
try {
|
try {
|
||||||
// 1. Create the secret document
|
// 1. Create the secret document
|
||||||
const secretDoc = await pb.collection("documents").create({
|
const secretDoc = await pb.collection("documents").create({
|
||||||
campaign: document.campaign, // assuming campaign is an id or object
|
campaign: session.campaign, // assuming campaign is an id or object
|
||||||
data: {
|
data: {
|
||||||
secret: {
|
secret: {
|
||||||
text: newSecret,
|
text: newSecret,
|
||||||
@@ -62,8 +62,8 @@ function RouteComponent() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
// 2. Check for existing relationship
|
// 2. Check for existing relationship
|
||||||
const existing = await pb.collection("relationships").getOne({
|
const existing = await pb.collection("relationships").getFullList({
|
||||||
filter: `primary = "${document.id}" && type = "plannedSecrets"`,
|
filter: `primary = "${session.id}" && type = "plannedSecrets"`,
|
||||||
});
|
});
|
||||||
if (existing.length > 0) {
|
if (existing.length > 0) {
|
||||||
// Update existing relationship to add new secret to secondary array
|
// Update existing relationship to add new secret to secondary array
|
||||||
@@ -73,7 +73,7 @@ function RouteComponent() {
|
|||||||
} else {
|
} else {
|
||||||
// Create new relationship
|
// Create new relationship
|
||||||
await pb.collection("relationships").create({
|
await pb.collection("relationships").create({
|
||||||
primary: document.id,
|
primary: session.id,
|
||||||
secondary: [secretDoc.id],
|
secondary: [secretDoc.id],
|
||||||
type: "plannedSecrets",
|
type: "plannedSecrets",
|
||||||
});
|
});
|
||||||
@@ -87,37 +87,106 @@ function RouteComponent() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleToggleDiscovered(secret: Secret, checked: boolean) {
|
||||||
|
// 1. Update the discovered field in the secret document
|
||||||
|
await pb.collection("documents").update(secret.id, {
|
||||||
|
data: {
|
||||||
|
...secret.data,
|
||||||
|
secret: {
|
||||||
|
text: secret.data.secret.text,
|
||||||
|
discovered: checked,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// 2. 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);
|
||||||
|
}
|
||||||
|
// 3. If marking as discovered, add a new discoveredIn relationship
|
||||||
|
if (checked) {
|
||||||
|
await pb.collection("relationships").create({
|
||||||
|
primary: secret.id,
|
||||||
|
secondary: [session.id],
|
||||||
|
type: "discoveredIn",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 4. Update local state
|
||||||
|
setSecretList(
|
||||||
|
secretList.map((s: any) =>
|
||||||
|
s.id === secret.id
|
||||||
|
? {
|
||||||
|
...s,
|
||||||
|
data: {
|
||||||
|
...s.data,
|
||||||
|
secret: { ...s.data.secret, discovered: checked },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: s,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-xl mx-auto py-8">
|
<div className="max-w-xl mx-auto py-8">
|
||||||
<h2 className="text-2xl font-bold mb-4 text-slate-100">Session Strong Start</h2>
|
<h2 className="text-2xl font-bold mb-4 text-slate-100">
|
||||||
|
Session Strong Start
|
||||||
|
</h2>
|
||||||
<AutoSaveTextarea
|
<AutoSaveTextarea
|
||||||
value={strongStart}
|
value={strongStart}
|
||||||
onSave={handleSaveStrongStart}
|
onSave={handleSaveStrongStart}
|
||||||
placeholder="Enter a strong start for this session..."
|
placeholder="Enter a strong start for this session..."
|
||||||
aria-label="Strong Start"
|
aria-label="Strong Start"
|
||||||
/>
|
/>
|
||||||
<h3 className="text-lg font-semibold mt-8 mb-2 text-slate-200">Planned Secrets</h3>
|
<h3 className="text-lg font-semibold mt-8 mb-2 text-slate-200">
|
||||||
|
Planned Secrets
|
||||||
|
</h3>
|
||||||
{secretList && secretList.length > 0 ? (
|
{secretList && secretList.length > 0 ? (
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{secretList.map((secret: any) => (
|
{secretList.map((secret: any) => (
|
||||||
<li key={secret.id} className="bg-slate-800 rounded p-4 text-slate-100">
|
<li
|
||||||
{secret.data?.secret?.text || <span className="italic text-slate-400">(No secret text)</span>}
|
key={secret.id}
|
||||||
|
className="bg-slate-800 rounded p-4 text-slate-100 flex items-center gap-3"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={!!secret.data?.secret?.discovered}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleToggleDiscovered(secret, e.target.checked)
|
||||||
|
}
|
||||||
|
className="accent-emerald-500 w-5 h-5"
|
||||||
|
aria-label="Discovered"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
{secret.data?.secret?.text || (
|
||||||
|
<span className="italic text-slate-400">
|
||||||
|
(No secret text)
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-slate-400">No planned secrets for this session.</div>
|
<div className="text-slate-400">
|
||||||
|
No planned secrets for this session.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
<form
|
<form
|
||||||
className="flex items-center gap-2 mt-4"
|
className="flex items-center gap-2 mt-4"
|
||||||
onSubmit={e => { e.preventDefault(); handleAddSecret(); }}
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleAddSecret();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="flex-1 px-3 py-2 rounded bg-slate-800 text-slate-100 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500"
|
className="flex-1 px-3 py-2 rounded bg-slate-800 text-slate-100 border border-slate-700 focus:outline-none focus:ring-2 focus:ring-violet-500"
|
||||||
placeholder="Add a new secret..."
|
placeholder="Add a new secret..."
|
||||||
value={newSecret}
|
value={newSecret}
|
||||||
onChange={e => setNewSecret(e.target.value)}
|
onChange={(e) => setNewSecret(e.target.value)}
|
||||||
disabled={adding}
|
disabled={adding}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
|||||||
Reference in New Issue
Block a user