119 lines
3.6 KiB
TypeScript
119 lines
3.6 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { pb } from "@/lib/pocketbase";
|
|
import type { Document, RelationshipType } from "@/lib/types";
|
|
import { DocumentList } from "@/components/DocumentList";
|
|
import { Loader } from "./Loader";
|
|
import { DocumentRow } from "./documents/DocumentRow";
|
|
import { DocumentForm } from "./documents/DocumentForm";
|
|
|
|
interface RelationshipListProps {
|
|
root: Document;
|
|
relationshipType: RelationshipType;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
export function RelationshipList({
|
|
root,
|
|
relationshipType,
|
|
}: RelationshipListProps) {
|
|
const [items, setItems] = useState<Document[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// Fetch related documents on mount or when root/relationshipType changes
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
async function fetchRelated() {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const relationships = await pb
|
|
.collection("relationships")
|
|
.getList(1, 1, {
|
|
filter: `primary = "${root.id}" && type = "${relationshipType}"`,
|
|
});
|
|
const secondaryIds =
|
|
relationships.items.length > 0
|
|
? relationships.items[0].secondary
|
|
: [];
|
|
let docs: Document[] = [];
|
|
if (Array.isArray(secondaryIds) && secondaryIds.length > 0) {
|
|
docs = (await pb.collection("documents").getFullList({
|
|
filter: secondaryIds
|
|
.map((id: string) => `id = "${id}"`)
|
|
.join(" || "),
|
|
})) as Document[];
|
|
}
|
|
if (!cancelled) setItems(docs);
|
|
} catch (e: any) {
|
|
if (!cancelled)
|
|
setError(e?.message || "Failed to load related documents.");
|
|
} finally {
|
|
if (!cancelled) setLoading(false);
|
|
}
|
|
}
|
|
fetchRelated();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [root.id, relationshipType]);
|
|
|
|
// Handles creation of a new document and adds it to the relationship
|
|
const handleCreate = async (doc: Document) => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
// Check for existing relationship
|
|
const existing = await pb.collection("relationships").getFullList({
|
|
filter: `primary = "${root.id}" && type = "${relationshipType}"`,
|
|
});
|
|
if (existing.length > 0) {
|
|
console.debug("Adding to existing relationship");
|
|
await pb.collection("relationships").update(existing[0].id, {
|
|
"+secondary": doc.id,
|
|
});
|
|
} else {
|
|
console.debug("Creating new relationship");
|
|
await pb.collection("relationships").create({
|
|
primary: root.id,
|
|
secondary: [doc.id],
|
|
type: relationshipType,
|
|
});
|
|
}
|
|
setItems((prev) => [...prev, doc]);
|
|
} catch (e: any) {
|
|
setError(e?.message || "Failed to add document to relationship.");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
<Loader />;
|
|
}
|
|
|
|
return (
|
|
<DocumentList
|
|
title={
|
|
relationshipType.charAt(0).toUpperCase() + relationshipType.slice(1)
|
|
}
|
|
items={items}
|
|
error={error}
|
|
renderRow={(document) => <DocumentRow document={document} />}
|
|
newItemForm={(onSubmit) => (
|
|
<DocumentForm
|
|
campaignId={root.campaign}
|
|
relationshipType={relationshipType}
|
|
onCreate={async (doc: Document) => {
|
|
await handleCreate(doc);
|
|
onSubmit();
|
|
}}
|
|
/>
|
|
)}
|
|
/>
|
|
);
|
|
}
|