CB-6063 feat: add ability to import data without PK (#3188)

* CB-6063 feat: add ability to import data without PK

* CB-6063 feat: add ability to insert rows to tables w/o PK using actions

removes isImportAllowed flag, adds hasRowIdentifier, uses it in place of forbidden actions

* CB-6063 refactor: use only false value for hasRowIdentifier flag

* CB-6063 fix: revert logic in hasRowIdentifier

* CB-6063 refactor: rename property

* CB-6063 fix: return lock icon back for tables w/o identifier

* CB-6063 fix: hasRowIdentifier in getResult

---------

Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com>
Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
This commit is contained in:
Sychev Andrey
2025-01-14 19:23:32 +01:00
committed by GitHub
co-authored by Daria Marutkina Ainur
parent 7a83252490
commit 3b0a4e7d33
13 changed files with 45 additions and 14 deletions
@@ -103,7 +103,10 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey, unknown
);
function isEditable(column: CalculatedColumn<IResultSetRowKey>): boolean {
if (!cellContext.cell) {
if (
!cellContext.cell ||
(!dataGridContext.model.hasElementIdentifier(tableDataContext.view.resultIndex) && cellContext.editionState !== DatabaseEditChangeType.add)
) {
return false;
}
@@ -86,9 +86,11 @@ export class DataGridContextMenuCellEditingService {
const isComplex = format.isBinary(key) || format.isGeometry(key);
const isTruncated = content.isTextTruncated(key);
const selectedElements = select?.getSelectedElements() || [];
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
const canEdit = model.hasElementIdentifier(resultIndex) || editor.getElementState(key) === DatabaseEditChangeType.add;
if (action === ACTION_EDIT) {
if (!column || cellValue === undefined || format.isReadOnly(key) || isComplex || isTruncated) {
if (!column || cellValue === undefined || format.isReadOnly(key) || isComplex || isTruncated || !canEdit) {
return false;
}
@@ -96,7 +98,7 @@ export class DataGridContextMenuCellEditingService {
}
if (action === ACTION_DATA_GRID_EDITING_SET_TO_NULL) {
return cellValue !== undefined && !format.isReadOnly(key) && !view.getColumn(key.column)?.required && !format.isNull(key);
return cellValue !== undefined && !format.isReadOnly(key) && !view.getColumn(key.column)?.required && !format.isNull(key) && canEdit;
}
if (action === ACTION_DATA_GRID_EDITING_ADD_ROW || action === ACTION_DATA_GRID_EDITING_DUPLICATE_ROW) {
@@ -104,11 +106,11 @@ export class DataGridContextMenuCellEditingService {
}
if (action === ACTION_DATA_GRID_EDITING_DELETE_ROW) {
return !format.isReadOnly(key) && editor.getElementState(key) !== DatabaseEditChangeType.delete;
return !format.isReadOnly(key) && canEdit && editor.getElementState(key) !== DatabaseEditChangeType.delete;
}
if (action === ACTION_DATA_GRID_EDITING_DELETE_SELECTED_ROW) {
if (model.isReadonly(resultIndex) || !editor.hasFeature('delete')) {
if (model.isReadonly(resultIndex) || !canEdit || !editor.hasFeature('delete')) {
return false;
}
@@ -222,12 +222,14 @@ export const DataGridTable = observer<IDataPresentationProps>(function DataGridT
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
gridSelectedCellCopy.onKeydownHandler(event);
const cell = selectionAction.getFocusedElement();
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
const canEdit = model.hasElementIdentifier(resultIndex) || !!(cell && tableData.editor.getElementState(cell) === DatabaseEditChangeType.add);
if (EventContext.has(event, EventStopPropagationFlag) || tableData.isReadOnly() || model.isReadonly(resultIndex)) {
if (EventContext.has(event, EventStopPropagationFlag) || !canEdit || tableData.isReadOnly() || model.isReadonly(resultIndex)) {
return;
}
const cell = selectionAction.getFocusedElement();
const activeElements = selectionAction.getActiveElements();
const activeRows = selectionAction.getActiveRows();
@@ -27,7 +27,12 @@ export const TableIndexColumnHeader = observer<RenderHeaderCellProps<any>>(funct
throw new Error('Contexts required');
}
const readonly = getComputed(() => tableDataContext.isReadOnly() || dataGridContext.model.isReadonly(dataGridContext.resultIndex));
const readonly = getComputed(
() =>
tableDataContext.isReadOnly() ||
dataGridContext.model.isReadonly(dataGridContext.resultIndex) ||
!dataGridContext.model.hasElementIdentifier(dataGridContext.resultIndex),
);
function handleClick(event: React.MouseEvent<HTMLDivElement>) {
selectionContext.selectTable();
@@ -59,6 +59,10 @@ export class DatabaseDataModel<TSource extends IDatabaseDataSource<any, any> = I
return this.source.isReadonly(resultIndex);
}
hasElementIdentifier(resultIndex: number): boolean {
return this.source.hasElementIdentifier(resultIndex);
}
isDataAvailable(offset: number, count: number): boolean {
return this.source.isDataAvailable(offset, count);
}
@@ -206,6 +206,10 @@ export abstract class DatabaseDataSource<TOptions, TResult extends IDatabaseData
return !this.isLoading() && !this.disabled;
}
hasElementIdentifier(resultIndex: number): boolean {
return this.getResult(resultIndex)?.data?.hasRowIdentifier === true;
}
isReadonly(resultIndex: number): boolean {
return this.access === DatabaseDataAccessMode.Readonly || this.results.length > 1 || this.disabled;
}
@@ -31,6 +31,7 @@ export interface IDatabaseDataModel<TSource extends IDatabaseDataSource<any, any
setName: (name: string | null) => this;
isReadonly: (resultIndex: number) => boolean;
hasElementIdentifier: (resultIndex: number) => boolean;
isDisabled: (resultIndex?: number) => boolean;
isLoading: () => boolean;
isDataAvailable: (offset: number, count: number) => boolean;
@@ -71,6 +71,7 @@ export interface IDatabaseDataSource<TOptions = unknown, TResult extends IDataba
isOutdated: () => boolean;
isLoadable: () => boolean;
isReadonly: (resultIndex: number) => boolean;
hasElementIdentifier: (resultIndex: number) => boolean;
isDataAvailable: (offset: number, count: number) => boolean;
isLoading: () => boolean;
isDisabled: (resultIndex?: number) => boolean;
@@ -39,7 +39,7 @@ export abstract class ResultSetDataSource<TOptions = IDatabaseDataOptions> exten
}
override isReadonly(resultIndex: number): boolean {
return super.isReadonly(resultIndex) || !this.executionContext?.context || this.getResult(resultIndex)?.data?.hasRowIdentifier === false;
return super.isReadonly(resultIndex) || !this.executionContext?.context;
}
override async cancel(): Promise<void> {
@@ -101,13 +101,16 @@ export class TableFooterMenuService {
}
case ACTION_DELETE: {
const editor = model.source.getActionImplementation(resultIndex, DatabaseEditAction);
const selectedElements = getActiveElements(model, resultIndex);
if (!editor) {
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
const canEdit =
model.hasElementIdentifier(resultIndex) || selectedElements.every(key => editor?.getElementState(key) === DatabaseEditChangeType.add);
if (!editor || !canEdit) {
return true;
}
const selectedElements = getActiveElements(model, resultIndex);
return selectedElements.length === 0 || !selectedElements.some(key => editor.getElementState(key) !== DatabaseEditChangeType.delete);
}
case ACTION_REVERT: {
@@ -51,7 +51,11 @@ export const BooleanValuePresentation: TabContainerPanelComponent<IDataValuePane
const column = viewAction.getColumn(firstSelectedCell.column);
const nullable = column?.required === false;
const readonly = model.isReadonly(resultIndex) || model.isDisabled(resultIndex) || formatAction.isReadOnly(firstSelectedCell);
const readonly =
model.isReadonly(resultIndex) ||
!model.hasElementIdentifier(resultIndex) ||
model.isDisabled(resultIndex) ||
formatAction.isReadOnly(firstSelectedCell);
return (
<div className={classes['container']}>
@@ -30,6 +30,7 @@ export function isTextValueReadonly({ contentAction, formatAction, model, result
formatAction.isGeometry(cell) ||
contentAction.isTextTruncated(cell) ||
model.isReadonly(resultIndex) ||
model.isDisabled(resultIndex)
model.isDisabled(resultIndex) ||
!model.hasElementIdentifier(resultIndex)
);
}
@@ -43,6 +43,7 @@ export class GeneratorMenuBootstrap extends Bootstrap {
const presentation = context.get(DATA_CONTEXT_DV_PRESENTATION);
return (
!model.isReadonly(resultIndex) &&
model.hasElementIdentifier(resultIndex) &&
model.source.getResult(resultIndex)?.dataFormat === ResultDataFormat.Resultset &&
!presentation?.readonly &&
(!presentation || presentation.type === DataViewerPresentationType.Data)