feat: подсправочник показывается целиком при выборе привязанной строки
This commit is contained in:
@@ -200,7 +200,7 @@ export default function SubdirectoryFieldConfig({
|
|||||||
</Select>
|
</Select>
|
||||||
{selectedSubdirectory && (
|
{selectedSubdirectory && (
|
||||||
<p className="text-xs text-muted-foreground mt-1">
|
<p className="text-xs text-muted-foreground mt-1">
|
||||||
Фильтр: «{selectedTable?.columns?.[selectedSubdirectory.parentColumnIndex ?? 0]?.name || '?'}» = «{selectedSubdirectory.childTable?.columns?.[selectedSubdirectory.childColumnIndex ?? 0]?.name || '?'}»
|
Подсправочник показывается при выборе привязанных строк родителя.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { apiRequest } from '@/lib/queryClient';
|
import { apiRequest } from '@/lib/queryClient';
|
||||||
import TableFieldSelect from '@/components/TableFieldSelect';
|
import TableFieldSelect from '@/components/TableFieldSelect';
|
||||||
import TableFieldMultiSelect from '@/components/TableFieldMultiSelect';
|
import TableFieldMultiSelect from '@/components/TableFieldMultiSelect';
|
||||||
import type { DataTableRow, DataTableSubdirectory } from '@shared/schema';
|
import type { DataTableSubdirectory } from '@shared/schema';
|
||||||
|
|
||||||
interface TableFieldValue {
|
interface TableFieldValue {
|
||||||
parentId?: string | null;
|
parentId?: string | null;
|
||||||
@@ -67,23 +67,6 @@ export default function TableFieldWithSubdirectory({
|
|||||||
const showSubdirectory = !!opts.showSubdirectory;
|
const showSubdirectory = !!opts.showSubdirectory;
|
||||||
const subdirectoryId = opts.subdirectoryId as number | undefined;
|
const subdirectoryId = opts.subdirectoryId as number | undefined;
|
||||||
|
|
||||||
const { data: tableData } = useQuery<{
|
|
||||||
table: {
|
|
||||||
rows: DataTableRow[];
|
|
||||||
columns: Array<{ name: string }>;
|
|
||||||
treeColumnIndex: number | null;
|
|
||||||
treeDisplayColumnIndex: number | null;
|
|
||||||
treeColumns: number[] | null;
|
|
||||||
treeDisplayColumns: number[] | null;
|
|
||||||
};
|
|
||||||
}>({
|
|
||||||
queryKey: ['/api/directories', tableId],
|
|
||||||
enabled: !!tableId,
|
|
||||||
staleTime: 5 * 60 * 1000,
|
|
||||||
gcTime: 10 * 60 * 1000,
|
|
||||||
retry: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: subdirectoriesData } = useQuery<{ subdirectories: DataTableSubdirectory[] }>({
|
const { data: subdirectoriesData } = useQuery<{ subdirectories: DataTableSubdirectory[] }>({
|
||||||
queryKey: ['/api/directories', tableId, 'subdirectories'],
|
queryKey: ['/api/directories', tableId, 'subdirectories'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
@@ -100,24 +83,11 @@ export default function TableFieldWithSubdirectory({
|
|||||||
|
|
||||||
const { parentId, childId } = parseTableValue(value);
|
const { parentId, childId } = parseTableValue(value);
|
||||||
|
|
||||||
const parentRow = useMemo(() => {
|
|
||||||
if (!parentId || !tableData?.table?.rows) return null;
|
|
||||||
return tableData.table.rows.find((r) => r.id.toString() === parentId) || null;
|
|
||||||
}, [parentId, tableData]);
|
|
||||||
|
|
||||||
const isParentLinked = useMemo(() => {
|
const isParentLinked = useMemo(() => {
|
||||||
if (!subdirectory || !parentId) return false;
|
if (!subdirectory || !parentId) return false;
|
||||||
return subdirectory.parentRowIds?.includes(Number(parentId)) ?? false;
|
return subdirectory.parentRowIds?.includes(Number(parentId)) ?? false;
|
||||||
}, [subdirectory, parentId]);
|
}, [subdirectory, parentId]);
|
||||||
|
|
||||||
const childFilter = useMemo(() => {
|
|
||||||
if (!subdirectory || !parentRow) return undefined;
|
|
||||||
const col = String(subdirectory.childColumnIndex ?? 0);
|
|
||||||
const parentValue = parentRow.values?.[subdirectory.parentColumnIndex ?? 0] ?? '';
|
|
||||||
if (!parentValue) return undefined;
|
|
||||||
return { [col]: parentValue };
|
|
||||||
}, [subdirectory, parentRow]);
|
|
||||||
|
|
||||||
const handleParentChange = (newParentId: string) => {
|
const handleParentChange = (newParentId: string) => {
|
||||||
onChange(serializeTableValue(newParentId || null, null));
|
onChange(serializeTableValue(newParentId || null, null));
|
||||||
};
|
};
|
||||||
@@ -144,8 +114,6 @@ export default function TableFieldWithSubdirectory({
|
|||||||
const childOptions = {
|
const childOptions = {
|
||||||
...opts,
|
...opts,
|
||||||
tableId: subdirectory.childTableId,
|
tableId: subdirectory.childTableId,
|
||||||
displayColumn: subdirectory.childTable?.columns?.[subdirectory.childColumnIndex ?? 0]?.name || undefined,
|
|
||||||
filters: childFilter,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -158,7 +126,7 @@ export default function TableFieldWithSubdirectory({
|
|||||||
value={parentId || ''}
|
value={parentId || ''}
|
||||||
onChange={handleParentChange}
|
onChange={handleParentChange}
|
||||||
/>
|
/>
|
||||||
{isParentLinked && childFilter && (
|
{isParentLinked && (
|
||||||
<TableFieldSelect
|
<TableFieldSelect
|
||||||
fieldId={fieldId}
|
fieldId={fieldId}
|
||||||
fieldCode={`${fieldCode}_sub`}
|
fieldCode={`${fieldCode}_sub`}
|
||||||
|
|||||||
@@ -116,8 +116,6 @@ export default function TableEditor() {
|
|||||||
const [subdirectoriesOpen, setSubdirectoriesOpen] = useState(false);
|
const [subdirectoriesOpen, setSubdirectoriesOpen] = useState(false);
|
||||||
const [editingSubdirectory, setEditingSubdirectory] = useState<DataTableSubdirectory | null>(null);
|
const [editingSubdirectory, setEditingSubdirectory] = useState<DataTableSubdirectory | null>(null);
|
||||||
const [subdirectoryChildTableId, setSubdirectoryChildTableId] = useState<string>('none');
|
const [subdirectoryChildTableId, setSubdirectoryChildTableId] = useState<string>('none');
|
||||||
const [subdirectoryParentColumnIndex, setSubdirectoryParentColumnIndex] = useState<number>(0);
|
|
||||||
const [subdirectoryChildColumnIndex, setSubdirectoryChildColumnIndex] = useState<number>(0);
|
|
||||||
const [subdirectoryParentRowIds, setSubdirectoryParentRowIds] = useState<Set<number>>(new Set());
|
const [subdirectoryParentRowIds, setSubdirectoryParentRowIds] = useState<Set<number>>(new Set());
|
||||||
|
|
||||||
const { filters, sort, setFilter, clearFilter, clearAll, setSort, clearSort, hasActiveFilters } = useColumnFilters();
|
const { filters, sort, setFilter, clearFilter, clearAll, setSort, clearSort, hasActiveFilters } = useColumnFilters();
|
||||||
@@ -213,7 +211,7 @@ export default function TableEditor() {
|
|||||||
}, [rowEditorOpen, rowEditorInitialValues]);
|
}, [rowEditorOpen, rowEditorInitialValues]);
|
||||||
|
|
||||||
const createSubdirectoryMutation = useMutation({
|
const createSubdirectoryMutation = useMutation({
|
||||||
mutationFn: async (payload: { childTableId: number; parentColumnIndex: number; childColumnIndex: number; parentRowIds: number[] }) => {
|
mutationFn: async (payload: { childTableId: number; parentRowIds: number[] }) => {
|
||||||
return apiRequest('POST', `/api/directories/${tableId}/subdirectories`, payload);
|
return apiRequest('POST', `/api/directories/${tableId}/subdirectories`, payload);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -230,7 +228,7 @@ export default function TableEditor() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateSubdirectoryMutation = useMutation({
|
const updateSubdirectoryMutation = useMutation({
|
||||||
mutationFn: async ({ linkId, payload }: { linkId: number; payload: { childTableId?: number; parentColumnIndex?: number; childColumnIndex?: number; parentRowIds?: number[] } }) => {
|
mutationFn: async ({ linkId, payload }: { linkId: number; payload: { childTableId?: number; parentRowIds?: number[] } }) => {
|
||||||
return apiRequest('PATCH', `/api/directories/${tableId}/subdirectories/${linkId}`, payload);
|
return apiRequest('PATCH', `/api/directories/${tableId}/subdirectories/${linkId}`, payload);
|
||||||
},
|
},
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
@@ -263,8 +261,6 @@ export default function TableEditor() {
|
|||||||
|
|
||||||
const resetSubdirectoryForm = () => {
|
const resetSubdirectoryForm = () => {
|
||||||
setSubdirectoryChildTableId('none');
|
setSubdirectoryChildTableId('none');
|
||||||
setSubdirectoryParentColumnIndex(0);
|
|
||||||
setSubdirectoryChildColumnIndex(0);
|
|
||||||
setSubdirectoryParentRowIds(new Set());
|
setSubdirectoryParentRowIds(new Set());
|
||||||
setEditingSubdirectory(null);
|
setEditingSubdirectory(null);
|
||||||
};
|
};
|
||||||
@@ -273,8 +269,6 @@ export default function TableEditor() {
|
|||||||
if (link) {
|
if (link) {
|
||||||
setEditingSubdirectory(link);
|
setEditingSubdirectory(link);
|
||||||
setSubdirectoryChildTableId(link.childTableId?.toString() || 'none');
|
setSubdirectoryChildTableId(link.childTableId?.toString() || 'none');
|
||||||
setSubdirectoryParentColumnIndex(link.parentColumnIndex ?? 0);
|
|
||||||
setSubdirectoryChildColumnIndex(link.childColumnIndex ?? 0);
|
|
||||||
setSubdirectoryParentRowIds(new Set(link.parentRowIds || []));
|
setSubdirectoryParentRowIds(new Set(link.parentRowIds || []));
|
||||||
} else {
|
} else {
|
||||||
resetSubdirectoryForm();
|
resetSubdirectoryForm();
|
||||||
@@ -298,8 +292,6 @@ export default function TableEditor() {
|
|||||||
}
|
}
|
||||||
const payload = {
|
const payload = {
|
||||||
childTableId: childId,
|
childTableId: childId,
|
||||||
parentColumnIndex: subdirectoryParentColumnIndex,
|
|
||||||
childColumnIndex: subdirectoryChildColumnIndex,
|
|
||||||
parentRowIds: Array.from(subdirectoryParentRowIds),
|
parentRowIds: Array.from(subdirectoryParentRowIds),
|
||||||
};
|
};
|
||||||
if (editingSubdirectory?.id) {
|
if (editingSubdirectory?.id) {
|
||||||
@@ -1793,10 +1785,9 @@ export default function TableEditor() {
|
|||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="font-medium text-sm truncate">{link.childTable?.name || `ID ${link.childTableId}`}</div>
|
<div className="font-medium text-sm truncate">{link.childTable?.name || `ID ${link.childTableId}`}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
по столбцу «{columns[link.parentColumnIndex ?? 0]?.name || '?'}» => «{link.childTable?.columns?.[link.childColumnIndex ?? 0]?.name || '?'}»
|
{link.parentRowIds && link.parentRowIds.length > 0
|
||||||
{link.parentRowIds && link.parentRowIds.length > 0 && (
|
? `Привязан к ${link.parentRowIds.length} строкам`
|
||||||
<span className="ml-1">({link.parentRowIds.length} строк)</span>
|
: 'Не привязан к строкам'}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
@@ -1833,41 +1824,6 @@ export default function TableEditor() {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-4">
|
|
||||||
<div>
|
|
||||||
<Label>Столбец родителя</Label>
|
|
||||||
<Select value={String(subdirectoryParentColumnIndex)} onValueChange={(v) => setSubdirectoryParentColumnIndex(parseInt(v))}>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Столбец" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{columns.map((col, idx) => (
|
|
||||||
<SelectItem key={idx} value={String(idx)}>{col.name}</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Столбец подсправочника</Label>
|
|
||||||
<Select
|
|
||||||
value={String(subdirectoryChildColumnIndex)}
|
|
||||||
onValueChange={(v) => setSubdirectoryChildColumnIndex(parseInt(v))}
|
|
||||||
disabled={subdirectoryChildTableId === 'none'}
|
|
||||||
>
|
|
||||||
<SelectTrigger>
|
|
||||||
<SelectValue placeholder="Столбец" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{allTables
|
|
||||||
.find((t) => t.id.toString() === subdirectoryChildTableId)
|
|
||||||
?.columns?.map((col: any, idx: number) => (
|
|
||||||
<SelectItem key={idx} value={String(idx)}>{col.name}</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Label className="mb-2 block">Строки родительского справочника</Label>
|
<Label className="mb-2 block">Строки родительского справочника</Label>
|
||||||
<div className="border rounded p-2 max-h-48 overflow-y-auto space-y-1">
|
<div className="border rounded p-2 max-h-48 overflow-y-auto space-y-1">
|
||||||
|
|||||||
Reference in New Issue
Block a user