Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix saving errored tables #648

Merged
merged 29 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ef2571f
Separate Upload components for maintainablity; fixed tabs height
roll Nov 6, 2024
b860c0a
Implemented loading state for file uploading
roll Nov 6, 2024
a5dbcf4
Implemented UploadFiles state management
roll Nov 12, 2024
94906f7
Added component state
roll Nov 12, 2024
7093e30
Normalized asset locations
roll Nov 12, 2024
0e80f50
Disabled controls while busy
roll Nov 12, 2024
a9d49a7
Recovered folder uploading
roll Nov 12, 2024
686a1a3
Merged local files button
roll Nov 12, 2024
2209848
Recovered remote file
roll Nov 12, 2024
a58702f
Rebased on progress
roll Nov 12, 2024
b751b75
Removed unused create actions
roll Nov 12, 2024
f511309
Fixed loading message
roll Nov 12, 2024
666a9d3
Return file size from the server
roll Nov 12, 2024
975b593
Added large file size message
roll Nov 12, 2024
7909ebc
Removed unused dialog
roll Nov 12, 2024
850f506
Removed unused AdjustFile dialog
roll Nov 12, 2024
b19a673
Removed unused IndexFiles dialog
roll Nov 12, 2024
ae63ad9
Removed unused file actions
roll Nov 12, 2024
5f25150
Validating -> Checking Errors
roll Nov 13, 2024
ae651ad
Merge branch 'main' into 622/large-files
roll Nov 14, 2024
7d6ec12
Fixed merging
roll Nov 14, 2024
09e74ce
Removed unused toPath prop from table/patch
roll Nov 14, 2024
74a4093
Maked table/patch atomic action
roll Nov 14, 2024
d39b75e
Add SaveChanges process indication
roll Nov 14, 2024
779aaf7
Added TODO
roll Nov 14, 2024
15483ef
Extracted updated_cells
roll Nov 18, 2024
cd6bd18
Reconstruct previous errors
roll Nov 18, 2024
8741303
Fixed table saving shortcut
roll Nov 18, 2024
c7216b1
Merge branch 'main' into 614/fix-saving-errored-tables
roll Nov 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,6 @@ export class Client {

async tablePatch(props: {
path: string
toPath?: string
history?: types.IHistory
resource?: types.IResource
}) {
Expand Down
2 changes: 2 additions & 0 deletions client/components/Application/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FileUploadDialog } from './Dialogs/FileUpload'
import OpenLocationDialog from './Dialogs/OpenLocation'
import PublishDialog from './Dialogs/Publish'
import RenameFileDialog from './Dialogs/RenameFile'
import { SaveChangesDialog } from './Dialogs/SaveChanges'
import UnsavedChangesDialog from './Dialogs/UnsavedChanges'
import WelcomeBannerDialog from './Dialogs/WelcomeBanner'

Expand Down Expand Up @@ -37,4 +38,5 @@ const DIALOGS = {
fileUpload: FileUploadDialog,
openLocation: OpenLocationDialog,
renameFile: RenameFileDialog,
saveChanges: SaveChangesDialog,
} as const
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { client } from '@client/client'
import * as helpers from '@client/helpers'
import * as appStore from '@client/store'

// We use component level state because dialog state
// needs to be shared between multiple components
// but it is not needed in the global state
class State {
progress?: IProgress
}

type IProgress = {
type: 'loading' | 'error'
title?: string
message?: string
blocking?: boolean
}

export const { state, useState } = helpers.createState('SaveChangesDialog', new State())

export async function saveChanges() {
const { grid } = appStore.getRefs()
const { path, resource, table } = appStore.getState()
if (!path || !grid || !table) return

appStore.openDialog('saveChanges')
state.progress = {
type: 'loading',
title: 'Saving the updated table',
message: 'If the file is large, this may take some time...',
blocking: true,
}

const appState = appStore.getState()
const isTableUpdated = appStore.getIsTableUpdated(appState)
const isResourceUpdated = appState.isResourceUpdated

const result = await client.tablePatch({
path,
history: isTableUpdated ? table.history : undefined,
resource: isResourceUpdated ? resource : undefined,
})

if (result instanceof client.Error) {
state.progress = {
type: 'error',
title: 'Error saving changes',
message: result.detail,
}
}

await appStore.onFileUpdated([path])
grid.reload()

state.progress = undefined
closeDialog()
}

export function closeDialog() {
if (!state.progress?.blocking) {
appStore.closeDialog()
}
}
47 changes: 47 additions & 0 deletions client/components/Application/Dialogs/SaveChanges/SaveChanges.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import NoButtonDialog from '@client/components/Parts/Dialogs/NoButton'
import Box from '@mui/material/Box'
import LinearProgress from '@mui/material/LinearProgress'
import Stack from '@mui/material/Stack'
import { startCase } from 'lodash'
import * as store from './SaveChanges.store'

export function SaveChangesDialog() {
return (
<NoButtonDialog
open={true}
maxWidth="md"
title="Saving Changes"
onClose={store.closeDialog}
>
<ProgressIndicator />
</NoButtonDialog>
)
}

// TODO: move to common components
function ProgressIndicator() {
const { progress } = store.useState()

if (!progress) {
return null
}

if (progress.type === 'error') {
return <Box sx={{ color: 'red' }}>{progress.message}</Box>
}

return (
<Stack spacing={1}>
<Box>{progress.title || startCase(progress.type)}...</Box>
<LinearProgress
sx={{
'& .MuiLinearProgress-bar': {
backgroundColor: '#00D1FF',
},
padding: '10px',
}}
/>
<Box sx={{ color: 'gray' }}>{progress.message}</Box>
</Stack>
)
}
2 changes: 2 additions & 0 deletions client/components/Application/Dialogs/SaveChanges/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { SaveChangesDialog } from './SaveChanges'
export * as saveChangesDialog from './SaveChanges.store'
11 changes: 6 additions & 5 deletions client/components/Controllers/Table/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import TableEditor from '../../Editors/Table'
import { saveChangesDialog } from '@client/components/Application/Dialogs/SaveChanges'
import * as store from '@client/store'
import * as types from '@client/types'
import { ClickAwayListener } from '@mui/base'
import Box from '@mui/material/Box'
import * as React from 'react'
import { useKeyPress } from 'ahooks'
import * as store from '@client/store'
import * as types from '@client/types'
import * as React from 'react'
import TableEditor from '../../Editors/Table'

export default function Editor() {
const schema = store.useStore((state) => state.record?.resource.schema)
Expand Down Expand Up @@ -40,7 +41,7 @@ export default function Editor() {
})

useKeyPress(['ctrl+s', 'meta+s'], () => {
store.saveTable()
saveChangesDialog.saveChanges()
})

// Ensure that when the user interact with other parts on the application
Expand Down
8 changes: 6 additions & 2 deletions client/components/Controllers/Table/Menu.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as menu from '../../Parts/Bars/Menu'
import { saveChangesDialog } from '@client/components/Application/Dialogs/SaveChanges'
import * as store from '@client/store'
import Box from '@mui/material/Box'
import * as action from '../../Parts/Bars/Action'
import * as menu from '../../Parts/Bars/Menu'

export default function Menu() {
const panel = store.useStore((state) => state.panel)
Expand Down Expand Up @@ -56,7 +57,10 @@ export default function Menu() {
disabled={isTableUpdated}
onClick={() => store.openDialog('publish')}
/>
<action.SaveButton updated={isTableUpdated} onClick={store.saveTable} />
<action.SaveButton
updated={isTableUpdated}
onClick={saveChangesDialog.saveChanges}
/>
</Box>
</Box>
</menu.MenuBar>
Expand Down
48 changes: 48 additions & 0 deletions client/components/Parts/Dialogs/NoButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import CloseIcon from '@mui/icons-material/Close'
import Dialog, { DialogProps } from '@mui/material/Dialog'
import DialogContent from '@mui/material/DialogContent'
import DialogTitle from '@mui/material/DialogTitle'
import IconButton from '@mui/material/IconButton'

export default function NoButtonDialog(
props: DialogProps & { title: string; onClose: () => void }
) {
const handleClose = () => {
props.onClose()
}

return (
<Dialog
fullWidth
maxWidth={props.maxWidth}
open={!!props.open}
onClose={props.onClose}
aria-labelledby={props.title}
>
<IconButton
aria-label="close"
onClick={handleClose}
sx={{
position: 'absolute',
right: 8,
top: 8,
color: (theme) => theme.palette.grey[500],
}}
>
<CloseIcon />
</IconButton>
<DialogTitle
id="dialog-title"
sx={{
paddingBottom: 1,
marginBottom: 2,
borderBottom: 'solid 1px #ddd',
backgroundColor: (theme) => theme.palette.OKFNGray100.main,
}}
>
{props.title}
</DialogTitle>
<DialogContent>{props.children}</DialogContent>
</Dialog>
)
}
5 changes: 3 additions & 2 deletions client/store/actions/file.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { client } from '@client/client'
import { saveChangesDialog } from '@client/components/Application/Dialogs/SaveChanges'
import * as helpers from '@client/helpers'
import * as settings from '@client/settings'
import { cloneDeep } from 'lodash'
Expand All @@ -7,7 +8,7 @@ import * as store from '../store'
import { openDialog } from './dialog'
import { emitEvent } from './event'
import { loadSource } from './source'
import { closeTable, getIsTableUpdated, openTable, revertTable, saveTable } from './table'
import { closeTable, getIsTableUpdated, openTable, revertTable } from './table'
import { closeText, getIsTextUpdated, openText, revertText, saveText } from './text'

export async function loadFiles(throwError?: boolean) {
Expand Down Expand Up @@ -141,7 +142,7 @@ export async function saveFile() {
invariant(record)

if (record.type === 'table') {
await saveTable()
await saveChangesDialog.saveChanges()
} else if (record.type === 'text') {
await saveText()
}
Expand Down
56 changes: 5 additions & 51 deletions client/store/actions/table.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { client } from '@client/client'
import invariant from 'tiny-invariant'
import { mapValues, isNull } from 'lodash'
import { onFileCreated, onFileUpdated } from './file'
import { cloneDeep } from 'lodash'
import { revertResource } from './resource'
import { getRefs } from './refs'
import * as helpers from '@client/helpers'
import * as settings from '@client/settings'
import * as types from '@client/types'
import { cloneDeep, isNull, mapValues } from 'lodash'
import invariant from 'tiny-invariant'
import * as store from '../store'
import { onFileUpdated } from './file'
import { getRefs } from './refs'
import { revertResource } from './resource'

export async function openTable() {
const { path, record } = store.getState()
Expand Down Expand Up @@ -36,26 +35,6 @@ export async function closeTable() {
})
}

export async function forkTable(toPath: string) {
const { path, table, resource } = store.getState()
if (!path || !table) return

const result = await client.tablePatch({
path,
toPath,
history: table.history,
resource,
})

if (result instanceof client.Error) {
return store.setState('fork-table-error', (state) => {
state.error = result
})
}

await onFileCreated([result.path])
}

export async function publishTable(control: types.IControl) {
const { record } = store.getState()

Expand Down Expand Up @@ -93,31 +72,6 @@ export async function revertTable() {
revertResource()
}

export async function saveTable() {
const { grid } = getRefs()
const { path, resource, table } = store.getState()
if (!path || !grid || !table) return

const state = store.getState()
const isTableUpdated = getIsTableUpdated(state)
const isResourceUpdated = state.isResourceUpdated

const result = await client.tablePatch({
path,
history: isTableUpdated ? table.history : undefined,
resource: isResourceUpdated ? resource : undefined,
})

if (result instanceof client.Error) {
return store.setState('save-table-error', (state) => {
state.error = result
})
}

await onFileUpdated([path])
grid.reload()
}

export function setTableSelection(selection: types.ITableSelection) {
store.setState('set-table-selection', (state) => {
state.table!.selection = selection
Expand Down
1 change: 1 addition & 0 deletions client/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './actions'
export { getRefs } from './actions/refs'
export * from './state'
export { getState, useStore } from './store'
1 change: 1 addition & 0 deletions client/store/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export type IDialog =
| 'fileUpload'
| 'openLocation'
| 'renameFile'
| 'saveChanges'

export type IPanel = 'metadata' | 'report' | 'changes' | 'source'

Expand Down
Loading
Loading