@@ -231,7 +235,7 @@ export function DataSetsDialog({
|
{
@@ -261,11 +265,11 @@ export function DataSetsDialog({
/>
|
- {canImport && (
+ {canEdit() && (
@@ -283,5 +287,10 @@ export function DataSetsDialog({
export function DataSetsOverlay(): JSX.Element {
const handleClose = React.useContext(OverlayContext);
- return ;
+ return ;
+}
+
+export function BatchEditDataSetsOverlay(): JSX.Element {
+ const handleClose = React.useContext(OverlayContext);
+ return ;
}
diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/WbNoUploadPlan.tsx b/specifyweb/frontend/js_src/lib/components/WbActions/WbNoUploadPlan.tsx
index 9e3cc357c91..51d855e8a03 100644
--- a/specifyweb/frontend/js_src/lib/components/WbActions/WbNoUploadPlan.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbActions/WbNoUploadPlan.tsx
@@ -27,6 +27,7 @@ export function WbNoUploadPlan({
if (
!isUploaded &&
(mappings?.lines ?? []).length === 0 &&
+ // TODO: Not sure about this, since this path will never logically happen for batch-edit
hasPermission('/workbench/dataset', 'upload')
) {
handleOpenNoUploadPlan();
diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/WbRollback.tsx b/specifyweb/frontend/js_src/lib/components/WbActions/WbRollback.tsx
index 7e437f74347..8c1b531fe01 100644
--- a/specifyweb/frontend/js_src/lib/components/WbActions/WbRollback.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbActions/WbRollback.tsx
@@ -2,18 +2,20 @@ import React from 'react';
import { useBooleanState } from '../../hooks/useBooleanState';
import { commonText } from '../../localization/common';
-import { wbText } from '../../localization/workbench';
import { ping } from '../../utils/ajax/ping';
import { Button } from '../Atoms/Button';
import { LoadingContext } from '../Core/Contexts';
import { Dialog, dialogClassNames } from '../Molecules/Dialog';
+import type { WbVariantLocalization } from '../Toolbar/WbsDialog';
import type { WbStatus } from '../WorkBench/WbView';
export function WbRollback({
datasetId,
triggerStatusComponent,
+ viewerLocalization,
}: {
readonly datasetId: number;
+ readonly viewerLocalization: WbVariantLocalization;
readonly triggerStatusComponent: (mode: WbStatus) => void;
}): JSX.Element {
const [confirmRollback, handleOpen, handleClose] = useBooleanState();
@@ -27,11 +29,12 @@ export function WbRollback({
aria-pressed={confirmRollback}
onClick={handleOpen}
>
- {wbText.rollback()}
+ {viewerLocalization.undo}
{confirmRollback && (
@@ -42,10 +45,12 @@ export function WbRollback({
function RollbackConfirmation({
datasetId,
+ viewerLocalization,
onClose: handleClose,
onRollback: handleRollback,
}: {
readonly datasetId: number;
+ readonly viewerLocalization: WbVariantLocalization;
readonly onClose: () => void;
readonly onRollback: () => void;
}): JSX.Element {
@@ -66,17 +71,17 @@ function RollbackConfirmation({
)
}
>
- {wbText.rollback()}
+ {viewerLocalization.undo}
>
}
className={{
container: dialogClassNames.narrowContainer,
}}
- header={wbText.beginRollback()}
+ header={viewerLocalization.undoConfirm}
onClose={handleClose}
>
- {wbText.beginRollbackDescription()}
+ {viewerLocalization.undoStartDescription}
);
}
diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/WbUpload.tsx b/specifyweb/frontend/js_src/lib/components/WbActions/WbUpload.tsx
index 400d3ee468a..d0ee0f984ba 100644
--- a/specifyweb/frontend/js_src/lib/components/WbActions/WbUpload.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbActions/WbUpload.tsx
@@ -5,6 +5,7 @@ import { commonText } from '../../localization/common';
import { wbText } from '../../localization/workbench';
import { Button } from '../Atoms/Button';
import { Dialog } from '../Molecules/Dialog';
+import type { WbVariantLocalization } from '../Toolbar/WbsDialog';
import type { WbCellCounts } from '../WorkBench/CellMeta';
import type { WbMapping } from '../WorkBench/mapping';
import type { WbStatus } from '../WorkBench/WbView';
@@ -15,12 +16,14 @@ export function WbUpload({
openNoUploadPlan,
startUpload,
cellCounts,
+ viewerLocalization,
}: {
readonly hasUnsavedChanges: boolean;
readonly mappings: WbMapping | undefined;
readonly openNoUploadPlan: () => void;
readonly startUpload: (mode: WbStatus) => void;
readonly cellCounts: WbCellCounts;
+ readonly viewerLocalization: WbVariantLocalization;
}): JSX.Element {
const [showUpload, openUpload, closeUpload] = useBooleanState();
@@ -51,7 +54,7 @@ export function WbUpload({
}
onClick={handleUpload}
>
- {wbText.upload()}
+ {viewerLocalization.do}
{showUpload && (
)}
>
diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx b/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx
index f8856a4b1b9..1edf6198780 100644
--- a/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbActions/index.tsx
@@ -10,8 +10,9 @@ import { Button } from '../Atoms/Button';
import { LoadingContext } from '../Core/Contexts';
import { ErrorBoundary } from '../Errors/ErrorBoundary';
import { Dialog } from '../Molecules/Dialog';
-import { hasPermission } from '../Permissions/helpers';
+import type { WbVariantLocalization } from '../Toolbar/WbsDialog';
import type { Dataset, Status } from '../WbPlanView/Wrapped';
+import { resolveVariantFromDataset } from '../WbUtils/datasetVariants';
import type { WbCellCounts } from '../WorkBench/CellMeta';
import type { WbMapping } from '../WorkBench/mapping';
import { CreateRecordSetButton } from '../WorkBench/RecordSet';
@@ -66,7 +67,13 @@ export function WbActions({
workbench,
});
- const message = mode === undefined ? undefined : getMessage(cellCounts, mode);
+ const variant = resolveVariantFromDataset(dataset);
+ const viewerLocalization: WbVariantLocalization = variant.localization.viewer;
+
+ const message =
+ mode === undefined
+ ? undefined
+ : getMessage(cellCounts, mode, viewerLocalization);
const isMapped = mappings !== undefined;
@@ -85,7 +92,7 @@ export function WbActions({
onCloseNoUploadPlan={closeNoUploadPlan}
onOpenNoUploadPlan={openNoUploadPlan}
/>
- {!isUploaded && hasPermission('/workbench/dataset', 'validate') ? (
+ {!isUploaded && variant.canValidate() ? (
- {isUploaded && hasPermission('/workbench/dataset', 'unupload') ? (
+ {isUploaded && variant.canUndo() ? (
) : undefined}
- {!isUploaded && hasPermission('/workbench/dataset', 'upload') ? (
+ {!isUploaded && variant.canDo() ? (
) : undefined}
- {!isUploaded && hasPermission('/workbench/dataset', 'update') ? (
+ {!isUploaded && variant.canEdit() ? (
<>
{
refreshInitiatorAborted.current = false;
@@ -212,16 +222,16 @@ export function WbActions({
mode === 'validate'
? wbText.validationCanceled()
: mode === 'unupload'
- ? wbText.rollbackCanceled()
- : wbText.uploadCanceled()
+ ? wbText.rollbackCanceled()
+ : viewerLocalization.doCancelled
}
onClose={closeAbortedMessage}
>
{mode === 'validate'
? wbText.validationCanceledDescription()
: mode === 'unupload'
- ? wbText.rollbackCanceledDescription()
- : wbText.uploadCanceledDescription()}
+ ? wbText.rollbackCanceledDescription()
+ : viewerLocalization.doCancelledDescription}
)}
>
@@ -289,7 +299,8 @@ function useWbActions({
function getMessage(
cellCounts: WbCellCounts,
- mode: WbStatus
+ mode: WbStatus,
+ viewerLocalization: WbVariantLocalization
): {
readonly header: LocalizedString;
readonly message: JSX.Element | LocalizedString;
@@ -322,23 +333,25 @@ function getMessage(
upload:
cellCounts.invalidCells === 0
? {
- header: wbText.uploadSuccessful(),
- message: wbText.uploadSuccessfulDescription(),
+ header: viewerLocalization.do,
+ message: viewerLocalization.doSuccessfulDescription,
}
: {
- header: wbText.uploadErrors(),
+ header: viewerLocalization.doErrors,
message: (
<>
- {wbText.uploadErrorsDescription()}
+ {viewerLocalization.doErrorsDescription}
- {wbText.uploadErrorsSecondDescription()}
+ {wbText.uploadErrorsSecondDescription({
+ type: viewerLocalization.do,
+ })}
>
),
},
unupload: {
header: wbText.dataSetRollback(),
- message: wbText.dataSetRollbackDescription(),
+ message: viewerLocalization.undoFinishedDescription,
},
};
diff --git a/specifyweb/frontend/js_src/lib/components/WbActions/useResults.ts b/specifyweb/frontend/js_src/lib/components/WbActions/useResults.ts
index 19212389933..d36e619ff8d 100644
--- a/specifyweb/frontend/js_src/lib/components/WbActions/useResults.ts
+++ b/specifyweb/frontend/js_src/lib/components/WbActions/useResults.ts
@@ -42,9 +42,10 @@ export function useResults({
const colsToInclude = new Set();
Object.entries(workbench.cells.cellMeta).forEach(([physicalRow, rowMeta]) =>
rowMeta.forEach((metaArray, physicalCol) => {
- if (!workbench.cells.getCellMetaFromArray(metaArray, 'isNew')) return;
- rowsToInclude.add(f.fastParseInt(physicalRow));
- colsToInclude.add(physicalCol);
+ if (workbench.cells.isResultCell(metaArray)) {
+ rowsToInclude.add(f.fastParseInt(physicalRow));
+ colsToInclude.add(physicalCol);
+ }
})
);
const rowsToHide = workbench.data
diff --git a/specifyweb/frontend/js_src/lib/components/WbImport/helpers.ts b/specifyweb/frontend/js_src/lib/components/WbImport/helpers.ts
index df6c0fc9559..b12c1c8b82c 100644
--- a/specifyweb/frontend/js_src/lib/components/WbImport/helpers.ts
+++ b/specifyweb/frontend/js_src/lib/components/WbImport/helpers.ts
@@ -13,6 +13,7 @@ import { tables } from '../DataModel/tables';
import { fileToText } from '../Molecules/FilePicker';
import { uniquifyHeaders } from '../WbPlanView/headerHelper';
import type { Dataset, DatasetBrief } from '../WbPlanView/Wrapped';
+import { datasetVariants } from '../WbUtils/datasetVariants';
/**
* REFACTOR: add this ESLint rule:
@@ -160,9 +161,9 @@ const MAX_NAME_LENGTH = 64;
export async function uniquifyDataSetName(
name: string,
currentDataSetId?: number,
- datasetsUrl = '/api/workbench/dataset/'
+ datasetsUrl: keyof typeof datasetVariants = 'workbench'
): Promise {
- return ajax>(datasetsUrl, {
+ return ajax>(datasetVariants[datasetsUrl].fetchUrl, {
headers: { Accept: 'application/json' },
}).then(({ data: datasets }) =>
getUniqueName(
diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/State.tsx b/specifyweb/frontend/js_src/lib/components/WbPlanView/State.tsx
index 4839b212ff7..792f0759dbf 100644
--- a/specifyweb/frontend/js_src/lib/components/WbPlanView/State.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/State.tsx
@@ -9,7 +9,7 @@ import { Button } from '../Atoms/Button';
import { LoadingContext } from '../Core/Contexts';
import type { Tables } from '../DataModel/types';
import { Dialog, dialogClassNames } from '../Molecules/Dialog';
-import { DataSetsDialog } from '../Toolbar/WbsDialog';
+import { GenericDataSetsDialog } from '../Toolbar/WbsDialog';
import { ListOfBaseTables } from './Components';
import type { UploadPlan } from './uploadPlanParser';
import type { Dataset } from './Wrapped';
@@ -38,8 +38,8 @@ function TemplateSelection({
{wbPlanText.invalidTemplatePlan()}
)}
-
loading(
diff --git a/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx b/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx
index 4f6e50edefa..ece4f0c92d5 100644
--- a/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbPlanView/Wrapped.tsx
@@ -77,6 +77,7 @@ export type Dataset = DatasetBase &
readonly rows: RA>;
readonly uploadplan: UploadPlan | null;
readonly visualorder: RA | null;
+ readonly isupdate: boolean;
};
/**
diff --git a/specifyweb/frontend/js_src/lib/components/WbToolkit/index.tsx b/specifyweb/frontend/js_src/lib/components/WbToolkit/index.tsx
index 7186f342011..c6689acc939 100644
--- a/specifyweb/frontend/js_src/lib/components/WbToolkit/index.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbToolkit/index.tsx
@@ -7,9 +7,10 @@ import type { RA } from '../../utils/types';
import { Button } from '../Atoms/Button';
import { raise } from '../Errors/Crash';
import { ErrorBoundary } from '../Errors/ErrorBoundary';
-import { hasPermission, hasTablePermission } from '../Permissions/helpers';
+import { hasTablePermission } from '../Permissions/helpers';
import { userPreferences } from '../Preferences/userPreferences';
import type { Dataset } from '../WbPlanView/Wrapped';
+import { resolveVariantFromDataset } from '../WbUtils/datasetVariants';
import { downloadDataSet } from '../WorkBench/helpers';
import type { WbMapping } from '../WorkBench/mapping';
import { WbChangeOwner } from './ChangeOwner';
@@ -57,14 +58,15 @@ export function WbToolkit({
const hasLocality =
mappings === undefined ? false : mappings.localityColumns.length > 0;
+ const variant = resolveVariantFromDataset(dataset);
+
return (
- {hasPermission('/workbench/dataset', 'transfer') &&
- hasTablePermission('SpecifyUser', 'read') ? (
+ {variant.canTransfer() && hasTablePermission('SpecifyUser', 'read') ? (
- {hasPermission('/workbench/dataset', 'update') && (
+ {variant.canUpdate() && (
<>
@@ -102,7 +103,7 @@ export function Navigation({
hasPermission('/workbench/dataset', 'create'),
+ canEdit: () => hasPermission('/workbench/dataset', 'update'),
+ route: (id: number) => `/specify/workbench/${id}`,
+ metaRoute: (id: number) => `/specify/overlay/workbench/${id}/meta/`,
+ canCreate: () => hasPermission('/workbench/dataset', 'create'),
+ canTransfer: () => hasPermission('/workbench/dataset', 'transfer'),
+ canUpdate: () => hasPermission('/workbench/dataset', 'update'),
+ canDo: () => hasPermission('/workbench/dataset', 'upload'),
+ canUndo: () => hasPermission('/workbench/dataset', 'unupload'),
+ canValidate: () => hasPermission('/workbench/dataset', 'validate'),
+ localization: {
+ datasetsDialog: {
+ header: (count: number) =>
+ commonText.countLine({
+ resource: wbText.dataSets({ variant: wbText.workBench() }),
+ count,
+ }),
+ empty: () =>
+ `${wbText.wbsDialogEmpty()} ${
+ hasPermission('/workbench/dataset', 'create')
+ ? wbText.createDataSetInstructions()
+ : ''
+ }`,
+ },
+ viewer: {
+ do: wbText.upload(),
+ doStart: wbText.startUpload(),
+ doErrors: wbText.uploadErrors(),
+ doCancelled: wbText.uploadCanceled(),
+ doCancelledDescription: wbText.uploadCanceledDescription(),
+ doStartDescription: wbText.startUploadDescription(),
+ doErrorsDescription: wbText.uploadErrorsDescription(),
+ undo: wbText.rollback(),
+ undoConfirm: wbText.beginRollback(),
+ undoStartDescription: wbText.beginRollbackDescription(),
+ doSuccessfulDescription: wbText.uploadSuccessfulDescription(),
+ undoFinishedDescription: wbText.dataSetRollbackDescription(),
+ doing: wbText.uploading(),
+ doStatus: wbText.wbStatusUpload(),
+ doSuccessful: wbText.uploadSuccessful(),
+ },
+ },
+ documentationUrl:
+ 'https://discourse.specifysoftware.org/t/the-specify-7-workbench/540',
+} as const;
+
+// Defines a shared interface to access dataset variants
+export const datasetVariants = {
+ workbench: baseWbVariant,
+ workbenchChoosePlan: {
+ ...baseWbVariant,
+ fetchUrl: '/api/workbench/dataset/?with_plan',
+ sortConfig: baseWbVariant.sortConfig,
+ canImport: () => false,
+ canEdit: () => false,
+ localization: {
+ datasetsDialog: {
+ header: () => wbPlanText.copyPlan(),
+ empty: () => wbPlanText.noPlansToCopyFrom(),
+ },
+ },
+ },
+ batchEdit: {
+ ...baseWbVariant,
+ fetchUrl: '/api/workbench/dataset/?isupdate=1',
+ sortConfig: {
+ key: 'listOfBatchEditDataSets',
+ field: 'name',
+ },
+ // Cannot import via the header
+ canImport: () => false,
+ header: (count: number) =>
+ commonText.countLine({
+ resource: wbText.dataSets({ variant: batchEditText.batchEdit() }),
+ count,
+ }),
+ canEdit: () => hasPermission('/batch_edit/dataset', 'update'),
+ canCreate: () => hasPermission('/batch_edit/dataset', 'create'),
+ canTransfer: () => hasPermission('/batch_edit/dataset', 'transfer'),
+ canDo: () => hasPermission('/batch_edit/dataset', 'commit'),
+ canUndo: () =>
+ userPreferences.get('batchEdit', 'editor', 'showRollback') &&
+ hasPermission('/batch_edit/dataset', 'rollback'),
+ canValidate: () => hasPermission('/batch_edit/dataset', 'validate'),
+ localization: {
+ datasetsDialog: {
+ header: (count: number) =>
+ commonText.countLine({
+ resource: wbText.dataSets({ variant: batchEditText.batchEdit() }),
+ count,
+ }),
+ empty: () =>
+ `${wbText.wbsDialogEmpty()} ${
+ hasPermission('/batch_edit/dataset', 'create')
+ ? batchEditText.createUpdateDataSetInstructions()
+ : ''
+ }`,
+ },
+ viewer: {
+ do: batchEditText.commit(),
+ doStart: batchEditText.startCommit(),
+ doErrors: batchEditText.commitErrors(),
+ doCancelled: batchEditText.commitCancelled(),
+ doErrorsDescription: batchEditText.commitErrorsDescription(),
+ doStartDescription: batchEditText.startCommitDescription(),
+ doCancelledDescription: batchEditText.commitCancelledDescription(),
+ undo: wbText.rollback(),
+ undoConfirm: wbText.beginRollback(),
+ undoStartDescription: batchEditText.startRevertDescription(),
+ doSuccessfulDescription: batchEditText.commitSuccessfulDescription(),
+ undoFinishedDescription: batchEditText.dateSetRevertDescription(),
+ doing: batchEditText.committing(),
+ doStatus: batchEditText.beStatusCommit(),
+ doSuccessful: batchEditText.commitSuccessful(),
+ },
+ },
+ documentationUrl: 'https://discourse.specifysoftware.org/t/batch-edit/2248',
+ },
+ bulkAttachment: {
+ fetchUrl: '/attachment_gw/dataset/',
+ sortConfig: {
+ key: 'attachmentDatasets',
+ field: 'name',
+ },
+ canImport: () => hasPermission('/attachment_import/dataset', 'create'),
+ header: f.never,
+ onEmpty: f.never,
+ canEdit: () => hasPermission('/attachment_import/dataset', 'update'),
+ route: (id: number) => `/specify/attachments/import/${id}`,
+ // Actually, in retrorespect, this would be a nice feature
+ metaRoute: f.never,
+ documentationUrl:
+ 'https://discourse.specifysoftware.org/t/batch-attachment-uploader/1374',
+ },
+} as const;
+
+export const resolveVariantFromDataset = (dataset: Dataset) =>
+ datasetVariants[dataset.isupdate ? 'batchEdit' : 'workbench'];
diff --git a/specifyweb/frontend/js_src/lib/components/WbUtils/index.tsx b/specifyweb/frontend/js_src/lib/components/WbUtils/index.tsx
index 295e8d1c437..9ab9f0ae0f5 100644
--- a/specifyweb/frontend/js_src/lib/components/WbUtils/index.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WbUtils/index.tsx
@@ -14,6 +14,7 @@ import type { WbUtils } from './Utils';
export function WbUtilsComponent({
isUploaded,
+ isUpdate,
cellCounts,
utils,
cells,
@@ -21,6 +22,7 @@ export function WbUtilsComponent({
searchRef,
}: {
readonly isUploaded: boolean;
+ readonly isUpdate: boolean;
readonly cellCounts: WbCellCounts;
readonly utils: WbUtils;
readonly cells: WbCellMeta;
@@ -127,6 +129,28 @@ export function WbUtilsComponent({
totalCount={cellCounts.newCells}
utils={utils}
/>
+ {
+ // Only show these cells if batch-edit
+ isUpdate === true && <>
+
+
+ >
+ }
{!isUploaded && (
;
readonly originalValue: string | undefined;
+ readonly isUpdated: boolean;
+ readonly isDeleted: boolean;
+ readonly isMatchedAndChanged: boolean;
};
export type WbCellCounts = {
@@ -26,6 +32,9 @@ export type WbCellCounts = {
readonly invalidCells: number;
readonly searchResults: number;
readonly modifiedCells: number;
+ readonly updatedCells: number;
+ readonly deletedCells: number;
+ readonly matchedAndChangedCells: number;
};
// REFACTOR: replace usages of WbMetaArray with WbMeta and test performance/memory
@@ -36,6 +45,9 @@ export type WbMetaArray = [
isSearchResult: boolean,
issues: RA,
originalValue: string | undefined,
+ isUpdated: boolean,
+ isDeleted: boolean,
+ isMatchedAndChanged: boolean,
];
const defaultMetaValues = Object.freeze([
@@ -44,6 +56,9 @@ const defaultMetaValues = Object.freeze([
false,
Object.freeze([]),
undefined,
+ false,
+ false,
+ false,
] as const);
/* eslint-disable functional/no-this-expression */
@@ -100,7 +115,14 @@ export class WbCellMeta {
const metaValueChanged =
issuesChanged ||
cellValueChanged ||
- (['isNew', 'isModified', 'isSearchResult'].includes(key) &&
+ ([
+ 'isNew',
+ 'isModified',
+ 'isSearchResult',
+ 'isUpdated',
+ 'isDeleted',
+ 'isMatchedAndChanged',
+ ].includes(key) &&
currentValue !== value);
if (!metaValueChanged) return false;
@@ -195,8 +217,16 @@ export class WbCellMeta {
if (key === 'isNew')
cell?.classList[value === true ? 'add' : 'remove']('wb-no-match-cell');
+ else if (key === 'isUpdated')
+ cell?.classList[value === true ? 'add' : 'remove']('wb-updated-cell');
+ else if (key === 'isDeleted')
+ cell?.classList[value === true ? 'add' : 'remove']('wb-deleted-cell');
else if (key === 'isModified')
cell?.classList[value === true ? 'add' : 'remove']('wb-modified-cell');
+ else if (key === 'isMatchedAndChanged')
+ cell?.classList[value === true ? 'add' : 'remove'](
+ 'wb-matched-and-changed-cell'
+ );
else if (key === 'isSearchResult')
cell?.classList[value === true ? 'add' : 'remove'](
'wb-search-match-cell'
@@ -347,6 +377,22 @@ export class WbCellMeta {
count + (this.getCellMetaFromArray(info, 'isModified') ? 1 : 0),
0
),
+ updatedCells: cellMeta.reduce(
+ (count, info) =>
+ count + (this.getCellMetaFromArray(info, 'isUpdated') ? 1 : 0),
+ 0
+ ),
+ deletedCells: cellMeta.reduce(
+ (count, info) =>
+ count + (this.getCellMetaFromArray(info, 'isDeleted') ? 1 : 0),
+ 0
+ ),
+ matchedAndChangedCells: cellMeta.reduce(
+ (count, info) =>
+ count +
+ (this.getCellMetaFromArray(info, 'isMatchedAndChanged') ? 1 : 0),
+ 0
+ ),
});
}
@@ -364,10 +410,28 @@ export class WbCellMeta {
case 'searchResults': {
return this.getCellMetaFromArray(metaArray, 'isSearchResult');
}
+ case 'updatedCells': {
+ return this.getCellMetaFromArray(metaArray, 'isUpdated');
+ }
+ case 'deletedCells': {
+ return this.getCellMetaFromArray(metaArray, 'isDeleted');
+ }
+ case 'matchedAndChangedCells': {
+ return this.getCellMetaFromArray(metaArray, 'isMatchedAndChanged');
+ }
default: {
return false;
}
}
}
+
+ public isResultCell(metaArray: WbMetaArray): boolean {
+ return (
+ this.cellIsType(metaArray, 'newCells') ||
+ this.cellIsType(metaArray, 'updatedCells') ||
+ this.cellIsType(metaArray, 'deletedCells') ||
+ this.cellIsType(metaArray, 'matchedAndChangedCells')
+ );
+ }
}
/* eslint-enable functional/no-this-expression */
diff --git a/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx b/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx
index 1d0f5c41944..f2097e632ed 100644
--- a/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx
+++ b/specifyweb/frontend/js_src/lib/components/WorkBench/DataSetMeta.tsx
@@ -29,6 +29,7 @@ import { hasPermission } from '../Permissions/helpers';
import { unsafeNavigate } from '../Router/Router';
import { getMaxDataSetLength, uniquifyDataSetName } from '../WbImport/helpers';
import type { Dataset } from '../WbPlanView/Wrapped';
+import { datasetVariants } from '../WbUtils/datasetVariants';
const syncNameAndRemarks = async (
name: LocalizedString,
@@ -43,7 +44,7 @@ const syncNameAndRemarks = async (
type DataSetMetaProps = {
readonly dataset: Dataset | EagerDataSet;
- readonly datasetUrl: '/api/workbench/dataset/' | '/attachment_gw/dataset/';
+ readonly datasetVariant: keyof typeof datasetVariants;
readonly getRowCount?: () => number;
readonly permissionResource:
| '/attachment_import/dataset'
@@ -65,7 +66,7 @@ type DataSetMetaProps = {
export function WbDataSetMeta(
props: Omit<
DataSetMetaProps,
- 'datasetUrl' | 'deleteDescription' | 'onChange' | 'permissionResource'
+ 'datasetVariant' | 'deleteDescription' | 'onChange' | 'permissionResource'
> & {
readonly onChange: ({
name,
@@ -80,7 +81,7 @@ export function WbDataSetMeta(
return (
@@ -101,7 +102,7 @@ export const blueTable = {icons.table};
export function DataSetMeta({
dataset,
getRowCount = (): number => dataset.rows.length,
- datasetUrl,
+ datasetVariant,
permissionResource,
deleteDescription,
onClose: handleClose,
@@ -118,6 +119,8 @@ export function DataSetMeta({
const [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);
+ const datasetUrl = datasetVariants[datasetVariant];
+
return isDeleted ? (
|