Skip to content

Commit

Permalink
pids: improve UI for optional DOI
Browse files Browse the repository at this point in the history
  • Loading branch information
zzacharo committed Jan 14, 2025
1 parent 3f81209 commit 3296844
Show file tree
Hide file tree
Showing 15 changed files with 1,091 additions and 692 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
// This file is part of Invenio-RDM-Records
// Copyright (C) 2020-2025 CERN.
//
// Invenio-RDM-Records is free software; you can redistribute it and/or modify it
// under the terms of the MIT License; see LICENSE file for more details.

import _debounce from "lodash/debounce";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { FieldLabel } from "react-invenio-forms";
import { Form } from "semantic-ui-react";
import {
ManagedIdentifierCmp,
OptionalDOIoptions,
UnmanagedIdentifierCmp,
} from "./components";
import { getFieldErrors } from "./components/helpers";

const PROVIDER_EXTERNAL = "external";
const UPDATE_PID_DEBOUNCE_MS = 200;

/**
* Render managed or unamanged PID fields and update
* Formik form on input changed.
* The field value has the following format:
* { 'doi': { identifier: '<value>', provider: '<value>', client: '<value>' } }
*/
export class OptionalPIDField extends Component {
constructor(props) {
super(props);

const { canBeManaged, canBeUnmanaged, record, field } = this.props;
this.canBeManagedAndUnmanaged = canBeManaged && canBeUnmanaged;
const value = field?.value;
const isInternalProvider = value?.provider !== PROVIDER_EXTERNAL;
const isDraft = record?.is_draft === true;
const hasIdentifier = value?.identifier;
const isManagedSelected =
isDraft && hasIdentifier && isInternalProvider ? true : undefined;

this.state = {
isManagedSelected: isManagedSelected,
isNoNeedSelected: undefined,
};
}

onExternalIdentifierChanged = (identifier) => {
const { form, fieldPath } = this.props;

const pid = {
identifier: identifier,
provider: PROVIDER_EXTERNAL,
};

this.debounced && this.debounced.cancel();
this.debounced = _debounce(() => {
form.setFieldValue(fieldPath, pid);
}, UPDATE_PID_DEBOUNCE_MS);
this.debounced();
};

render() {
const { isManagedSelected, isNoNeedSelected } = this.state;
const {
btnLabelDiscardPID,
btnLabelGetPID,
canBeManaged,
canBeUnmanaged,
form,
fieldPath,
fieldLabel,
isEditingPublishedRecord,
managedHelpText,
pidLabel,
pidIcon,
pidPlaceholder,
required,
unmanagedHelpText,
pidType,
field,
record,
optionalDOItransitions,
} = this.props;

let { doiDefaultSelection } = this.props;

const value = field.value || {};
const currentIdentifier = value.identifier || "";
const currentProvider = value.provider || "";

let managedIdentifier = "",
unmanagedIdentifier = "";
if (currentIdentifier !== "") {
const isProviderExternal = currentProvider === PROVIDER_EXTERNAL;
managedIdentifier = !isProviderExternal ? currentIdentifier : "";
unmanagedIdentifier = isProviderExternal ? currentIdentifier : "";
}

const hasManagedIdentifier = managedIdentifier !== "";
const hasUnmanagedIdentifier = unmanagedIdentifier !== "";
const doi = record?.pids?.doi?.identifier || "";
const parentDoi = record.parent?.pids?.doi?.identifier || "";

const hasDoi = doi !== "";
const hasParentDoi = parentDoi !== "";
const isDraft = record.is_draft;

const _isUnmanagedSelected =
isManagedSelected === undefined
? hasUnmanagedIdentifier ||
(currentIdentifier === "" && doiDefaultSelection === "yes")
: !isManagedSelected;

const _isManagedSelected =
isManagedSelected === undefined
? hasManagedIdentifier ||
(currentIdentifier === "" && doiDefaultSelection === "no") // i.e pids: {}
: isManagedSelected;

const _isNoNeedSelected =
isNoNeedSelected === undefined
? (!_isManagedSelected && !_isUnmanagedSelected) ||
(isDraft !== true &&
currentIdentifier === "" &&
doiDefaultSelection === "not_needed")
: isNoNeedSelected;

const fieldError = getFieldErrors(form, fieldPath);

return (
<>
<Form.Field
required={required || hasParentDoi}
error={fieldError ? true : false}
>
<FieldLabel htmlFor={fieldPath} icon={pidIcon} label={fieldLabel} />
</Form.Field>

{this.canBeManagedAndUnmanaged && (
<OptionalDOIoptions
optionalDOItransitions={optionalDOItransitions}
isManagedSelected={_isManagedSelected}
isNoNeedSelected={_isNoNeedSelected}
onManagedUnmanagedChange={(userSelectedManaged, userSelectedNoNeed) => {
if (userSelectedManaged) {
form.setFieldValue("pids", {});
if (!required) {
// We set the value as required so we can validate the action on submit
form.setFieldValue("noINeedDOI", true);
}
} else if (userSelectedNoNeed) {
form.setFieldValue("pids", {});
form.setFieldValue("noINeedDOI", false);
} else {
this.onExternalIdentifierChanged("");
form.setFieldValue("noINeedDOI", false);
}
form.setFieldError(fieldPath, false);
this.setState({
isManagedSelected: userSelectedManaged,
isNoNeedSelected: userSelectedNoNeed,
});
}}
pidLabel={pidLabel}
required={required}
/>
)}

{canBeManaged && _isManagedSelected && (
<ManagedIdentifierCmp
disabled={hasDoi && isEditingPublishedRecord}
btnLabelDiscardPID={btnLabelDiscardPID}
btnLabelGetPID={btnLabelGetPID}
form={form}
fieldPath={fieldPath}
identifier={managedIdentifier}
helpText={managedHelpText}
pidPlaceholder={pidPlaceholder}
pidType={pidType}
pidLabel={pidLabel}
/>
)}

{canBeUnmanaged && (!_isManagedSelected || _isNoNeedSelected) && (
<UnmanagedIdentifierCmp
identifier={unmanagedIdentifier}
onIdentifierChanged={(identifier) => {
this.onExternalIdentifierChanged(identifier);
}}
form={form}
fieldPath={fieldPath}
pidPlaceholder={pidPlaceholder}
helpText={unmanagedHelpText}
disabled={_isNoNeedSelected}
/>
)}
</>
);
}
}

