Skip to content

Commit 87182f0

Browse files
committed
Merge remote-tracking branch 'origin/production' into add_delete_tree_rank_api
2 parents fb53e79 + a048403 commit 87182f0

File tree

21 files changed

+292
-58
lines changed

21 files changed

+292
-58
lines changed

specifyweb/frontend/js_src/lib/components/Atoms/Icons.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ export const icons = {
123123
// This icon is not from heroicons. It was drawn by @maxpatiiuk
124124
tree: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><rect height="10" rx="2" width="10"/><rect fillOpacity="0" height="8" rx="2" stroke="currentColor" strokeWidth="2" width="8" x="11" y="11"/><rect height="16" rx="1" width="2" x="4"/><rect height="2" rx="1" width="7" x="4" y="14"/></svg>,
125125
// This icon is not from Heroicons. It was drawn by @grantfitzsimmons
126+
truck: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M3.375 4.5C2.339 4.5 1.5 5.34 1.5 6.375V13.5h12V6.375c0-1.036-.84-1.875-1.875-1.875h-8.25ZM13.5 15h-12v2.625c0 1.035.84 1.875 1.875 1.875h.375a3 3 0 1 1 6 0h3a.75.75 0 0 0 .75-.75V15Z" /><path d="M8.25 19.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0ZM15.75 6.75a.75.75 0 0 0-.75.75v11.25c0 .087.015.17.042.248a3 3 0 0 1 5.958.464c.853-.175 1.522-.935 1.464-1.883a18.659 18.659 0 0 0-3.732-10.104 1.837 1.837 0 0 0-1.47-.725H15.75Z" /><path d="M19.5 19.5a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0Z" /></svg>,
126127
undoSynonym: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M1.6,10.3c-0.4,0.4-0.4,1,0,1.4l6,6C8,18.1,9.3,18,9.3,17v-2.3V3c0-0.6-0.4-1-1-1s-1,0.4-1,1v11.6L3,10.3 C2.6,9.9,2,9.9,1.6,10.3z"/><path d="M18.4,9.7c0.4-0.4,0.4-1,0-1.4l-6-6C12,1.9,10.7,2,10.7,3v2.3V17c0,0.6,0.4,1,1,1s1-0.4,1-1V5.4L17,9.7 C17.4,10.1,18,10.1,18.4,9.7z"/></svg>,
127128
upload: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M3 17a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM6.293 6.707a1 1 0 010-1.414l3-3a1 1 0 011.414 0l3 3a1 1 0 01-1.414 1.414L11 5.414V13a1 1 0 11-2 0V5.414L7.707 6.707a1 1 0 01-1.414 0z" fillRule="evenodd" /></svg>,
128129
userCircle: <svg aria-hidden className={iconClassName} fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path clipRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-6-3a2 2 0 11-4 0 2 2 0 014 0zm-2 4a5 5 0 00-4.546 2.916A5.986 5.986 0 0010 16a5.986 5.986 0 004.546-2.084A5 5 0 0010 11z" fillRule="evenodd" /></svg>,

specifyweb/frontend/js_src/lib/components/Errors/JsonError.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,10 @@ function resolveBackendLocalization(jsonResponseData: any): string {
177177
operation: jsonResponseData.operation,
178178
nodeModel: jsonResponseData.nodeModel,
179179
});
180-
else if (localizationKey === 'mergeAcrossTrees')
181-
return backEndText.mergeAcrossTrees();
182-
else if (localizationKey === 'synonymizeAcrossTrees')
183-
return backEndText.synonymizeAcrossTrees();
180+
else if (localizationKey === 'operationAcrossTrees')
181+
return backEndText.operationAcrossTrees({
182+
operation: jsonResponseData.operation,
183+
});
184184
else if (localizationKey === 'limitReachedDeterminingAccepted')
185185
return backEndText.limitReachedDeterminingAccepted({
186186
taxonId: jsonResponseData.taxonId,

specifyweb/frontend/js_src/lib/components/FormSliders/IntegratedRecordSelector.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ import type { FormType } from '../FormParse';
2121
import type { SubViewSortField } from '../FormParse/cells';
2222
import { augmentMode, ResourceView } from '../Forms/ResourceView';
2323
import { useFirstFocus } from '../Forms/SpecifyForm';
24-
import type { InteractionWithPreps } from '../Interactions/helpers';
25-
import { interactionPrepTables } from '../Interactions/helpers';
24+
import {
25+
interactionPrepTables,
26+
InteractionWithPreps,
27+
} from '../Interactions/helpers';
2628
import { InteractionDialog } from '../Interactions/InteractionDialog';
2729
import { hasTablePermission } from '../Permissions/helpers';
2830
import { relationshipIsToMany } from '../WbPlanView/mappingHelpers';
@@ -152,7 +154,10 @@ export function IntegratedRecordSelector({
152154
typeof collection.related === 'object' &&
153155
isDialogOpen ? (
154156
<InteractionDialog
155-
actionTable={collection.related.specifyTable}
157+
actionTable={
158+
collection.related
159+
.specifyTable as SpecifyTable<InteractionWithPreps>
160+
}
156161
interactionResource={interactionResource}
157162
itemCollection={
158163
collection as Collection<AnyInteractionPreparation>

specifyweb/frontend/js_src/lib/components/Interactions/InteractionDialog.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,22 @@ import { H3 } from '../Atoms';
2525
import { Button } from '../Atoms/Button';
2626
import { Link } from '../Atoms/Link';
2727
import { LoadingContext, ReadOnlyContext } from '../Core/Contexts';
28-
import type { AnySchema, SerializedResource, AnyInteractionPreparation,
29-
SerializedResource, } from '../DataModel/helperTypes';
28+
import type {
29+
AnySchema,
30+
AnyInteractionPreparation,
31+
SerializedResource,
32+
} from '../DataModel/helperTypes';
3033
import type { SpecifyResource } from '../DataModel/legacyTypes';
3134
import { getResourceViewUrl } from '../DataModel/resource';
3235
import type { LiteralField } from '../DataModel/specifyField';
3336
import type { Collection, SpecifyTable } from '../DataModel/specifyTable';
3437
import { tables } from '../DataModel/tables';
35-
import type { RecordSet } from '../DataModel/types';
38+
import type {
39+
DisposalPreparation,
40+
GiftPreparation,
41+
LoanPreparation,
42+
RecordSet,
43+
} from '../DataModel/types';
3644
import { AutoGrowTextArea } from '../Molecules/AutoGrowTextArea';
3745
import { Dialog } from '../Molecules/Dialog';
3846
import { userPreferences } from '../Preferences/userPreferences';

specifyweb/frontend/js_src/lib/components/Permissions/definitions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const operationPolicies = {
3737
'synonymize',
3838
'desynonymize',
3939
'repair',
40+
'bulk_move',
4041
],
4142
'/tree/edit/geologictimeperiod': [
4243
'merge',

specifyweb/frontend/js_src/lib/components/PickLists/definitions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const auditLogActions = [
4444
queryText.treeMove(),
4545
queryText.treeSynonymize(),
4646
queryText.treeDesynonymize(),
47+
queryText.treeBulkMove(),
4748
] as const;
4849

4950
const pickListSortTypes = f.store(() => [

specifyweb/frontend/js_src/lib/components/QueryBuilder/Formatter.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export function QueryFieldFormatter({
8080
<div>
8181
<Select
8282
aria-label={queryText.chooseFormatter()}
83-
className={`${customSelectElementBackground}`}
83+
className={customSelectElementBackground}
8484
disabled={handleChange === undefined}
8585
id={id('list')}
8686
value={formatter}
@@ -89,7 +89,7 @@ export function QueryFieldFormatter({
8989
<option />
9090
{availableFormatters.map(({ name, title, isDefault }, index) => (
9191
<option key={index} value={name}>
92-
{title} {isDefault ? resourcesText.defaultInline() : ''}
92+
{`${title} ${isDefault ? resourcesText.defaultInline() : ''}`}
9393
</option>
9494
))}
9595
</Select>

specifyweb/frontend/js_src/lib/components/Statistics/Categories.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { className } from '../Atoms/className';
1212
import { Input } from '../Atoms/Form';
1313
import type { Tables } from '../DataModel/types';
1414
import { getNoAccessTables } from '../QueryBuilder/helpers';
15+
import { DeleteStatsCategory } from './DeleteCategory';
1516
import { generateStatUrl, makeSerializedFieldsFromPaths } from './hooks';
1617
import { StatItem } from './StatItems';
1718
import { backEndStatsSpec, dynamicStatsSpec, statsSpec } from './StatsSpec';
@@ -142,6 +143,7 @@ export function Categories({
142143
(items ?? []).some(
143144
(item) => item.type === 'CustomStat' || item.isVisible === undefined
144145
);
146+
145147
return pageLayout === undefined ? null : (
146148
<>
147149
{pageLayout.categories.map(
@@ -269,14 +271,12 @@ export function Categories({
269271
) : null}
270272
{typeof handleAdd === 'function' ? (
271273
<div className="flex gap-2">
272-
<Button.Small
273-
variant={className.borderedGrayButton}
274-
onClick={(): void =>
275-
handleRemove?.(categoryIndex, undefined)
276-
}
277-
>
278-
{statsText.deleteCategory()}
279-
</Button.Small>
274+
{typeof handleRemove === 'function' ? (
275+
<DeleteStatsCategory
276+
categoryLabel={label}
277+
onDelete={() => handleRemove(categoryIndex, undefined)}
278+
/>
279+
) : null}
280280
<span className="-ml-2 flex-1" />
281281
<Button.Small
282282
variant={className.infoButton}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
3+
import { useBooleanState } from '../../hooks/useBooleanState';
4+
import { commonText } from '../../localization/common';
5+
import { statsText } from '../../localization/stats';
6+
import { StringToJsx } from '../../localization/utils';
7+
import { localized } from '../../utils/types';
8+
import { Button } from '../Atoms/Button';
9+
import { className } from '../Atoms/className';
10+
import { Dialog } from '../Molecules/Dialog';
11+
12+
export function DeleteStatsCategory({
13+
onDelete: handleDelete,
14+
categoryLabel,
15+
}: {
16+
readonly categoryLabel: string;
17+
readonly onDelete: () => void;
18+
}): JSX.Element {
19+
const [triedToDelete, _, __, toggleTriedToDelete] = useBooleanState(false);
20+
return (
21+
<>
22+
<Button.Small
23+
variant={className.secondaryButton}
24+
onClick={toggleTriedToDelete}
25+
>
26+
{statsText.deleteCategory()}
27+
</Button.Small>
28+
{triedToDelete && (
29+
<Dialog
30+
buttons={
31+
<>
32+
<Button.Danger
33+
onClick={() => {
34+
handleDelete();
35+
toggleTriedToDelete();
36+
}}
37+
>
38+
{commonText.delete()}
39+
</Button.Danger>
40+
<span className="-ml-2 flex-1" />
41+
<Button.DialogClose>{commonText.cancel()}</Button.DialogClose>
42+
</>
43+
}
44+
header={localized(
45+
`${statsText.deleteCategory()} '${categoryLabel}'?`
46+
)}
47+
onClose={toggleTriedToDelete}
48+
>
49+
{statsText.deleteWarning()}{' '}
50+
<StringToJsx
51+
components={{
52+
wrap: <i className="flex items-center gap-2">{categoryLabel}</i>,
53+
}}
54+
string={commonText.jsxColonLine({
55+
label: statsText.categoryToDelete(),
56+
})}
57+
/>
58+
</Dialog>
59+
)}
60+
</>
61+
);
62+
}

specifyweb/frontend/js_src/lib/components/Statistics/StatItems.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { Http } from '../../utils/ajax/definitions';
99
import { throttledPromise } from '../../utils/ajax/throttledPromise';
1010
import { localized } from '../../utils/types';
1111
import { formatNumber } from '../Atoms/Internationalization';
12-
import {
13-
deserializeResource,
14-
serializeResource,
15-
} from '../DataModel/serializers';
12+
import { serializeResource } from '../DataModel/serializers';
1613
import { getNoAccessTables } from '../QueryBuilder/helpers';
1714
import {
1815
makeSerializedFieldsFromPaths,
@@ -244,7 +241,7 @@ function QueryItem({
244241
async () =>
245242
throttledPromise<AjaxResponseObject<{ readonly count: number }>>(
246243
'queryStats',
247-
queryCountPromiseGenerator(deserializeResource(serializedQuery)),
244+
queryCountPromiseGenerator(serializedQuery),
248245
JSON.stringify(querySpec)
249246
).then((response) => {
250247
if (response === undefined) return undefined;
@@ -253,8 +250,7 @@ function QueryItem({
253250
setStatState('valid');
254251
return formatNumber(data.count);
255252
}
256-
if (status === Http.FORBIDDEN) setStatState('noPermission');
257-
setStatState('error');
253+
setStatState(status === Http.FORBIDDEN ? 'noPermission' : 'error');
258254
return undefined;
259255
}),
260256

0 commit comments

Comments
 (0)