Skip to content

Commit 17775e5

Browse files
committed
Merge remote-tracking branch 'origin/issue-6126' into issue-6126
2 parents ef752a2 + 30ae8de commit 17775e5

File tree

13 files changed

+74
-10
lines changed

13 files changed

+74
-10
lines changed

specifyweb/frontend/js_src/lib/components/WbActions/index.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export function WbActions({
5858
useBooleanState();
5959
const [operationCompleted, openOperationCompleted, closeOperationCompleted] =
6060
useBooleanState();
61+
6162
const { mode, refreshInitiatorAborted, startUpload, triggerStatusComponent } =
6263
useWbActions({
6364
datasetId: dataset.id,
@@ -262,6 +263,10 @@ function useWbActions({
262263
const refreshInitiatorAborted = React.useRef<boolean>(false);
263264
const loading = React.useContext(LoadingContext);
264265

266+
/**
267+
* NOTE: Only validate and upload use startUpload
268+
* For rollback, we directly call the API inside the RollbackConfirmation component
269+
*/
265270
const startUpload = (newMode: WbStatus): void => {
266271
workbench.validation.stopLiveValidation();
267272
loading(

specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export type Dataset = DatasetBase &
7878
readonly uploadplan: UploadPlan | null;
7979
readonly visualorder: RA<number> | null;
8080
readonly isupdate: boolean;
81+
readonly rolledback: boolean;
8182
};
8283

8384
/**

specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanBuilder.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Tables } from '../DataModel/types';
77
import { getTreeDefinitions, isTreeTable } from '../InitialContext/treeRanks';
88
import { defaultColumnOptions } from './linesGetter';
99
import type { BatchEditPrefs } from './Mapper';
10-
import type { SplitMappingPath} from './mappingHelpers';
10+
import type { SplitMappingPath } from './mappingHelpers';
1111
import { valueIsTreeMeta } from './mappingHelpers';
1212
import {
1313
getNameFromTreeDefinitionName,

specifyweb/frontend/js_src/lib/components/WbPlanView/uploadPlanParser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ import type { Tables } from '../DataModel/types';
66
import { softFail } from '../Errors/Crash';
77
import { getTreeDefinitions } from '../InitialContext/treeRanks';
88
import { defaultColumnOptions } from './linesGetter';
9+
import type { BatchEditPrefs, MappingPath } from './Mapper';
910
import type { SplitMappingPath } from './mappingHelpers';
1011
import { formatTreeDefinition } from './mappingHelpers';
1112
import { formatToManyIndex, formatTreeRank } from './mappingHelpers';
1213
import { RANK_KEY_DELIMITER } from './uploadPlanBuilder';
13-
import type { BatchEditPrefs, MappingPath } from './Mapper';
1414

1515
export type MatchBehaviors = 'ignoreAlways' | 'ignoreNever' | 'ignoreWhenBlank';
1616

specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { LocalizedString } from 'typesafe-i18n';
44

55
import { useBooleanState } from '../../hooks/useBooleanState';
66
import { useId } from '../../hooks/useId';
7+
import { batchEditText } from '../../localization/batchEdit';
78
import { commonText } from '../../localization/common';
89
import { StringToJsx } from '../../localization/utils';
910
import { wbText } from '../../localization/workbench';
@@ -376,6 +377,11 @@ export function DataSetName({
376377
{dataset.uploadresult?.success === true && (
377378
<span className="text-red-600">{wbText.dataSetUploadedLabel()}</span>
378379
)}
380+
{dataset.isupdate && dataset.rolledback && (
381+
<span className="text-red-600">
382+
{batchEditText.cannotEditAfterRollback()}
383+
</span>
384+
)}
379385
</h2>
380386
<Button.Small onClick={handleOpen}>
381387
{getField(tables.WorkbenchTemplateMappingItem, 'metaData').label}

specifyweb/frontend/js_src/lib/components/WorkBench/WbSpreadsheet.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ function WbSpreadsheetComponent({
4141
workbench,
4242
mappings,
4343
isResultsOpen,
44+
hasBatchEditRolledBack,
4445
checkDeletedFail,
4546
spreadsheetChanged,
4647
onClickDisambiguate: handleClickDisambiguate,
@@ -53,6 +54,7 @@ function WbSpreadsheetComponent({
5354
readonly workbench: Workbench;
5455
readonly mappings: WbMapping | undefined;
5556
readonly isResultsOpen: boolean;
57+
readonly hasBatchEditRolledBack: boolean;
5658
readonly checkDeletedFail: (statusCode: number) => boolean;
5759
readonly spreadsheetChanged: () => void;
5860
readonly onClickDisambiguate: () => void;
@@ -173,7 +175,7 @@ function WbSpreadsheetComponent({
173175
};
174176

175177
React.useEffect(() => {
176-
if (hot === undefined) return;
178+
if (hot === undefined || hasBatchEditRolledBack) return;
177179
hot.batch(() => {
178180
(mappings === undefined
179181
? Promise.resolve({})

specifyweb/frontend/js_src/lib/components/WorkBench/WbView.tsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -169,9 +169,17 @@ export function WbView({
169169

170170
const searchRef = React.useRef<HTMLInputElement | null>(null);
171171

172+
const hasBatchEditRolledBack = dataset.rolledback && dataset.isupdate;
173+
172174
return (
173175
<ReadOnlyContext.Provider
174-
value={isAlreadyReadOnly || isUploaded || showResults || !canUpdate}
176+
value={
177+
isAlreadyReadOnly ||
178+
isUploaded ||
179+
showResults ||
180+
!canUpdate ||
181+
hasBatchEditRolledBack
182+
}
175183
>
176184
<section
177185
className={`wbs-form ${className.containerFull}`}
@@ -228,6 +236,7 @@ export function WbView({
228236
checkDeletedFail={checkDeletedFail}
229237
data={data}
230238
dataset={dataset}
239+
hasBatchEditRolledBack={hasBatchEditRolledBack}
231240
hot={hot}
232241
isResultsOpen={showResults}
233242
isUploaded={isUploaded}

specifyweb/frontend/js_src/lib/components/WorkBench/hotProps.tsx

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import ReactDOMServer from 'react-dom/server';
33

44
import { wbPlanText } from '../../localization/wbPlan';
55
import { icons } from '../Atoms/Icons';
6+
import { ReadOnlyContext } from '../Core/Contexts';
67
import { TableIcon } from '../Molecules/TableIcon';
78
import { userPreferences } from '../Preferences/userPreferences';
89
import type { Dataset } from '../WbPlanView/Wrapped';
@@ -26,6 +27,7 @@ export function useHotProps({
2627
readonly mappings: WbMapping | undefined;
2728
readonly physicalColToMappingCol: (physicalCol: number) => number | undefined;
2829
}) {
30+
const isReadOnly = React.useContext(ReadOnlyContext);
2931
const [autoWrapCol] = userPreferences.use(
3032
'workBench',
3133
'editor',
@@ -46,12 +48,12 @@ export function useHotProps({
4648
(_, physicalCol) => ({
4749
// Get data from nth column for nth column
4850
data: physicalCol,
49-
readOnly: [-1, undefined].includes(
50-
physicalColToMappingCol(physicalCol)
51-
),
51+
readOnly:
52+
isReadOnly ||
53+
[-1, undefined].includes(physicalColToMappingCol(physicalCol)),
5254
})
5355
),
54-
[dataset.columns.length]
56+
[dataset.columns.length, isReadOnly]
5557
);
5658

5759
const [enterMovesPref] = userPreferences.use(

specifyweb/frontend/js_src/lib/components/WorkBench/resultsParser.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,21 @@ type PropagatedFailure = State<'PropagatedFailure'>;
154154
type MatchedAndChanged = State<'MatchedAndChanged', Omit<Matched, 'type'>>;
155155

156156
type RecordResultTypes =
157-
Deleted | Deleted | FailedBusinessRule | Matched | MatchedAndChanged | MatchedAndChanged | MatchedMultiple | NoChange | NoChange | NoMatch | NullRecord | ParseFailures | PropagatedFailure | Updated | Uploaded;
157+
| Deleted
158+
| Deleted
159+
| FailedBusinessRule
160+
| Matched
161+
| MatchedAndChanged
162+
| MatchedAndChanged
163+
| MatchedMultiple
164+
| NoChange
165+
| NoChange
166+
| NoMatch
167+
| NullRecord
168+
| ParseFailures
169+
| PropagatedFailure
170+
| Updated
171+
| Uploaded;
158172

159173
// Records the specific result of attempting to upload a particular record
160174
type WbRecordResult = {

specifyweb/frontend/js_src/lib/localization/batchEdit.ts

+4
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,8 @@ export const batchEditText = createDictionary({
109109
'en-us':
110110
'Batch Edit is disabled for system tables and scoping hierarchy tables',
111111
},
112+
cannotEditAfterRollback: {
113+
'en-us':
114+
'(Batch Edit datasets cannot be edited after rollback - Read Only)',
115+
},
112116
} as const);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 3.2.15 on 2025-04-17 15:52
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('workbench', '0007_spdatasetattachment'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='spdataset',
15+
name='rolledback',
16+
field=models.BooleanField(default=False, null=True),
17+
),
18+
]

specifyweb/workbench/models.py

+2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class Spdataset(Dataset):
145145
rowresults = models.TextField(null=True)
146146

147147
isupdate = models.BooleanField(default=False, null=True)
148+
rolledback = models.BooleanField(default=False, null=True)
148149

149150
# very complicated. Essentially, each batch-edit dataset gets backed by another dataset (for rollbacks).
150151
# This should be a one-to-one field, imagine the mess otherwise.
@@ -163,6 +164,7 @@ def get_dataset_as_dict(self):
163164
"visualorder": self.visualorder,
164165
"rowresults": self.rowresults and json.loads(self.rowresults),
165166
"isupdate": self.isupdate == True,
167+
"rolledback": self.rolledback == True,
166168
}
167169
)
168170
return ds_dict

specifyweb/workbench/tasks.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,5 @@ def progress(current: int, total: Optional[int]) -> None:
9292
unupload_dataset(ds, agent, progress)
9393

9494
ds.uploaderstatus = None
95-
ds.save(update_fields=['uploaderstatus'])
95+
ds.rolledback = True
96+
ds.save(update_fields=['uploaderstatus', 'rolledback'])

0 commit comments

Comments
 (0)