Files
iistwin/client/src/components/TableFieldWithSubdirectory.tsx

138 lines
4.1 KiB
TypeScript

import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { apiRequest } from '@/lib/queryClient';
import TableFieldSelect from '@/components/TableFieldSelect';
import TableFieldMultiSelect from '@/components/TableFieldMultiSelect';
import type { DataTableSubdirectory } from '@shared/schema';
interface TableFieldValue {
parentId?: string | null;
childId?: string | null;
}
interface TableFieldWithSubdirectoryProps {
fieldId: number;
fieldCode: string;
placeholder?: string;
options?: unknown;
value: any;
onChange: (value: string) => void;
allowMultiple?: boolean;
}
function parseTableValue(value: any): TableFieldValue {
if (value == null || value === '') return { parentId: null, childId: null };
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value);
if (parsed && typeof parsed === 'object') {
return {
parentId: parsed.parentId != null ? String(parsed.parentId) : null,
childId: parsed.childId != null ? String(parsed.childId) : null,
};
}
} catch {
// fallback: plain string is parentId
}
return { parentId: value, childId: null };
}
if (typeof value === 'object') {
return {
parentId: value.parentId != null ? String(value.parentId) : null,
childId: value.childId != null ? String(value.childId) : null,
};
}
return { parentId: String(value), childId: null };
}
function serializeTableValue(parentId: string | null | undefined, childId: string | null | undefined): string {
const p = parentId || null;
const c = childId || null;
if (!p) return '';
if (!c) return p;
return JSON.stringify({ parentId: p, childId: c });
}
export default function TableFieldWithSubdirectory({
fieldId,
fieldCode,
placeholder,
options,
value,
onChange,
allowMultiple,
}: TableFieldWithSubdirectoryProps) {
const opts = options && !Array.isArray(options) ? (options as Record<string, any>) : {};
const tableId = opts.tableId as number | undefined;
const showSubdirectory = !!opts.showSubdirectory;
const { data: subdirectoriesData } = useQuery<{ subdirectories: DataTableSubdirectory[] }>({
queryKey: ['/api/directories', tableId, 'subdirectories'],
queryFn: async () => {
const res = await apiRequest('GET', `/api/directories/${tableId}/subdirectories`);
return res.json();
},
enabled: !!tableId && showSubdirectory,
staleTime: 60 * 1000,
});
const { parentId, childId } = parseTableValue(value);
const subdirectory = useMemo(() => {
if (!parentId) return undefined;
return subdirectoriesData?.subdirectories?.find((s) =>
s.parentRowIds?.includes(Number(parentId))
);
}, [subdirectoriesData, parentId]);
const handleParentChange = (newParentId: string) => {
onChange(serializeTableValue(newParentId || null, null));
};
const handleChildChange = (newChildId: string) => {
onChange(serializeTableValue(parentId, newChildId || null));
};
if (!showSubdirectory || allowMultiple) {
const SelectComponent = allowMultiple ? TableFieldMultiSelect : TableFieldSelect;
return (
<SelectComponent
fieldId={fieldId}
fieldCode={fieldCode}
placeholder={placeholder}
options={options}
value={parentId || ''}
onChange={onChange as any}
/>
);
}
const parentOptions = opts;
const childOptions = subdirectory
? { ...opts, tableId: subdirectory.childTableId }
: opts;
return (
<div className="space-y-2">
<TableFieldSelect
fieldId={fieldId}
fieldCode={fieldCode}
placeholder={placeholder || 'Выберите значение...'}
options={parentOptions}
value={parentId || ''}
onChange={handleParentChange}
/>
{subdirectory && (
<TableFieldSelect
fieldId={fieldId}
fieldCode={`${fieldCode}_sub`}
placeholder={subdirectory.childTable?.name || 'Выберите подсправочник...'}
options={childOptions}
value={childId || ''}
onChange={handleChildChange}
/>
)}
</div>
);
}