Switches over to the relationship list
This commit is contained in:
116
src/components/RelationshipList.tsx
Normal file
116
src/components/RelationshipList.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { pb } from "@/lib/pocketbase";
|
||||
import type { Document } from "@/lib/types";
|
||||
import { DocumentList } from "@/components/DocumentList";
|
||||
import { Loader } from "./Loader";
|
||||
|
||||
interface RelationshipListProps<T extends Document> {
|
||||
root: Document;
|
||||
relationshipType: string;
|
||||
renderRow: (item: T) => React.ReactNode;
|
||||
newItemForm: (onCreate: (doc: T) => Promise<void>) => React.ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T extends Document>({
|
||||
root,
|
||||
relationshipType,
|
||||
renderRow,
|
||||
newItemForm,
|
||||
}: RelationshipListProps<T>) {
|
||||
const [items, setItems] = useState<T[]>([]);
|
||||
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: T[] = [];
|
||||
if (Array.isArray(secondaryIds) && secondaryIds.length > 0) {
|
||||
docs = (await pb.collection("documents").getFullList({
|
||||
filter: secondaryIds
|
||||
.map((id: string) => `id = "${id}"`)
|
||||
.join(" || "),
|
||||
})) as T[];
|
||||
}
|
||||
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: T) => {
|
||||
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={renderRow}
|
||||
newItemForm={(onSubmit) =>
|
||||
newItemForm(async (doc: T) => {
|
||||
await handleCreate(doc);
|
||||
onSubmit();
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user