Makes a generic document form
This commit is contained in:
@@ -1,26 +1,25 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
import type { Document } from "@/lib/types";
|
import type { Document, RelationshipType } from "@/lib/types";
|
||||||
import { DocumentList } from "@/components/DocumentList";
|
import { DocumentList } from "@/components/DocumentList";
|
||||||
import { Loader } from "./Loader";
|
import { Loader } from "./Loader";
|
||||||
import { DocumentRow } from "./documents/DocumentRow";
|
import { DocumentRow } from "./documents/DocumentRow";
|
||||||
|
import { DocumentForm } from "./documents/DocumentForm";
|
||||||
|
|
||||||
interface RelationshipListProps<T extends Document> {
|
interface RelationshipListProps {
|
||||||
root: Document;
|
root: Document;
|
||||||
relationshipType: string;
|
relationshipType: RelationshipType;
|
||||||
newItemForm: (onCreate: (doc: T) => Promise<void>) => React.ReactNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RelationshipList manages a list of documents related to a root document via a relationship type.
|
* RelationshipList manages a list of documents related to a root document via a relationship type.
|
||||||
* It handles fetching, creation, and relationship management, and renders a DocumentList.
|
* It handles fetching, creation, and relationship management, and renders a DocumentList.
|
||||||
*/
|
*/
|
||||||
export function RelationshipList<T extends Document>({
|
export function RelationshipList({
|
||||||
root,
|
root,
|
||||||
relationshipType,
|
relationshipType,
|
||||||
newItemForm,
|
}: RelationshipListProps) {
|
||||||
}: RelationshipListProps<T>) {
|
const [items, setItems] = useState<Document[]>([]);
|
||||||
const [items, setItems] = useState<T[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -40,13 +39,13 @@ export function RelationshipList<T extends Document>({
|
|||||||
relationships.items.length > 0
|
relationships.items.length > 0
|
||||||
? relationships.items[0].secondary
|
? relationships.items[0].secondary
|
||||||
: [];
|
: [];
|
||||||
let docs: T[] = [];
|
let docs: Document[] = [];
|
||||||
if (Array.isArray(secondaryIds) && secondaryIds.length > 0) {
|
if (Array.isArray(secondaryIds) && secondaryIds.length > 0) {
|
||||||
docs = (await pb.collection("documents").getFullList({
|
docs = (await pb.collection("documents").getFullList({
|
||||||
filter: secondaryIds
|
filter: secondaryIds
|
||||||
.map((id: string) => `id = "${id}"`)
|
.map((id: string) => `id = "${id}"`)
|
||||||
.join(" || "),
|
.join(" || "),
|
||||||
})) as T[];
|
})) as Document[];
|
||||||
}
|
}
|
||||||
if (!cancelled) setItems(docs);
|
if (!cancelled) setItems(docs);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -63,7 +62,7 @@ export function RelationshipList<T extends Document>({
|
|||||||
}, [root.id, relationshipType]);
|
}, [root.id, relationshipType]);
|
||||||
|
|
||||||
// Handles creation of a new document and adds it to the relationship
|
// Handles creation of a new document and adds it to the relationship
|
||||||
const handleCreate = async (doc: T) => {
|
const handleCreate = async (doc: Document) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setError(null);
|
setError(null);
|
||||||
try {
|
try {
|
||||||
@@ -104,12 +103,16 @@ export function RelationshipList<T extends Document>({
|
|||||||
items={items}
|
items={items}
|
||||||
error={error}
|
error={error}
|
||||||
renderRow={(document) => <DocumentRow document={document} />}
|
renderRow={(document) => <DocumentRow document={document} />}
|
||||||
newItemForm={(onSubmit) =>
|
newItemForm={(onSubmit) => (
|
||||||
newItemForm(async (doc: T) => {
|
<DocumentForm
|
||||||
await handleCreate(doc);
|
campaignId={root.campaign}
|
||||||
onSubmit();
|
relationshipType={relationshipType}
|
||||||
})
|
onCreate={async (doc: Document) => {
|
||||||
}
|
await handleCreate(doc);
|
||||||
|
onSubmit();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/components/documents/DocumentForm.tsx
Normal file
28
src/components/documents/DocumentForm.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { RelationshipType, type CampaignId, type Document } from "@/lib/types";
|
||||||
|
import { SecretForm } from "./secret/SecretForm";
|
||||||
|
|
||||||
|
function assertUnreachable(_x: never): never {
|
||||||
|
throw new Error("DocumentForm switch is not exhaustive");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a form for any document type depending on the relationship.
|
||||||
|
*/
|
||||||
|
export const DocumentForm = ({
|
||||||
|
campaignId,
|
||||||
|
relationshipType,
|
||||||
|
onCreate,
|
||||||
|
}: {
|
||||||
|
campaignId: CampaignId;
|
||||||
|
relationshipType: RelationshipType;
|
||||||
|
onCreate: (document: Document) => Promise<void>;
|
||||||
|
}) => {
|
||||||
|
switch (relationshipType) {
|
||||||
|
case RelationshipType.Secrets:
|
||||||
|
return <SecretForm campaign={campaignId} onCreate={onCreate} />;
|
||||||
|
case RelationshipType.DiscoveredIn:
|
||||||
|
return "Form not supported here";
|
||||||
|
}
|
||||||
|
|
||||||
|
return assertUnreachable(relationshipType);
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// SecretForm.tsx
|
// SecretForm.tsx
|
||||||
// Form for adding a new secret to a session.
|
// Form for adding a new secret to a session.
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import type { Secret } from "@/lib/types";
|
import type { CampaignId, Secret } from "@/lib/types";
|
||||||
import { pb } from "@/lib/pocketbase";
|
import { pb } from "@/lib/pocketbase";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -11,7 +11,7 @@ export const SecretForm = ({
|
|||||||
campaign,
|
campaign,
|
||||||
onCreate,
|
onCreate,
|
||||||
}: {
|
}: {
|
||||||
campaign: string;
|
campaign: CampaignId;
|
||||||
onCreate: (secret: Secret) => Promise<void>;
|
onCreate: (secret: Secret) => Promise<void>;
|
||||||
}) => {
|
}) => {
|
||||||
const [newSecret, setNewSecret] = useState("");
|
const [newSecret, setNewSecret] = useState("");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export type Campaign = RecordModel & {
|
|||||||
|
|
||||||
export type Document = RecordModel & {
|
export type Document = RecordModel & {
|
||||||
id: DocumentId;
|
id: DocumentId;
|
||||||
campaign: Campaign;
|
campaign: CampaignId;
|
||||||
data: {};
|
data: {};
|
||||||
// These two are not in Pocketbase's types, but they seem to always be present
|
// These two are not in Pocketbase's types, but they seem to always be present
|
||||||
created: string;
|
created: string;
|
||||||
@@ -57,8 +57,11 @@ export const RelationshipType = {
|
|||||||
DiscoveredIn: "discoveredIn",
|
DiscoveredIn: "discoveredIn",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
export type RelationshipType =
|
||||||
|
(typeof RelationshipType)[keyof typeof RelationshipType];
|
||||||
|
|
||||||
export type Relationship = RecordModel & {
|
export type Relationship = RecordModel & {
|
||||||
primary: DocumentId;
|
primary: DocumentId;
|
||||||
secondary: DocumentId[];
|
secondary: DocumentId[];
|
||||||
type: (typeof RelationshipType)[keyof typeof RelationshipType];
|
type: RelationshipType;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { pb } from "@/lib/pocketbase";
|
|||||||
import { RelationshipType, type Session } from "@/lib/types";
|
import { RelationshipType, type Session } from "@/lib/types";
|
||||||
import { RelationshipList } from "@/components/RelationshipList";
|
import { RelationshipList } from "@/components/RelationshipList";
|
||||||
import { SessionForm } from "@/components/documents/session/SessionForm";
|
import { SessionForm } from "@/components/documents/session/SessionForm";
|
||||||
import { SecretForm } from "@/components/documents/secret/SecretForm";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authenticated/document/$documentId")({
|
export const Route = createFileRoute("/_authenticated/document/$documentId")({
|
||||||
loader: async ({ params }) => {
|
loader: async ({ params }) => {
|
||||||
@@ -28,9 +27,6 @@ function RouteComponent() {
|
|||||||
<RelationshipList
|
<RelationshipList
|
||||||
root={session}
|
root={session}
|
||||||
relationshipType={RelationshipType.Secrets}
|
relationshipType={RelationshipType.Secrets}
|
||||||
newItemForm={(onCreate) => (
|
|
||||||
<SecretForm campaign={session.campaign.id} onCreate={onCreate} />
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user