Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 34 additions & 17 deletions specifyweb/frontend/js_src/lib/components/SetupTool/SetupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ import type {
} from '../TreeView/CreateTree';
import { PopulatedTreeList } from '../TreeView/CreateTree';
import type { FieldConfig, ResourceConfig } from './setupResources';
import { disciplineTypeOptions, FIELD_MAX_LENGTH, resources,stepOrder } from './setupResources';
import {
disciplineTypeOptions,
FIELD_MAX_LENGTH,
resources,
stepOrder,
} from './setupResources';
import type { ResourceFormData } from './types';

function getFormValue(
Expand Down Expand Up @@ -142,15 +147,20 @@ export function renderFormFieldFactory({

const verticalSpacing = width !== undefined && width < 2 ? '-mb-2' : 'mb-2';

const disciplineTypeValue = getFormValue(formData, stepOrder.indexOf('discipline'), 'type');
const disciplineTypeValue = getFormValue(
formData,
stepOrder.indexOf('discipline'),
'type'
);
const isDisciplineNameDisabled =
resources[currentStep].resourceName === 'discipline' &&
fieldName === 'name' &&
(disciplineTypeValue === undefined || disciplineTypeValue === '');

const isRowEnableToggle = name === 'include';
const isRowEnabled =
Boolean(getFormValue(formData, currentStep, `${parentName}.include`));
const isRowEnabled = Boolean(
getFormValue(formData, currentStep, `${parentName}.include`)
);

const taxonTreeAvailable =
Array.isArray(treeOptions) &&
Expand All @@ -177,9 +187,7 @@ export function renderFormFieldFactory({
getFormValue(formData, currentStep, fieldName)
)}
disabled={
inTable
? !isRowEnabled && !isRowEnableToggle
: false
inTable ? !isRowEnabled && !isRowEnableToggle : false
}
id={fieldName}
name={fieldName}
Expand Down Expand Up @@ -325,7 +333,7 @@ export function renderFormFieldFactory({
<PopulatedTreeList
discipline={disciplineTypeValue as string | undefined}
handleClick={(
resource: TaxonFileDefaultDefinition,
resource: TaxonFileDefaultDefinition
): void => {
handleChange(fieldName, resource);
handleChange('preload', true);
Expand All @@ -351,10 +359,14 @@ export function renderFormFieldFactory({
value={getFormValue(formData, currentStep, fieldName) ?? ''}
onChange={({ target }) => {
// Only allow unique discipline names
if (resources[currentStep].resourceName === 'discipline' && fieldName === 'name') {
if (
resources[currentStep].resourceName === 'discipline' &&
fieldName === 'name'
) {
const value = (target.value ?? '').trim();
const existingDisciplines = institutionData.children.flatMap(division =>
division.children.map(discipline => discipline.name)
const existingDisciplines = institutionData.children.flatMap(
(division) =>
division.children.map((discipline) => discipline.name)
);
const isUsed = existingDisciplines.includes(value);
target.setCustomValidity(
Expand Down Expand Up @@ -441,7 +453,7 @@ export function updateSetupFormData(
name: string,
newValue: LocalizedString | TaxonFileDefaultDefinition | boolean,
currentStep: number,
institutionData?: InstitutionData,
institutionData?: InstitutionData
): void {
setFormData((previous: ResourceFormData) => {
const resourceName = resources[currentStep].resourceName;
Expand All @@ -457,10 +469,15 @@ export function updateSetupFormData(
(option) => option.value === newValue
);
if (matchingType) {
const existingDisciplines = institutionData ? institutionData.children.flatMap(division =>
division.children.map(discipline => discipline.name)
) : [];
const disciplineName = getUniqueName(matchingType.label, existingDisciplines);
const existingDisciplines = institutionData
? institutionData.children.flatMap((division) =>
division.children.map((discipline) => discipline.name)
)
: [];
const disciplineName = getUniqueName(
matchingType.label,
existingDisciplines
);
updates.name = matchingType ? disciplineName : '';
}

Expand All @@ -485,4 +502,4 @@ export function updateSetupFormData(
[resourceName]: updates,
};
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { tables } from '../DataModel/tables';
import { getSystemInfo } from '../InitialContext/systemInfo';
import { Dialog, LoadingScreen } from '../Molecules/Dialog';
import { ResourceLink } from '../Molecules/ResourceLink';
import { hasTablePermission } from '../Permissions/helpers';
import { tableLabel } from '../Preferences/UserDefinitions';
import {
applyFormDefaults,
Expand All @@ -40,7 +41,6 @@ import { nestAllResources } from '../SetupTool/utils';
import type { TaxonFileDefaultDefinition } from '../TreeView/CreateTree';
import { CollapsibleSection } from './CollapsibleSection';
import type { InstitutionData } from './Utils';
import { hasTablePermission } from '../Permissions/helpers';

type HierarchyNodeKind =
| 'collection'
Expand Down
59 changes: 28 additions & 31 deletions specifyweb/frontend/js_src/lib/components/TreeView/CreateTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ export function ImportTree<SCHEMA extends AnyTree>({
readonly tableName: SCHEMA['tableName'];
readonly treeDefId: number;
readonly treeDefinitionItems: RA<
SerializedResource<FilterTablesByEndsWith<'TreeDefItem'>>
>;
SerializedResource<FilterTablesByEndsWith<'TreeDefItem'>>
>;
readonly buttonLabel?: LocalizedString;
readonly buttonClassName?: string;
}): JSX.Element {
Expand All @@ -222,9 +222,14 @@ export function ImportTree<SCHEMA extends AnyTree>({
string | undefined
>(undefined);

const [missingTreeRanks, setMissingTreeRanks] = React.useState<RA<string> | undefined>(undefined);
const [isMissingTreeRanks, setIsMissingTreeRanks] = React.useState<boolean>(false);
const [selectedPopulatedTree, setSelectedPopulatedTree] = React.useState<TaxonFileDefaultDefinition | undefined>(undefined);
const [missingTreeRanks, setMissingTreeRanks] = React.useState<
RA<string> | undefined
>(undefined);
const [isMissingTreeRanks, setIsMissingTreeRanks] =
React.useState<boolean>(false);
const [selectedPopulatedTree, setSelectedPopulatedTree] = React.useState<
TaxonFileDefaultDefinition | undefined
>(undefined);

const connectedCollection = getSystemInfo().collection;

Expand All @@ -240,15 +245,20 @@ export function ImportTree<SCHEMA extends AnyTree>({
method: 'POST',
headers: { Accept: 'application/json' },
body: {
mappingUrl: resource.mappingFile
}
mappingUrl: resource.mappingFile,
},
});
if (response.status === Http.OK && response.data) {
const mappingRankNames = response.data.ranks.map((rank: any) => rank.name);
const existingNames = new Set(treeDefinitionItems.map((item) => item.name));
const mappingRankNames = response.data.ranks.map(
(rank: any) => rank.name
);
const existingNames = new Set(
treeDefinitionItems.map((item) => item.name)
);

const missing = mappingRankNames
.filter((rankName: string) => !existingNames.has(rankName));
const missing = mappingRankNames.filter(
(rankName: string) => !existingNames.has(rankName)
);

if (missing.length > 0) {
setSelectedPopulatedTree(resource);
Expand Down Expand Up @@ -323,9 +333,7 @@ export function ImportTree<SCHEMA extends AnyTree>({
{isActive === 1 ? (
<Dialog
buttons={
<Button.DialogClose>
{commonText.cancel()}
</Button.DialogClose>
<Button.DialogClose>{commonText.cancel()}</Button.DialogClose>
}
header={commonText.import()}
onClose={() => setIsActive(0)}
Expand Down Expand Up @@ -368,7 +376,7 @@ async function startTreeCreation(
treeName: string,
treeDefId: number | undefined,
createMissingRanks: boolean | undefined,
onSuccess: (taskId: string | undefined) => void,
onSuccess: (taskId: string | undefined) => void
): Promise<void> {
return ajax<TreeCreationInfo>('/trees/create_default_tree/', {
method: 'POST',
Expand All @@ -389,10 +397,7 @@ async function startTreeCreation(
console.log(`${treeName} tree created successfully:`, data);
} else if (status === Http.ACCEPTED) {
// Tree is being created in the background.
console.log(
`${treeName} tree creation started successfully:`,
data
);
console.log(`${treeName} tree creation started successfully:`, data);
onSuccess(data.task_id);
}
})
Expand Down Expand Up @@ -488,34 +493,26 @@ export function MissingTreeRanksDialog({
<Dialog
buttons={
<>
<Button.DialogClose>
{commonText.close()}
</Button.DialogClose>
<Button.DialogClose>{commonText.close()}</Button.DialogClose>
<Button.Secondary onClick={handleNo}>
{commonText.no()}
</Button.Secondary>
<Button.Info onClick={handleYes}>
{queryText.yes()}
</Button.Info>
<Button.Info onClick={handleYes}>{queryText.yes()}</Button.Info>
</>
}
header={treeText.missingRanks()}
onClose={onClose}
>
<div className="mb-4 flex flex-col gap-4">
<section>
{treeText.missingRanksDescription()}
</section>
<section>{treeText.missingRanksDescription()}</section>
<section>
<ul className="ml-4">
{missingTreeRanks && missingTreeRanks.length > 0
? missingTreeRanks.map((rank) => <li key={rank}>{rank}</li>)
: null}
</ul>
</section>
<section>
{treeText.createMissingRanks()}
</section>
<section>{treeText.createMissingRanks()}</section>
</div>
</Dialog>
);
Expand Down
19 changes: 10 additions & 9 deletions specifyweb/frontend/js_src/lib/components/TreeView/Tree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { idFromUrl } from '../DataModel/resource';
import { deserializeResource } from '../DataModel/serializers';
import { softError } from '../Errors/assert';
import { ResourceView } from '../Forms/ResourceView';
import { userInformation } from '../InitialContext/userInformation';
import { hasTablePermission } from '../Permissions/helpers';
import { useHighContrast } from '../Preferences/Hooks';
import { userPreferences } from '../Preferences/userPreferences';
Expand All @@ -31,7 +32,6 @@ import { ImportTree } from './CreateTree';
import type { Conformations, Row, Stats } from './helpers';
import { fetchStats } from './helpers';
import { TreeRow } from './Row';
import { userInformation } from '../InitialContext/userInformation';

const treeToPref = {
Geography: 'geography',
Expand Down Expand Up @@ -168,10 +168,7 @@ export function Tree<
stop();
globalThis.location.reload();
return;
} else if (
oldTreeCreationProgress === undefined &&
!data.active
) {
} else if (oldTreeCreationProgress === undefined && !data.active) {
// Tree was already complete
stop();
}
Expand Down Expand Up @@ -318,7 +315,8 @@ export function Tree<
?.slice(1) as Conformations
}
focusPath={
(focusPath[0] === 0 && index === 0) || focusPath[0] === row.nodeId
(focusPath[0] === 0 && index === 0) ||
focusPath[0] === row.nodeId
? focusPath.slice(1)
: undefined
}
Expand All @@ -342,13 +340,16 @@ export function Tree<
setFocusPath([rows[index - 1].nodeId]);
else if (action === 'previous' || action === 'parent')
setFocusPath([]);
else if (action === 'focusPrevious') focusRef.current?.focus();
else if (action === 'focusNext') searchBoxRef.current?.focus();
else if (action === 'focusPrevious')
focusRef.current?.focus();
else if (action === 'focusNext')
searchBoxRef.current?.focus();
return undefined;
}}
onChangeConformation={(newConformation): void =>
setConformation([
...(conformation?.filter(([id]) => id !== row.nodeId) ?? []),
...(conformation?.filter(([id]) => id !== row.nodeId) ??
[]),
...(typeof newConformation === 'object'
? ([[row.nodeId, ...newConformation]] as const)
: []),
Expand Down
2 changes: 1 addition & 1 deletion specifyweb/frontend/js_src/lib/localization/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,7 @@ export const treeText = createDictionary({
'Creating tree record {current:number|formatted}/{total:number|formatted}',
},
defaultTreeCreationLoadingMessage: {
'en-us': 'Default Tree Creation is in progress, please wait...'
'en-us': 'Default Tree Creation is in progress, please wait...',
},
missingRanks: {
'en-us': 'Missing Ranks',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from specifyweb.specify.migration_utils.misc_migrations import make_selectseries_false
from specifyweb.specify.migration_utils.tectonic_ranks import create_default_tectonic_ranks, create_root_tectonic_node
from specifyweb.backend.patches.migration_utils import apply_migrations as apply_patches
from specifyweb.backend.setup_tool.schema_defaults import apply_schema_defaults

logger = logging.getLogger(__name__)

Expand All @@ -48,6 +49,15 @@ def fix_cots(stdout: WriteToStdOut | None = None):
log_and_run(funcs, stdout)

def fix_schema_config(stdout: WriteToStdOut | None = None):
def apply_schema_overrides_for_all_disciplines(_apps):
Discipline = _apps.get_model('specify', 'Discipline')
for discipline in Discipline.objects.all():
if stdout is not None:
stdout(
f"Applying schema defaults/overrides for discipline {discipline.id} ({discipline.type})..."
)
apply_schema_defaults(discipline)

funcs = [
# usc.update_all_table_schema_config_with_defaults,
usc.create_geo_table_schema_config_with_defaults, # specify 0002
Expand Down Expand Up @@ -80,6 +90,7 @@ def fix_schema_config(stdout: WriteToStdOut | None = None):
usc.componets_schema_config_migrations, # specify 0040
usc.create_discipline_type_picklist, # specify 0042
usc.update_discipline_type_splocalecontaineritem, # specify 0042
apply_schema_overrides_for_all_disciplines,
usc.deduplicate_schema_config_orm,
]
log_and_run(funcs, stdout)
Expand Down
Loading