200 lines
4.2 KiB
TypeScript
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;
|