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

Display a loading bar for the merging process #3864

Merged
merged 36 commits into from Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a570b5d
Display a loading bar for the merging process
CarolineDenis Jul 28, 2023
73b74f8
Call Status in merge component
CarolineDenis Jul 31, 2023
ea2f9d6
In progress
CarolineDenis Jul 31, 2023
ba2a335
Introduce remaining status
CarolineDenis Jul 31, 2023
f9bdff5
Work in progress
CarolineDenis Aug 1, 2023
147161f
Move mergeId setter to parent
CarolineDenis Aug 1, 2023
ff01f05
Remove handleClose from onMerge
CarolineDenis Aug 1, 2023
522f9a9
Modify back end to have access to status
CarolineDenis Aug 1, 2023
1bf0d76
Set status
CarolineDenis Aug 1, 2023
e014af8
Combine states in one state
CarolineDenis Aug 2, 2023
6c8e240
Use destructured object for API response
CarolineDenis Aug 2, 2023
dc45f35
Bring back conditional for array of ids contain less than 2 items
CarolineDenis Aug 2, 2023
9d1adcf
Remove comments
CarolineDenis Aug 2, 2023
d3ae80c
Use destructorCalled
CarolineDenis Aug 2, 2023
5228093
Simplify code
CarolineDenis Aug 2, 2023
52407fe
Narrow down status type
CarolineDenis Aug 2, 2023
c64cf44
Fix back end issue
CarolineDenis Aug 2, 2023
1ecdd23
Add visual loading bar
CarolineDenis Aug 2, 2023
aee2e0b
Comment out code for abort
CarolineDenis Aug 2, 2023
90bbe5f
Modify back end for status
CarolineDenis Aug 2, 2023
4a4c652
refactor(Merging): Clean up status code
maxpatiiuk Aug 3, 2023
8ae8fbc
Export types to new file
CarolineDenis Aug 3, 2023
f3f906a
Merge remote-tracking branch 'origin/v7.9-dev' into merging-progress-bar
CarolineDenis Aug 3, 2023
e20dd88
Add abort button + abort notification
CarolineDenis Aug 3, 2023
257a3fc
Move Status to seprate file
CarolineDenis Aug 3, 2023
7f013a2
Add key to map children
CarolineDenis Aug 3, 2023
7d3013a
Add dimension key to progress dialog to differentiate merging from st…
CarolineDenis Aug 3, 2023
8995d00
Close status dialog and reload query builder after success
CarolineDenis Aug 3, 2023
a162675
Add a close button in status dialog when success
CarolineDenis Aug 4, 2023
8cf24bf
Update merging localized strings
CarolineDenis Aug 4, 2023
f401692
[record-merging] Rerun query after merge has completed
melton-jason Aug 11, 2023
7a2ee15
Display remaining time below loading bar
CarolineDenis Aug 14, 2023
bc9fb10
[record-merging] Create event for record-merging
melton-jason Aug 14, 2023
904ddc9
Revert "[record-merging] Create event for record-merging"
melton-jason Aug 14, 2023
8ae72bc
[record-merging] move event call to Status component
melton-jason Aug 14, 2023
ca751cd
Merge remote-tracking branch 'origin/v7.9-dev' into merging-progress-bar
CarolineDenis Aug 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -24,6 +24,10 @@ import type { Tables } from './types';

// FEATURE: use this everywhere
export const resourceEvents = eventListener<{
readonly merged: [
source: SpecifyResource<AnySchema>,
target: SpecifyResource<AnySchema>
];
readonly deleted: SpecifyResource<AnySchema>;
}>();

Expand Down
Expand Up @@ -161,6 +161,24 @@ export const notificationRenderers: IR<
</>
);
},
'record-merge-aborted'(notification) {
const tableName = notification.payload.table;
const collectionId = parseInt(notification.payload.collection_id);
const mergeName = notification.payload.name;
const collection = userInformation.availableCollections.find(
({ id }) => id === collectionId
);

return (
<>
{mergingText.mergingHasBeenCanceled()}
<div className="flex items-center gap-2">
<TableIcon label name={tableName} />
<p>{`${collection?.collectionName} - ${mergeName}`}</p>
</div>
</>
);
},
'record-merge-succeeded'(notification) {
const id = parseInt(notification.payload.new_record_id);
const tableName = notification.payload.table;
Expand Down
112 changes: 112 additions & 0 deletions specifyweb/frontend/js_src/lib/components/Merging/Status.tsx
@@ -0,0 +1,112 @@
import React from 'react';
import { MergeStatus, StatusState, initialStatusState } from './types';
import { ajax } from '../../utils/ajax';
import { MILLISECONDS } from '../Atoms/timeUnits';
import { softFail } from '../Errors/Crash';
import { LoadingContext } from '../Core/Contexts';
import { Dialog, dialogClassNames } from '../Molecules/Dialog';
import { Button } from '../Atoms/Button';
import { ping } from '../../utils/ajax/ping';
import { mergingText } from '../../localization/merging';
import { Label } from '../Atoms/Form';
import { Progress } from '../Atoms';
import { RemainingLoadingTime } from '../WorkBench/RemainingLoadingTime';
import { commonText } from '../../localization/common';

const statusLocalization = {
FAILED: mergingText.mergeFailed(),
SUCCESS: mergingText.mergeSucceeded(),
PENDING: mergingText.mergePending(),
MERGING: mergingText.merging(),
};

export function Status({
mergingId,
handleClose,
}: {
readonly mergingId: string;
readonly handleClose: () => void;
}): JSX.Element {
const [state, setState] = React.useState<StatusState>(initialStatusState);

React.useEffect(() => {
let destructorCalled = false;
const fetchStatus = () =>
void ajax<{
readonly taskstatus: MergeStatus;
readonly taskprogress: {
readonly total: number;
readonly current: number;
};
readonly taskid: string;
}>(`/api/specify/merge/status/${mergingId}/`, {
// eslint-disable-next-line @typescript-eslint/naming-convention
headers: { Accept: 'application/json' },
})
.then(
({
data: { taskstatus: taskStatus, taskprogress: taskProgress },
}) => {
setState({
status: taskStatus,
total: taskProgress.total,
current: taskProgress.current,
});
if (!destructorCalled)
globalThis.setTimeout(fetchStatus, 2 * MILLISECONDS);
return undefined;
}
)
.catch(softFail);
fetchStatus();
return (): void => {
destructorCalled = true;
};
}, [mergingId]);

const loading = React.useContext(LoadingContext);

return (
<Dialog
buttons={
state.status === 'SUCCESS' ? (
<Button.Danger onClick={handleClose}>
{commonText.close()}
</Button.Danger>
) : (
<Button.Danger
onClick={(): void =>
loading(
ping(`/api/specify/merge/abort/${mergingId}/`, {
method: 'POST',
})
.then(handleClose)
.catch(softFail)
)
}
>
{commonText.cancel()}
</Button.Danger>
)
}
className={{ container: dialogClassNames.narrowContainer }}
dimensionsKey="merging-progress"
header={statusLocalization[state.status]}
onClose={undefined}
>
<Label.Block aria-atomic aria-live="polite" className="gap-2">
<div className="flex flex-col gap-2">
{state.status === 'MERGING' && (
<>
<Progress max={state.total} value={state.current} />
<RemainingLoadingTime
current={state.current}
total={state.total}
/>
</>
)}
</div>
</Label.Block>
</Dialog>
);
}