Skip to content

Commit

Permalink
Fix errored cell editing (#657)
Browse files Browse the repository at this point in the history
* Added errorIndex/RowNumbers to the state

* Moved error apllying logic to `tableLoader`

* Improved `make preview` command for testing

* Revert "Improved `make preview` command for testing"

This reverts commit 05d43d8.
  • Loading branch information
roll authored Nov 26, 2024
1 parent c08961b commit ef80e62
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ async function uploadLocalFiles(props: { source: FileList }) {
const files: IFile[] = []
const folder = appStore.getFolderPath(appStore.getState())

// @ts-ignore
for (const file of props.source) {
const path = file.webkitRelativePath
const result = await client.fileCreate({ file, path, folder, deduplicate: true })
Expand Down
6 changes: 6 additions & 0 deletions client/components/Controllers/Table/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import TableEditor from '../../Editors/Table'
export default function Editor() {
const schema = store.useStore((state) => state.record?.resource.schema)
const report = store.useStore((state) => state.report)
const errorIndex = store.useStore((state) => state.errorIndex)
const errorRowNumbers = store.useStore((state) => state.errorRowNumbers)
const table = store.useStore((state) => state.table)

// NOTE: It might be better to move it to the global store for easier debugging
Expand Down Expand Up @@ -54,6 +56,8 @@ export default function Editor() {

if (!schema) return null
if (!report) return null
if (!errorIndex) return null
if (!errorRowNumbers) return null
if (!table) return null

return (
Expand All @@ -68,6 +72,8 @@ export default function Editor() {
source={store.tableLoader}
schema={schema}
report={report}
errorIndex={errorIndex}
errorRowNumbers={errorRowNumbers}
history={table.history}
selection={table.selection}
onEditStart={store.startTableEditing}
Expand Down
27 changes: 18 additions & 9 deletions client/components/Editors/Table/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,26 @@ import LightTooltip from '../../Parts/Tooltips/Light'

// TODO: remove colors hard-coding (declare them in settings.ts and use in theme/here)

export function createColumns(
schema: types.ISchema,
report?: types.IReport,
history?: types.IHistory,
selection?: types.ITableSelection,
export function createColumns(props: {
schema: types.ISchema
report: types.IReport
errorIndex: types.IErrorIndex
errorRowNumbers: number[]
history: types.IHistory
selection?: types.ITableSelection
colorPalette?: any
) {
const errorIndex = helpers.createErrorIndex(report)
}) {
const {
schema,
report,
errorIndex,
errorRowNumbers,
history,
selection,
colorPalette,
} = props

const changeIndex = helpers.createChangeIndex(history)
const errorRowNumbers = helpers.getErrorRowNumbers(report)

// Row number column

Expand Down Expand Up @@ -108,7 +118,6 @@ export function createColumns(
error = errorIndex.cell[cellKey][0]
}
if (error) {
value = error.cell || value
cellProps.style.color = 'white'
cellProps.style.cursor = 'pointer'
cellProps.style.background = colorPalette.OKFNRed400.main
Expand Down
31 changes: 26 additions & 5 deletions client/components/Editors/Table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,43 @@ export type ITableEditor = TypeComputedProps | null
export interface TableEditorProps extends Partial<TypeDataGridProps> {
source: types.ITableLoader | types.IRow[]
schema: types.ISchema
report?: types.IReport
history?: types.IHistory
report: types.IReport
errorIndex: types.IErrorIndex
errorRowNumbers: number[]
history: types.IHistory
selection?: types.ITableSelection
onColumnRename?: (props: { index: number; oldName: string; newName: string }) => void
}

export default function TableEditor(props: TableEditorProps) {
const { source, schema, report, history, selection, onColumnRename, ...others } = props
const {
source,
schema,
report,
errorIndex,
errorRowNumbers,
history,
selection,
onColumnRename,
...others
} = props
const [dialog, setDialog] = React.useState<IDialog | undefined>()

const theme = useTheme()
const colorPalette = theme.palette

const columns = React.useMemo(
() => createColumns(schema, report, history, selection, colorPalette),
[schema, report, history, selection]
() =>
createColumns({
schema,
report,
errorIndex,
errorRowNumbers,
history,
selection,
colorPalette,
}),
[schema, report, errorIndex, history, selection]
)
const [rowsPerPage, setRowsPerPage] = React.useState(20)
const [userRowsPerPage, setUserRowsPerPage] = React.useState<number | undefined>()
Expand Down
21 changes: 20 additions & 1 deletion client/helpers/history.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as types from '../types'
import * as types from '@client/types'
import remove from 'lodash/remove'

export function createChangeIndex(patch?: types.IHistory) {
const changeIndex: types.IChangeIndex = { header: {}, label: {}, row: {}, cell: {} }
Expand All @@ -19,3 +20,21 @@ export function createChangeIndex(patch?: types.IHistory) {
}
return changeIndex
}

export function applyTableHistory(history: types.IHistory, rows: types.IRow[]) {
for (const change of history.changes) {
if (change.type === 'row-delete') {
remove(rows, (row) => row._rowNumber === change.rowNumber)
} else if (change.type === 'cell-update') {
for (const row of rows) {
if (row._rowNumber === change.rowNumber) row[change.fieldName] = change.value
}
} else if (change.type == 'multiple-cells-update') {
for (const row of rows) {
for (const cell of change.cells) {
if (row._rowNumber === cell.rowNumber) row[cell.fieldName] = cell.value
}
}
}
}
}
1 change: 0 additions & 1 deletion client/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,4 @@ export * from './menu'
export * from './portal'
export * from './report'
export * from './store'
export * from './table'
export * from './validateURL'
15 changes: 14 additions & 1 deletion client/helpers/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,18 @@ export function getErrorRowNumbers(report?: types.IReport) {
}
}

return rowNumbers
return rowNumbers.toSorted((a, b) => a - b)
}

export function applyTableErrors(errorIndex: types.IErrorIndex, rows: types.IRow[]) {
for (const row of rows) {
const rowNumber = row._rowNumber
for (const [columnName, cell] of Object.entries(row)) {
const cellKey = `${rowNumber},${columnName}`
if (cellKey in errorIndex.cell) {
const error = errorIndex.cell[cellKey][0]
row[columnName] = error?.cell || cell
}
}
}
}
20 changes: 0 additions & 20 deletions client/helpers/table.ts

This file was deleted.

4 changes: 4 additions & 0 deletions client/store/actions/file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ async function openFile() {
store.setState('open-file-loaded', (state) => {
state.record = result.record
state.report = result.report
state.errorIndex = helpers.createErrorIndex(result.report)
state.errorRowNumbers = helpers.getErrorRowNumbers(result.report)
state.resource = cloneDeep(result.record.resource)
})

Expand All @@ -99,6 +101,8 @@ async function closeFile() {
store.setState('close-file', (state) => {
state.record = undefined
state.report = undefined
state.errorIndex = undefined
state.errorRowNumbers = undefined
state.resource = undefined
state.isResourceUpdated = undefined
state.source = undefined
Expand Down
18 changes: 12 additions & 6 deletions client/store/actions/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,11 @@ export async function renameColumn(props: {

export const tableLoader: types.ITableLoader = async ({ skip, limit, sortInfo }) => {
const defaultResult = { data: [], count: 0 }
const { path, table } = store.getState()
if (!path || !table) return defaultResult
const { path, table, errorIndex } = store.getState()

if (!path || !table || !errorIndex) {
return defaultResult
}

const result = await client.tableRead({
path,
Expand All @@ -276,15 +279,18 @@ export const tableLoader: types.ITableLoader = async ({ skip, limit, sortInfo })
return defaultResult
}

// Create requested data frame
// convert null fields in db to empty strings to avoid errors
// in table representation. InovuaDataGrid complains with null values
const rowsNotNull = []
const data = []
for (const row of result.rows) {
rowsNotNull.push(mapValues(row, (value) => (isNull(value) ? '' : value)))
data.push(mapValues(row, (value) => (isNull(value) ? '' : value)))
}

helpers.applyTableHistory(table.history, rowsNotNull)
return { data: rowsNotNull, count: table.rowCount || 0 }
helpers.applyTableErrors(errorIndex, data)
helpers.applyTableHistory(table.history, data)

return { data, count: table.rowCount || 0 }
}

// Selectors
Expand Down
10 changes: 10 additions & 0 deletions client/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ export type IState = {
**/
report?: types.IReport

/**
* An error index derived from the validation report
**/
errorIndex?: types.IErrorIndex

/**
* An error row numbers derived from the validation report
**/
errorRowNumbers?: number[]

/**
* A Data Resource descriptor for the current file
* It can be edited by a metadata editor as a part of metadata adjustment
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
"compilerOptions": {
"strict": true,
"noEmit": true,
"target": "ES2021",
"target": "ES2022",
"module": "commonjs",
"lib": ["ESNext", "ESNext.Array", "DOM"],
"declaration": true,
"esModuleInterop": true,
"resolveJsonModule": true,
Expand Down

0 comments on commit ef80e62

Please sign in to comment.