diff --git a/apps/web/src/components/EntitySelect.tsx b/apps/web/src/components/EntitySelect.tsx new file mode 100644 index 0000000..c485f35 --- /dev/null +++ b/apps/web/src/components/EntitySelect.tsx @@ -0,0 +1,80 @@ +import { Select, Spin } from 'antd'; +import { useState, useEffect, useCallback } from 'react'; +import { listPluginData } from '../api/pluginData'; + +interface EntitySelectProps { + pluginId: string; + entity: string; + labelField: string; + searchFields?: string[]; + value?: string; + onChange?: (value: string, label: string) => void; + cascadeFrom?: string; + cascadeFilter?: string; + cascadeValue?: string; + placeholder?: string; +} + +export default function EntitySelect({ + pluginId, + entity, + labelField, + searchFields, + value, + onChange, + cascadeFrom, + cascadeFilter, + cascadeValue, + placeholder, +}: EntitySelectProps) { + const [options, setOptions] = useState<{ value: string; label: string }[]>([]); + const [loading, setLoading] = useState(false); + + const fetchData = useCallback( + async (keyword?: string) => { + setLoading(true); + try { + const filter: Record | undefined = + cascadeFrom && cascadeFilter && cascadeValue + ? { [cascadeFilter]: cascadeValue } + : undefined; + + const result = await listPluginData(pluginId, entity, 1, 20, { + search: keyword, + filter, + }); + + const items = (result.data || []).map((item) => ({ + value: item.id, + label: String(item.data?.[labelField] ?? item.id), + })); + setOptions(items); + } finally { + setLoading(false); + } + }, + [pluginId, entity, labelField, cascadeFrom, cascadeFilter, cascadeValue], + ); + + useEffect(() => { + fetchData(); + }, [fetchData]); + + return ( +