Merge pull request #77 from valueonag/feat/demo-system-readieness
merge fixes
This commit is contained in:
commit
77f4693f4b
2 changed files with 90 additions and 210 deletions
|
|
@ -81,6 +81,15 @@ import api from '../../../api';
|
||||||
import { PeriodPicker } from '../../PeriodPicker';
|
import { PeriodPicker } from '../../PeriodPicker';
|
||||||
import type { PeriodValue } from '../../PeriodPicker';
|
import type { PeriodValue } from '../../PeriodPicker';
|
||||||
import { TableViewsBar, groupLevelsToApiPayload, type GroupByLevelSpec } from '../TableViewsBar';
|
import { TableViewsBar, groupLevelsToApiPayload, type GroupByLevelSpec } from '../TableViewsBar';
|
||||||
|
import {
|
||||||
|
listTableViews,
|
||||||
|
getTableView,
|
||||||
|
createTableView,
|
||||||
|
updateTableView,
|
||||||
|
deleteTableView,
|
||||||
|
type TableListViewRow,
|
||||||
|
type TableViewConfig,
|
||||||
|
} from '../../../api/tableViewApi';
|
||||||
|
|
||||||
function groupLevelsFromViewConfig(raw: unknown): GroupByLevelSpec[] {
|
function groupLevelsFromViewConfig(raw: unknown): GroupByLevelSpec[] {
|
||||||
if (!Array.isArray(raw)) return [];
|
if (!Array.isArray(raw)) return [];
|
||||||
|
|
@ -91,15 +100,6 @@ function groupLevelsFromViewConfig(raw: unknown): GroupByLevelSpec[] {
|
||||||
}))
|
}))
|
||||||
.filter((l) => l.field);
|
.filter((l) => l.field);
|
||||||
}
|
}
|
||||||
import {
|
|
||||||
listTableViews,
|
|
||||||
getTableView,
|
|
||||||
createTableView,
|
|
||||||
updateTableView,
|
|
||||||
deleteTableView,
|
|
||||||
type TableListViewRow,
|
|
||||||
type TableViewConfig,
|
|
||||||
} from '../../../api/tableViewApi';
|
|
||||||
|
|
||||||
function collapseLocalStorageKey(contextKey: string) {
|
function collapseLocalStorageKey(contextKey: string) {
|
||||||
return `porta_table_collapse_${contextKey}`;
|
return `porta_table_collapse_${contextKey}`;
|
||||||
|
|
@ -2650,6 +2650,81 @@ export function FormGeneratorTable<T extends Record<string, any>>({
|
||||||
batchActions.length > 0 ||
|
batchActions.length > 0 ||
|
||||||
(selectable && selectedIds.size > 0);
|
(selectable && selectedIds.size > 0);
|
||||||
|
|
||||||
|
const _renderDataRow = (row: T, index: number) => {
|
||||||
|
const dataAttributes = getRowDataAttributes ? getRowDataAttributes(row, index) : {};
|
||||||
|
const rowId = _getRowId(row);
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
key={rowId || index}
|
||||||
|
className={`${styles.tr} ${selectedIds.has(rowId) ? styles.selected : ''} ${onRowClick ? styles.clickable : ''}`}
|
||||||
|
onClick={() => onRowClick?.(row, index)}
|
||||||
|
draggable={rowDraggable}
|
||||||
|
onDragStart={(e) => {
|
||||||
|
if (rowDraggable && onRowDragStart) onRowDragStart(e, row);
|
||||||
|
}}
|
||||||
|
onDragEnd={() => {}}
|
||||||
|
{...Object.fromEntries(Object.entries(dataAttributes).map(([k, v]) => [`data-${k}`, v]))}
|
||||||
|
>
|
||||||
|
{selectable && (
|
||||||
|
<td className={styles.selectColumn} style={{ width: '40px', minWidth: '40px', maxWidth: '40px' }}>
|
||||||
|
<input type="checkbox"
|
||||||
|
checked={selectedIds.has(rowId)}
|
||||||
|
onChange={() => handleRowSelect(row)}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
disabled={isRowSelectable && !isRowSelectable(row)}
|
||||||
|
title={isRowSelectable && !isRowSelectable(row) ? t('Dieses Element kann nicht ausgewählt werden') : t('Dieses Element auswählen')}
|
||||||
|
style={{ opacity: isRowSelectable && !isRowSelectable(row) ? 0.4 : 1, cursor: isRowSelectable && !isRowSelectable(row) ? 'not-allowed' : 'pointer' }}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
{hasActionColumn && (
|
||||||
|
<td className={styles.actionsColumn} style={compact ? undefined : { width: `${currentActionsWidth}px`, minWidth: `${defaultActionsWidth}px` }}>
|
||||||
|
<div ref={(el) => { if (el) actionButtonsRefs.current.set(index, el); else actionButtonsRefs.current.delete(index); }}
|
||||||
|
className={`${styles.actionButtons} ${shouldWrapActionButtons ? styles.actionButtonsWrap : ''}`}>
|
||||||
|
{actionButtons.map((ab, ai) => {
|
||||||
|
if (ab.visible && !ab.visible(row, hookData)) return null;
|
||||||
|
const abTitle = typeof ab.title === 'function' ? ab.title(row) : ab.title;
|
||||||
|
let dis: boolean | { disabled: boolean; message?: string } = false;
|
||||||
|
if (ab.disabled) { dis = ab.disabled(row, hookData); }
|
||||||
|
else if (row._permissions) {
|
||||||
|
if (ab.type === 'edit' && row._permissions.canUpdate === false) dis = true;
|
||||||
|
else if (ab.type === 'delete' && row._permissions.canDelete === false) dis = true;
|
||||||
|
}
|
||||||
|
const isLd = ab.loading ? ab.loading(row) : false;
|
||||||
|
const isProc = ab.isProcessing ? ab.isProcessing(row) : false;
|
||||||
|
const bp = { row, disabled: dis, loading: isLd, className: [compact ? actionBtnStyles.compact : '', ab.className ?? ''].filter(Boolean).join(' '), title: abTitle, idField: ab.idField ?? 'id', nameField: ab.nameField ?? 'name', typeField: ab.typeField ?? 'type', contentField: ab.contentField ?? 'content', operationName: ab.operationName, loadingStateName: ab.loadingStateName };
|
||||||
|
switch (ab.type) {
|
||||||
|
case 'edit': return <EditActionButton key={`a-${ai}`} {...bp} onEdit={ab.onAction} hookData={hookData} />;
|
||||||
|
case 'delete': return <DeleteActionButton key={`a-${ai}`} {...bp} containerRef={{ current: actionButtonsRefs.current.get(index) || null }} hookData={hookData} />;
|
||||||
|
case 'view': return <ViewActionButton key={`a-${ai}`} {...bp} onView={ab.onAction || (() => {})} isViewing={isProc} hookData={hookData} />;
|
||||||
|
case 'copy': return <CopyActionButton key={`a-${ai}`} {...bp} onCopy={ab.onAction} isCopying={isProc} contentField={ab.contentField} />;
|
||||||
|
default: return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
{customActions.map((ca) => (
|
||||||
|
<CustomActionButton key={`ca-${ca.id}`} row={row} id={ca.id} icon={ca.icon}
|
||||||
|
onClick={ca.onClick} visible={ca.visible} disabled={ca.disabled}
|
||||||
|
loading={ca.loading} title={ca.title} className={ca.className}
|
||||||
|
hookData={hookData} idField={ca.idField ?? 'id'} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
|
{detectedColumns.map((col) => {
|
||||||
|
const cv = row[col.key];
|
||||||
|
const cCls = col.cellClassName ? col.cellClassName(cv, row) : '';
|
||||||
|
const aStyle = _columnAlignStyle(col);
|
||||||
|
return (
|
||||||
|
<td key={col.key} className={`${styles.td} ${cCls}`.trim()}
|
||||||
|
style={{ width: columnWidths[col.key] || col.width || 150, minWidth: columnWidths[col.key] || col.width || 150, maxWidth: columnWidths[col.key] || col.width || 150, ...aStyle }}>
|
||||||
|
{formatCellValue(cv, col, row)}
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`${styles.formGeneratorTable} ${compact ? styles.compactMode : ''} ${useSectionsGroupLayout ? styles.formGeneratorTableSectionsRoot : ''} ${className}`}
|
className={`${styles.formGeneratorTable} ${compact ? styles.compactMode : ''} ${useSectionsGroupLayout ? styles.formGeneratorTableSectionsRoot : ''} ${className}`}
|
||||||
|
|
@ -3157,116 +3232,9 @@ export function FormGeneratorTable<T extends Record<string, any>>({
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
{isExpanded && groupRows.map((row, rowIndex) => {
|
{isExpanded && groupRows.map((row) => {
|
||||||
const globalIndex = displayData.indexOf(row);
|
const globalIndex = displayData.indexOf(row);
|
||||||
const dataAttributes = getRowDataAttributes ? getRowDataAttributes(row, globalIndex) : {};
|
return _renderDataRow(row, globalIndex);
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={`${groupKey}-row-${rowIndex}`}
|
|
||||||
className={`${styles.tr} ${selectedIds.has(_getRowId(row)) ? styles.selected : ''} ${onRowClick ? styles.clickable : ''}`}
|
|
||||||
onClick={() => onRowClick?.(row, globalIndex)}
|
|
||||||
draggable={rowDraggable}
|
|
||||||
onDragStart={rowDraggable && onRowDragStart ? (e) => onRowDragStart(e, row) : undefined}
|
|
||||||
{...Object.fromEntries(
|
|
||||||
Object.entries(dataAttributes).map(([key, value]) => [`data-${key}`, value])
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{selectable && (
|
|
||||||
<td className={styles.selectColumn} style={{ width: '40px', minWidth: '40px', maxWidth: '40px' }}>
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={selectedIds.has(_getRowId(row))}
|
|
||||||
onChange={() => handleRowSelect(row)}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
disabled={isRowSelectable && !isRowSelectable(row)}
|
|
||||||
title={
|
|
||||||
isRowSelectable && !isRowSelectable(row)
|
|
||||||
? t('Dieses Element kann nicht ausgewählt werden')
|
|
||||||
: t('Dieses Element auswählen')
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
opacity: isRowSelectable && !isRowSelectable(row) ? 0.4 : 1,
|
|
||||||
cursor: isRowSelectable && !isRowSelectable(row) ? 'not-allowed' : 'pointer'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
{hasActionColumn && (
|
|
||||||
<td
|
|
||||||
className={styles.actionsColumn}
|
|
||||||
style={compact ? undefined : { width: `${currentActionsWidth}px`, minWidth: `${defaultActionsWidth}px` }}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={(el) => {
|
|
||||||
if (el) {
|
|
||||||
actionButtonsRefs.current.set(globalIndex, el);
|
|
||||||
} else {
|
|
||||||
actionButtonsRefs.current.delete(globalIndex);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={`${styles.actionButtons} ${shouldWrapActionButtons ? styles.actionButtonsWrap : ''}`}
|
|
||||||
>
|
|
||||||
{actionButtons.map((actionButton, actionIndex) => {
|
|
||||||
if (actionButton.visible && !actionButton.visible(row, hookData)) return null;
|
|
||||||
const actionTitle = typeof actionButton.title === 'function'
|
|
||||||
? actionButton.title(row)
|
|
||||||
: actionButton.title;
|
|
||||||
let disabledResult: boolean | { disabled: boolean; message?: string } = false;
|
|
||||||
if (actionButton.disabled) {
|
|
||||||
disabledResult = actionButton.disabled(row, hookData);
|
|
||||||
} else if (row._permissions) {
|
|
||||||
if (actionButton.type === 'edit' && row._permissions.canUpdate === false) {
|
|
||||||
disabledResult = true;
|
|
||||||
} else if (actionButton.type === 'delete' && row._permissions.canDelete === false) {
|
|
||||||
disabledResult = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const isLoading = actionButton.loading ? actionButton.loading(row) : false;
|
|
||||||
const isProcessing = actionButton.isProcessing ? actionButton.isProcessing(row) : false;
|
|
||||||
const baseProps = {
|
|
||||||
row, disabled: disabledResult, loading: isLoading,
|
|
||||||
className: [compact ? actionBtnStyles.compact : '', actionButton.className ?? ''].filter(Boolean).join(' '),
|
|
||||||
title: actionTitle,
|
|
||||||
idField: actionButton.idField ?? 'id', nameField: actionButton.nameField ?? 'name',
|
|
||||||
typeField: actionButton.typeField ?? 'type', contentField: actionButton.contentField ?? 'content',
|
|
||||||
operationName: actionButton.operationName, loadingStateName: actionButton.loadingStateName
|
|
||||||
};
|
|
||||||
switch (actionButton.type) {
|
|
||||||
case 'edit':
|
|
||||||
return <EditActionButton key={`action-${actionIndex}`} {...baseProps} onEdit={actionButton.onAction} hookData={hookData} />;
|
|
||||||
case 'delete':
|
|
||||||
return <DeleteActionButton key={`action-${actionIndex}`} {...baseProps} containerRef={{ current: actionButtonsRefs.current.get(globalIndex) || null }} hookData={hookData} />;
|
|
||||||
case 'view':
|
|
||||||
return <ViewActionButton key={`action-${actionIndex}`} {...baseProps} onView={actionButton.onAction || (() => {})} isViewing={isProcessing} hookData={hookData} />;
|
|
||||||
case 'copy':
|
|
||||||
return <CopyActionButton key={`action-${actionIndex}`} {...baseProps} onCopy={actionButton.onAction} isCopying={isProcessing} contentField={actionButton.contentField} />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{customActions.map((customAction) => (
|
|
||||||
<CustomActionButton key={`custom-${customAction.id}`} row={row} id={customAction.id} icon={customAction.icon}
|
|
||||||
onClick={customAction.onClick} visible={customAction.visible} disabled={customAction.disabled}
|
|
||||||
loading={customAction.loading} title={customAction.title} className={customAction.className}
|
|
||||||
hookData={hookData} idField={customAction.idField ?? 'id'} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
{detectedColumns.map(column => {
|
|
||||||
const cellValue = row[column.key];
|
|
||||||
const customClassName = column.cellClassName ? column.cellClassName(cellValue, row) : '';
|
|
||||||
const combinedClassName = `${styles.td} ${customClassName}`.trim();
|
|
||||||
const alignStyle = _columnAlignStyle(column);
|
|
||||||
return (
|
|
||||||
<td key={column.key} className={combinedClassName}
|
|
||||||
style={{ width: columnWidths[column.key] || column.width || 150, minWidth: columnWidths[column.key] || column.width || 150, maxWidth: columnWidths[column.key] || column.width || 150, ...alignStyle }}>
|
|
||||||
{formatCellValue(cellValue, column, row)}
|
|
||||||
</td>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
|
|
@ -3276,97 +3244,6 @@ export function FormGeneratorTable<T extends Record<string, any>>({
|
||||||
// Total colspan for group/breadcrumb/ungrouped rows
|
// Total colspan for group/breadcrumb/ungrouped rows
|
||||||
const _totalColSpan = (selectable ? 1 : 0) + (hasActionColumn ? 1 : 0) + detectedColumns.length;
|
const _totalColSpan = (selectable ? 1 : 0) + (hasActionColumn ? 1 : 0) + detectedColumns.length;
|
||||||
|
|
||||||
// ── Helper: render a single data row ──────────────────
|
|
||||||
const _renderDataRow = (row: T, index: number) => {
|
|
||||||
const dataAttributes = getRowDataAttributes ? getRowDataAttributes(row, index) : {};
|
|
||||||
const rowId = _getRowId(row);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr
|
|
||||||
key={rowId || index}
|
|
||||||
className={`${styles.tr} ${selectedIds.has(rowId) ? styles.selected : ''} ${onRowClick ? styles.clickable : ''}`}
|
|
||||||
onClick={() => onRowClick?.(row, index)}
|
|
||||||
draggable={rowDraggable}
|
|
||||||
onDragStart={(e) => {
|
|
||||||
if (rowDraggable && onRowDragStart) onRowDragStart(e, row);
|
|
||||||
}}
|
|
||||||
onDragEnd={() => {}}
|
|
||||||
{...Object.fromEntries(Object.entries(dataAttributes).map(([k, v]) => [`data-${k}`, v]))}
|
|
||||||
>
|
|
||||||
{selectable && (
|
|
||||||
<td
|
|
||||||
className={styles.selectColumn}
|
|
||||||
style={{ width: '40px', minWidth: '40px', maxWidth: '40px' }}
|
|
||||||
>
|
|
||||||
<input type="checkbox"
|
|
||||||
checked={selectedIds.has(_getRowId(row))}
|
|
||||||
onChange={() => handleRowSelect(row)}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
disabled={isRowSelectable && !isRowSelectable(row)}
|
|
||||||
title={isRowSelectable && !isRowSelectable(row) ? t('Dieses Element kann nicht ausgewählt werden') : t('Dieses Element auswählen')}
|
|
||||||
style={{ opacity: isRowSelectable && !isRowSelectable(row) ? 0.4 : 1, cursor: isRowSelectable && !isRowSelectable(row) ? 'not-allowed' : 'pointer' }}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
{hasActionColumn && (
|
|
||||||
<td
|
|
||||||
className={styles.actionsColumn}
|
|
||||||
style={compact ? undefined : { width: `${currentActionsWidth}px`, minWidth: `${defaultActionsWidth}px` }}
|
|
||||||
>
|
|
||||||
<div ref={(el) => { if (el) actionButtonsRefs.current.set(index, el); else actionButtonsRefs.current.delete(index); }}
|
|
||||||
className={`${styles.actionButtons} ${shouldWrapActionButtons ? styles.actionButtonsWrap : ''}`}>
|
|
||||||
{actionButtons.map((ab, ai) => {
|
|
||||||
if (ab.visible && !ab.visible(row, hookData)) return null;
|
|
||||||
const abTitle = typeof ab.title === 'function' ? ab.title(row) : ab.title;
|
|
||||||
let dis: boolean | { disabled: boolean; message?: string } = false;
|
|
||||||
if (ab.disabled) { dis = ab.disabled(row, hookData); }
|
|
||||||
else if (row._permissions) {
|
|
||||||
if (ab.type === 'edit' && row._permissions.canUpdate === false) dis = true;
|
|
||||||
else if (ab.type === 'delete' && row._permissions.canDelete === false) dis = true;
|
|
||||||
}
|
|
||||||
const isLd = ab.loading ? ab.loading(row) : false;
|
|
||||||
const isProc = ab.isProcessing ? ab.isProcessing(row) : false;
|
|
||||||
const bp = { row, disabled: dis, loading: isLd, className: [compact ? actionBtnStyles.compact : '', ab.className ?? ''].filter(Boolean).join(' '), title: abTitle, idField: ab.idField ?? 'id', nameField: ab.nameField ?? 'name', typeField: ab.typeField ?? 'type', contentField: ab.contentField ?? 'content', operationName: ab.operationName, loadingStateName: ab.loadingStateName };
|
|
||||||
switch (ab.type) {
|
|
||||||
case 'edit': return <EditActionButton key={`a-${ai}`} {...bp} onEdit={ab.onAction} hookData={hookData} />;
|
|
||||||
case 'delete': return <DeleteActionButton key={`a-${ai}`} {...bp} containerRef={{ current: actionButtonsRefs.current.get(index) || null }} hookData={hookData} />;
|
|
||||||
case 'view': return <ViewActionButton key={`a-${ai}`} {...bp} onView={ab.onAction || (() => {})} isViewing={isProc} hookData={hookData} />;
|
|
||||||
case 'copy': return <CopyActionButton key={`a-${ai}`} {...bp} onCopy={ab.onAction} isCopying={isProc} contentField={ab.contentField} />;
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
{customActions.map((ca) => (
|
|
||||||
<CustomActionButton key={`ca-${ca.id}`} row={row} id={ca.id} icon={ca.icon}
|
|
||||||
onClick={ca.onClick} visible={ca.visible} disabled={ca.disabled}
|
|
||||||
loading={ca.loading} title={ca.title} className={ca.className}
|
|
||||||
hookData={hookData} idField={ca.idField ?? 'id'} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
)}
|
|
||||||
{detectedColumns.map((col) => {
|
|
||||||
const cv = row[col.key];
|
|
||||||
const cCls = col.cellClassName ? col.cellClassName(cv, row) : '';
|
|
||||||
const aStyle = _columnAlignStyle(col);
|
|
||||||
return (
|
|
||||||
<td
|
|
||||||
key={col.key}
|
|
||||||
className={`${styles.td} ${cCls}`.trim()}
|
|
||||||
style={{
|
|
||||||
width: columnWidths[col.key] || col.width || 150,
|
|
||||||
minWidth: columnWidths[col.key] || col.width || 150,
|
|
||||||
maxWidth: columnWidths[col.key] || col.width || 150,
|
|
||||||
...aStyle,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{formatCellValue(cv, col, row)}
|
|
||||||
</td>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Strategy B view grouping: insert collapsible group headers ──
|
// ── Strategy B view grouping: insert collapsible group headers ──
|
||||||
if (effectiveGroupLayout && effectiveGroupLayout.bands.length > 0) {
|
if (effectiveGroupLayout && effectiveGroupLayout.bands.length > 0) {
|
||||||
const rows: React.ReactNode[] = [];
|
const rows: React.ReactNode[] = [];
|
||||||
|
|
|
||||||
|
|
@ -405,10 +405,12 @@ export function FormGeneratorTree<T = any>({
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
onRefresh,
|
onRefresh,
|
||||||
onSendToChat,
|
onSendToChat,
|
||||||
|
allowCreateFolder = true,
|
||||||
className,
|
className,
|
||||||
}: FormGeneratorTreeProps<T>) {
|
}: FormGeneratorTreeProps<T>) {
|
||||||
const { t } = useLanguage();
|
const { t } = useLanguage();
|
||||||
const { confirm, ConfirmDialog } = useConfirm();
|
const { confirm } = useConfirm();
|
||||||
|
const { prompt, PromptDialog } = usePrompt();
|
||||||
const [nodes, setNodes] = useState<TreeNode<T>[]>([]);
|
const [nodes, setNodes] = useState<TreeNode<T>[]>([]);
|
||||||
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
|
||||||
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());
|
||||||
|
|
@ -695,6 +697,7 @@ export function FormGeneratorTree<T = any>({
|
||||||
if (ownership === 'shared') return;
|
if (ownership === 'shared') return;
|
||||||
if (draggingIds.size === 0) return;
|
if (draggingIds.size === 0) return;
|
||||||
if (draggingIds.has(node.id)) return;
|
if (draggingIds.has(node.id)) return;
|
||||||
|
if (node.type !== 'folder') return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.dataTransfer.dropEffect = 'move';
|
e.dataTransfer.dropEffect = 'move';
|
||||||
setDragOverId(node.id);
|
setDragOverId(node.id);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue