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

213 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useMemo } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Table, Link2 } from 'lucide-react';
import { apiRequest } from '@/lib/queryClient';
import type { DataTableSubdirectory } from '@shared/schema';
interface FormFieldTable {
id?: number;
type: string;
allowMultiple?: boolean;
options?: Record<string, any>;
}
interface SubdirectoryFieldConfigProps {
field: FormFieldTable;
index: number;
tablesData?: { tables: Array<{ id: number; name: string; columns: any[] }> };
updateField: (index: number, updates: Partial<FormFieldTable>) => void;
}
export default function SubdirectoryFieldConfig({
field,
index,
tablesData,
updateField,
}: SubdirectoryFieldConfigProps) {
const tableId = field.options?.tableId as number | undefined;
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,
staleTime: 60 * 1000,
});
const subdirectories = subdirectoriesData?.subdirectories || [];
const showSubdirectory = !!field.options?.showSubdirectory;
const selectedSubdirectoryId = field.options?.subdirectoryId as number | undefined;
const selectedSubdirectory = useMemo(() => {
return subdirectories.find((s) => s.id === selectedSubdirectoryId || s.childTableId === selectedSubdirectoryId);
}, [subdirectories, selectedSubdirectoryId]);
const selectedTable = useMemo(() => {
return tablesData?.tables?.find((t) => t.id === tableId);
}, [tablesData, tableId]);
const handleTableChange = (value: string) => {
if (value === 'none') {
updateField(index, {
options: {
...field.options,
tableId: undefined,
displayColumn: undefined,
showSubdirectory: undefined,
subdirectoryId: undefined,
},
});
return;
}
const newTableId = parseInt(value);
const newSelectedTable = tablesData?.tables?.find((t) => t.id === newTableId);
updateField(index, {
options: {
...field.options,
tableId: newTableId,
displayColumn: newSelectedTable?.columns?.[0]?.name || undefined,
showSubdirectory: undefined,
subdirectoryId: undefined,
},
});
};
const handleShowSubdirectoryChange = (checked: boolean) => {
updateField(index, {
options: {
...field.options,
showSubdirectory: checked,
subdirectoryId: checked ? field.options?.subdirectoryId : undefined,
},
});
};
const handleSubdirectoryChange = (value: string) => {
const newSubdirectory = value === 'none' ? undefined : subdirectories.find((s) => s.id?.toString() === value || s.childTableId?.toString() === value);
updateField(index, {
options: {
...field.options,
subdirectoryId: newSubdirectory?.id,
childTableId: newSubdirectory?.childTableId,
},
});
};
return (
<div className="mt-3 space-y-3">
<div className="flex items-center space-x-2">
<Checkbox
checked={field.allowMultiple || false}
onCheckedChange={(checked) => updateField(index, { allowMultiple: !!checked })}
data-testid={`checkbox-field-table-allow-multiple-${index}`}
/>
<label className="text-sm font-medium">Разрешить множественный выбор</label>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium">Таблица</label>
<Select value={tableId?.toString() || 'none'} onValueChange={handleTableChange}>
<SelectTrigger data-testid={`select-field-table-${index}`}>
<SelectValue placeholder="Выберите таблицу" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">Не выбрано</SelectItem>
{tablesData?.tables?.map((table) => (
<SelectItem key={table.id} value={table.id.toString()}>
<div className="flex items-center gap-2">
<Table className="w-4 h-4" />
{table.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{tableId && (
<div>
<label className="text-sm font-medium">Отображаемый столбец</label>
<Select
value={field.options?.displayColumn || 'none'}
onValueChange={(value) =>
updateField(index, {
options: { ...field.options, displayColumn: value === 'none' ? undefined : value },
})
}
>
<SelectTrigger data-testid={`select-field-column-${index}`}>
<SelectValue placeholder="Выберите столбец" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">Не выбрано</SelectItem>
{selectedTable?.columns?.map((col: any, idx: number) => (
<SelectItem key={idx} value={col.name || `col-${idx}`}>
{col.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
)}
</div>
{tableId && (
<div className="border rounded-md p-3 space-y-3 bg-muted/20">
<div className="flex items-center space-x-2">
<Checkbox
checked={showSubdirectory}
onCheckedChange={handleShowSubdirectoryChange}
data-testid={`checkbox-field-show-subdirectory-${index}`}
/>
<label className="text-sm font-medium flex items-center gap-1.5">
<Link2 className="w-3.5 h-3.5" />
Отображать подсправочник
</label>
</div>
{showSubdirectory && (
<div>
<Label className="text-xs">Подсправочник</Label>
<Select value={selectedSubdirectoryId?.toString() || 'none'} onValueChange={handleSubdirectoryChange}>
<SelectTrigger>
<SelectValue placeholder="Выберите подсправочник" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">Не выбрано</SelectItem>
{subdirectories.length === 0 && (
<SelectItem value="no-subdirectories" disabled>
Нет привязанных подсправочников
</SelectItem>
)}
{subdirectories.map((sub) => (
<SelectItem key={sub.id} value={sub.id?.toString() || sub.childTableId?.toString() || ''}>
{sub.childTable?.name || `Справочник ${sub.childTableId}`}
</SelectItem>
))}
</SelectContent>
</Select>
{selectedSubdirectory && (
<p className="text-xs text-muted-foreground mt-1">
Подсправочник показывается при выборе привязанных строк родителя.
</p>
)}
</div>
)}
</div>
)}
</div>
);
}