origin/apps/web/src/components/models/term/term-tree.tsx

200 lines
4.2 KiB
TypeScript

import React, { useEffect, useState, useCallback } from "react";
import { Tree } from "antd";
import type { DataNode, TreeProps } from "antd/es/tree";
import { getUniqueItems } from "@nice/common";
import { api } from "@nice/client";
interface TermData {
value?: string;
children?: TermData[];
key?: string;
hasChildren?: boolean;
isLeaf?: boolean;
pId?: string;
title?: React.ReactNode;
data?: any;
order?: string;
id?: string;
}
interface TermTreeProps {
defaultValue?: string | string[];
value?: string | string[];
onChange?: (value: string | string[]) => void;
multiple?: boolean;
taxonomyId?: string;
disabled?: boolean;
className?: string;
domainId?: string;
style?: React.CSSProperties;
}
const TermTree: React.FC<TermTreeProps> = ({
defaultValue,
value,
onChange,
className,
multiple = false,
taxonomyId,
domainId,
disabled = false,
style,
}) => {
const utils = api.useUtils();
const [treeData, setTreeData] = useState<TermData[]>([]);
const processTermData = (terms: TermData[]): TermData[] => {
return terms.map((term) => ({
...term,
key: term.key || term.id || "",
title: term.title || term.value,
children: term.children
? processTermData(term.children)
: undefined,
}));
};
const fetchParentTerms = useCallback(
async (termIds: string | string[], taxonomyId?: string) => {
const idsArray = Array.isArray(termIds)
? termIds
: [termIds].filter(Boolean);
try {
const result = await utils.term.getParentSimpleTree.fetch({
termIds: idsArray,
taxonomyId,
domainId,
});
return processTermData(result);
} catch (error) {
console.error(
"Error fetching parent terms for termIds",
idsArray,
":",
error
);
throw error;
}
},
[utils, domainId]
);
const fetchTerms = useCallback(async () => {
try {
const rootTerms = await utils.term.getChildSimpleTree.fetch({
taxonomyId,
domainId,
});
let combinedTerms = processTermData(rootTerms);
if (defaultValue) {
const defaultTerms = await fetchParentTerms(
defaultValue,
taxonomyId
);
combinedTerms = getUniqueItems(
[...treeData, ...combinedTerms, ...defaultTerms],
"key"
);
}
if (value) {
const valueTerms = await fetchParentTerms(value, taxonomyId);
combinedTerms = getUniqueItems(
[...treeData, ...combinedTerms, ...valueTerms],
"key"
);
}
setTreeData(combinedTerms);
} catch (error) {
console.error("Error fetching terms:", error);
}
}, [
defaultValue,
value,
taxonomyId,
utils,
fetchParentTerms,
domainId,
treeData,
]);
useEffect(() => {
fetchTerms();
}, [fetchTerms]);
const onLoadData = async ({ key }: any) => {
try {
const result = await utils.term.getChildSimpleTree.fetch({
termIds: [key],
taxonomyId,
domainId,
});
const processedResult = processTermData(result);
const newItems = getUniqueItems(
[...treeData, ...processedResult],
"key"
);
setTreeData(newItems);
} catch (error) {
console.error(
"Error loading data for node with key",
key,
":",
error
);
}
};
const handleCheck: TreeProps["onCheck"] = (checkedKeys, info) => {
if (onChange) {
if (multiple) {
onChange(checkedKeys as string[]);
} else {
onChange((checkedKeys as string[])[0] || "");
}
}
};
const handleExpand = async (expandedKeys: React.Key[]) => {
try {
const allKeyIds = expandedKeys
.map((key) => key.toString())
.filter(Boolean);
const expandedNodes = await utils.term.getChildSimpleTree.fetch({
termIds: allKeyIds,
taxonomyId,
domainId,
});
const processedNodes = processTermData(expandedNodes);
const newItems = getUniqueItems(
[...treeData, ...processedNodes],
"key"
);
setTreeData(newItems);
} catch (error) {
console.error(
"Error expanding nodes with keys",
expandedKeys,
":",
error
);
}
};
return (
<Tree
checkable={multiple}
disabled={disabled}
className={className}
style={style}
treeData={treeData as DataNode[]}
checkedKeys={Array.isArray(value) ? value : value ? [value] : []}
onCheck={handleCheck}
loadData={onLoadData}
onExpand={handleExpand}
/>
);
};
export default TermTree;