OptionalPIDField.propTypes = {
field: PropTypes.object,
form: PropTypes.object.isRequired,
btnLabelDiscardPID: PropTypes.string.isRequired,
btnLabelGetPID: PropTypes.string.isRequired,
canBeManaged: PropTypes.bool.isRequired,
canBeUnmanaged: PropTypes.bool.isRequired,
fieldPath: PropTypes.string.isRequired,
fieldLabel: PropTypes.string.isRequired,
isEditingPublishedRecord: PropTypes.bool.isRequired,
managedHelpText: PropTypes.string,
pidIcon: PropTypes.string.isRequired,
pidLabel: PropTypes.string.isRequired,
pidPlaceholder: PropTypes.string.isRequired,
pidType: PropTypes.string.isRequired,
required: PropTypes.bool.isRequired,
unmanagedHelpText: PropTypes.string,
record: PropTypes.object.isRequired,
doiDefaultSelection: PropTypes.object.isRequired,
optionalDOItransitions: PropTypes.array.isRequired,
};

OptionalPIDField.defaultProps = {
managedHelpText: null,
unmanagedHelpText: null,
field: undefined,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// This file is part of Invenio-RDM-Records
// Copyright (C) 2020-2023 CERN.
// Copyright (C) 2020-2022 Northwestern University.
//
// Invenio-RDM-Records is free software; you can redistribute it and/or modify it
// under the terms of the MIT License; see LICENSE file for more details.

import { FastField } from "formik";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { RequiredPIDField } from "./RequiredPIDField";
import { OptionalPIDField } from "./OptionalPIDField";

/**
* Render the PIDField using a custom Formik component
*/
export class PIDField extends Component {
constructor(props) {
super(props);

this.validatePropValues();
}

validatePropValues = () => {
const { canBeManaged, canBeUnmanaged, fieldPath } = this.props;

if (!canBeManaged && !canBeUnmanaged) {
throw Error(`${fieldPath} must be managed, unmanaged or both.`);
}
};

render() {
const { fieldPath, required } = this.props;
const cmp = required ? RequiredPIDField : OptionalPIDField;

return <FastField name={fieldPath} component={cmp} {...this.props} />;
}
}

PIDField.propTypes = {
btnLabelDiscardPID: PropTypes.string,
btnLabelGetPID: PropTypes.string,
canBeManaged: PropTypes.bool,
canBeUnmanaged: PropTypes.bool,
fieldPath: PropTypes.string.isRequired,
fieldLabel: PropTypes.string.isRequired,
isEditingPublishedRecord: PropTypes.bool.isRequired,
managedHelpText: PropTypes.string,
pidIcon: PropTypes.string,
pidLabel: PropTypes.string.isRequired,
pidPlaceholder: PropTypes.string,
pidType: PropTypes.string.isRequired,
required: PropTypes.bool,
unmanagedHelpText: PropTypes.string,
record: PropTypes.object.isRequired,
doiDefaultSelection: PropTypes.object.isRequired,
optionalDOItransitions: PropTypes.array.isRequired,
};

PIDField.defaultProps = {
btnLabelDiscardPID: "Discard",
btnLabelGetPID: "Reserve",
canBeManaged: true,
canBeUnmanaged: true,
managedHelpText: null,
pidIcon: "barcode",
pidPlaceholder: "",
required: false,
unmanagedHelpText: null,
};
Loading

0 comments on commit 3296844

Please sign in to comment.