Skip to content

Commit

Permalink
Merge branch 'main' into 359/detect-proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
roll committed Jun 3, 2024
2 parents 292e12e + 76f8bf2 commit c7cfa66
Show file tree
Hide file tree
Showing 42 changed files with 614 additions and 199 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/general.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- name: Prepare environment
run: pip3 install hatch
- name: Install dependencies
Expand All @@ -54,7 +54,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- name: Prepare environment
run: pip3 install hatch
- name: Install dependencies
Expand Down Expand Up @@ -94,7 +94,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: 18
node-version: 20
- name: Prepare environment
run: pip3 install hatch
- name: Install dependencies
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ The Open Data Editor (ODE) is a **no-code application** to **explore, validate a

🔵 [Contributing Guide](https://opendataeditor.okfn.org/contributing/development/)

🔵 [Technical Architecture](https://opendataeditor.okfn.org/contributing/architecture/)

# How to download the ODE

📍***Note:** the ODE is currently available for download and testing in beta. We are working on a stable version. Updates will be announced throughout the year.*
Expand Down
21 changes: 19 additions & 2 deletions client/components/Application/Dialogs/AddRemoteFile.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
import * as React from 'react'
import UploadIcon from '@mui/icons-material/Upload'
import InputDialog from '../../Parts/Dialogs/Input'
import { useStore } from '../store'
import * as helpers from '../../../helpers'

export default function AddRemoteFileDialog() {
const fetchFile = useStore((state) => state.fetchFile)
const updateState = useStore((state) => state.updateState)
const [textFieldError, setTextFieldError] = React.useState(false)
const [errorHelperText, setErrorHelperText] = React.useState('')

return (
<InputDialog
open={true}
title="Add Remote File"
label="Add"
Icon={UploadIcon}
textFieldError={textFieldError}
errorHelperText={errorHelperText}
description="You are fetching a file. Enter source:"
placholder="Enter or paste a URL"
onCancel={() => updateState({ dialog: undefined })}
onConfirm={async (url) => {
await fetchFile(url)
updateState({ dialog: undefined })
if (url !== '' && helpers.isUrlValid(url)) {
setTextFieldError(false)
await fetchFile(url)
updateState({ dialog: undefined })
} else {
setTextFieldError(true)
if (url === '') {
setErrorHelperText('Enter an URL')
} else if (!helpers.isUrlValid(url)) {
setErrorHelperText('Enter a valid URL')
}
}
}}
/>
)
Expand Down
53 changes: 31 additions & 22 deletions client/components/Controllers/Base/Panels/Source.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,44 @@
import { useTheme } from '@mui/material/styles'
import Box from '@mui/material/Box'
import TextEditor from '../../../Editors/Text'
import * as types from '../../../../types'

export type SourcePanelProps =
| {
json?: false
value?: string
onChange?: (text: string) => void
}
| {
json: true
value?: any
onChange?: (data: any) => void
}
export type SourcePanelProps<T extends string | types.IData> = {
value?: T
language?: string
onChange?: (value: T) => void
}

export function JsonSourcePanel(props: SourcePanelProps<types.IData>) {
const { value, onChange, ...others } = props
return (
<TextSourcePanel
language="json"
value={JSON.stringify(value, null, 2)}
onChange={
onChange
? (text) => {
try {
const data = JSON.parse(text || '{)')
onChange(data)
} catch (error) {}
}
: undefined
}
{...others}
/>
)
}

export default function SourcePanel(props: SourcePanelProps) {
export function TextSourcePanel(props: SourcePanelProps<string>) {
const theme = useTheme()
if (props.value === undefined) return null
return (
<Box>
<TextEditor
value={props.json ? JSON.stringify(props.value, null, 2) : props.value}
onChange={(text) => {
if (props.json) {
try {
text = JSON.parse(text || '{)')
} catch (error) {}
}
if (props.onChange) props.onChange(text || '')
}}
language="json"
value={props.value}
onChange={(value) => props.onChange?.(value || '')}
language={props.language || 'plaintext'}
options={{ readOnly: !props.onChange }}
height={theme.spacing(41)}
/>
Expand Down
6 changes: 0 additions & 6 deletions client/components/Controllers/Chart/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,6 @@ export default function Menu() {
}, [panel])
return (
<menu.MenuBar>
<menu.EditorButton
active={panel === 'editor'}
onClick={() => {
updateState({ panel: panel !== 'editor' ? 'editor' : undefined })
}}
/>
<menu.MetadataButton
active={panel === 'metadata'}
onClick={() =>
Expand Down
5 changes: 2 additions & 3 deletions client/components/Controllers/Chart/Panels/Source.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import SourcePanel from '../../Base/Panels/Source'
import { JsonSourcePanel } from '../../Base/Panels/Source'
import { useStore } from '../store'

export default function Source() {
const modified = useStore((state) => state.modified)
const updateState = useStore((state) => state.updateState)
return (
<SourcePanel
json
<JsonSourcePanel
value={modified}
onChange={(value) => updateState({ modified: value })}
/>
Expand Down
1 change: 0 additions & 1 deletion client/components/Controllers/File/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function Menu() {
const updateState = useStore((state) => state.updateState)
return (
<menu.MenuBar>
<menu.EditorButton disabled />
<menu.MetadataButton
active={panel === 'metadata'}
onClick={() =>
Expand Down
4 changes: 2 additions & 2 deletions client/components/Controllers/File/Panels/Source.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import SourcePanel from '../../Base/Panels/Source'
import { TextSourcePanel } from '../../Base/Panels/Source'
import { useStore } from '../store'

export default function Source() {
const textSource = useStore((state) => state.textSource)
if (!textSource) return null
return <SourcePanel value={textSource} />
return <TextSourcePanel value={textSource} />
}
1 change: 0 additions & 1 deletion client/components/Controllers/Metadata/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export default function Menu() {
const updateState = useStore((state) => state.updateState)
return (
<menu.MenuBar>
<menu.EditorButton enabled />
<menu.MetadataButton enabled />
<menu.ReportButton
disabled={!report || report?.valid}
Expand Down
5 changes: 2 additions & 3 deletions client/components/Controllers/Metadata/Panels/Source.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import SourcePanel from '../../Base/Panels/Source'
import { JsonSourcePanel } from '../../Base/Panels/Source'
import { useStore } from '../store'

export default function Source() {
const modified = useStore((state) => state.modified)
const updateState = useStore((state) => state.updateState)
return (
<SourcePanel
json
<JsonSourcePanel
value={modified}
onChange={(value) => updateState({ modified: value })}
/>
Expand Down
15 changes: 15 additions & 0 deletions client/components/Controllers/Table/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import TableEditor from '../../Editors/Table'
import { useStore } from './store'
import * as React from 'react'

export default function Editor() {
const schema = useStore((state) => state.record?.resource.schema)
Expand All @@ -11,6 +12,17 @@ export default function Editor() {
const saveEditing = useStore((state) => state.saveEditing)
const stopEditing = useStore((state) => state.stopEditing)
const updateState = useStore((state) => state.updateState)
const deleteMultipleCells = useStore((state) => state.deleteMultipleCells)

const [cellSelection, setCellSelection] = React.useState({})

const onKeyDown = React.useCallback(
(event: { key: any }) => {
if (event.key === 'Delete') deleteMultipleCells(cellSelection)
},
[cellSelection]
)

if (!schema) return null
if (!report) return null
return (
Expand All @@ -25,6 +37,9 @@ export default function Editor() {
onEditComplete={saveEditing}
onEditStop={stopEditing}
handle={(gridRef) => updateState({ gridRef })}
onKeyDown={onKeyDown}
defaultCellSelection={cellSelection}
onCellSelectionChange={setCellSelection}
/>
)
}
3 changes: 2 additions & 1 deletion client/components/Controllers/Table/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useStore } from './store'
import * as menu from '../../Parts/Bars/Menu'
import * as helpers from '../../../helpers'
import * as settings from '../../../settings'

export default function Menu() {
Expand All @@ -17,7 +18,6 @@ export default function Menu() {
const redoChange = useStore((state) => state.redoChange)
return (
<menu.MenuBar>
<menu.EditorButton enabled />
<menu.MetadataButton
active={panel === 'metadata'}
onClick={() =>
Expand All @@ -30,6 +30,7 @@ export default function Menu() {
onClick={() => updateState({ panel: panel !== 'report' ? 'report' : undefined })}
/>
<menu.SourceButton
disabled={!helpers.getLanguageByFormat(format)}
active={panel === 'source'}
onClick={() => updateState({ panel: panel !== 'source' ? 'source' : undefined })}
/>
Expand Down
4 changes: 2 additions & 2 deletions client/components/Controllers/Table/Panels/Source.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import SourcePanel from '../../Base/Panels/Source'
import { TextSourcePanel } from '../../Base/Panels/Source'
import { useStore } from '../store'

export default function Source() {
Expand All @@ -10,5 +10,5 @@ export default function Source() {
loadSource().catch(console.error)
}, [record])
if (!source) return null
return <SourcePanel value={source} />
return <TextSourcePanel value={source} />
}
40 changes: 37 additions & 3 deletions client/components/Controllers/Table/store.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import * as React from 'react'
import * as zustand from 'zustand'
import noop from 'lodash/noop'
import mapValues from 'lodash/mapValues'
import isNull from 'lodash/isNull'
import isEqual from 'fast-deep-equal'
import cloneDeep from 'lodash/cloneDeep'
import { createStore } from 'zustand/vanilla'
Expand Down Expand Up @@ -50,13 +52,14 @@ export interface State {

// Editing

initialEditingValue?: any
initialEditingValue?: string | number
// TODO: find proper context type
startEditing: (context: any) => void
saveEditing: (context: any) => void
stopEditing: (context: any) => void
undoChange: () => void
redoChange: () => void
deleteMultipleCells: (cells: object) => Promise<void>
}

export function makeStore(props: TableProps) {
Expand Down Expand Up @@ -158,8 +161,15 @@ export function makeStore(props: TableProps) {
desc: sortInfo?.dir === -1,
})

helpers.applyTableHistory(history, rows)
return { data: rows, count: rowCount || 0 }
// convert null fields in db to empty strings to avoid errors
// in table representation. InovuaDataGrid complains with null values
const rowsNotNull = []
for (const row of rows) {
rowsNotNull.push(mapValues(row, (value) => (isNull(value) ? '' : value)))
}

helpers.applyTableHistory(history, rowsNotNull)
return { data: rowsNotNull, count: rowCount || 0 }
},
toggleErrorMode: async () => {
const { path, client, mode, gridRef } = get()
Expand Down Expand Up @@ -218,6 +228,7 @@ export function makeStore(props: TableProps) {
history.changes.push(change)
undoneHistory.changes = []
set({ history: { ...history } })
gridRef?.current?.reload()
},
stopEditing: () => {
const { gridRef, updateState } = get()
Expand All @@ -240,6 +251,29 @@ export function makeStore(props: TableProps) {
set({ history: { ...history } })
gridRef?.current?.reload()
},
deleteMultipleCells: async (cells: object) => {
const { gridRef, history, undoneHistory } = get()
const grid = gridRef?.current
if (!grid) return

const cellChanges = []

for (const [key] of Object.entries(cells)) {
const row = key.substring(0, key.indexOf(','))
const rowNumber = parseInt(row)
const column = key.substring(key.indexOf(',') + 1, key.length)

cellChanges.push({ rowNumber, fieldName: column, value: '' })
}
const change: types.IChange = {
type: 'multiple-cells-update',
cells: cellChanges,
}
history.changes.push(change)
helpers.applyTableHistory({ changes: [change] }, grid.data)
undoneHistory.changes = []
set({ history: { ...history } })
},
}))
}

Expand Down
1 change: 0 additions & 1 deletion client/components/Controllers/Text/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export default function Menu() {
const maximalVersion = useStore((state) => state.maximalVersion)
return (
<menu.MenuBar>
<menu.EditorButton enabled />
<menu.MetadataButton
active={panel === 'metadata'}
onClick={() =>
Expand Down
Loading

0 comments on commit c7cfa66

Please sign in to comment.