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
1 change: 1 addition & 0 deletions stripes-reshare/translations/stripes-reshare/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"actions.accept-cancel": "Accept cancellation",
"actions.accept-retry": "Accept retry",
"actions.reject-retry": "Reject retry",
"actions.rerequest": "Create revised request",
"actions.recall": "Recall item",
"actions.recall.success": "Item recalled",
"actions.recall.error": "Error recalling item. {errMsg}",
Expand Down
1 change: 1 addition & 0 deletions ui-rs/src/components/Flow/actionMeta.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const actionMeta = {
'pullslip-printed': { icon: 'print' },
'ask-retry': { icon: 'edit' },
'recall': { icon: 'flag' },
'rerequest': { icon: 'duplicate' },
'overdue': { icon: 'clock' },
'renew': { icon: 'replace' },
'accept-renewal': { icon: 'check-circle', primaryOnly: true },
Expand Down
31 changes: 31 additions & 0 deletions ui-rs/src/components/Flow/moreActions/Rerequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useHistory, useLocation } from 'react-router-dom';
import { useIsActionPending } from '@projectreshare/stripes-reshare';
import { Button, Icon } from '@folio/stripes/components';

import actionMeta from '../actionMeta';

// Opens the revision form; the form posts the action itself on submit.
const Rerequest = ({ request }) => {
const history = useHistory();
const location = useLocation();
const actionPending = !!useIsActionPending(request.id);

Comment thread
skomorokh marked this conversation as resolved.
const open = () => history.push({
// Relative: replaces the flow route's last segment.
pathname: 'rerequest',
search: location.search,
state: { direct: true },
});

return (
<Button buttonStyle="dropdownItem" onClick={open} disabled={actionPending}>
<Icon icon={actionMeta.rerequest.icon}>
<FormattedMessage id="stripes-reshare.actions.rerequest" />
</Icon>
</Button>
);
};

export default Rerequest;
1 change: 1 addition & 0 deletions ui-rs/src/components/Flow/moreActions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export { default as AddCondition } from './AddCondition';
export { default as AddItem } from './AddItem';
export { default as AskRetry } from './AskRetry';
export { default as Recall } from './Recall';
export { default as Rerequest } from './Rerequest';
export { default as SupplyDocument } from './SupplyDocument';

export const CannotSupply = props => (
Expand Down
74 changes: 74 additions & 0 deletions ui-rs/src/components/PatronRequestForm/PatronRequestFormPane.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { useForm, useFormState } from 'react-final-form';
import { Prompt } from 'react-router-dom';
import { Button, Pane, Paneset, PaneFooter } from '@folio/stripes/components';

const PatronRequestFormPane = ({
titleId,
submitLabelId = titleId,
submitDisabled = false,
onClose,
children,
}) => {
const form = useForm();
const { pristine, submitting, submitSucceeded } = useFormState({
subscription: { pristine: true, submitting: true, submitSucceeded: true },
});

// The submit button is in the footer, outside the form element, so both route
// through here. Also applies submitDisabled to an Enter-key submit.
const submit = (e) => {
e.preventDefault();
e.stopPropagation();
if (submitting || submitDisabled) return undefined;
return form.submit();
};

return (
<Paneset>
<Pane
defaultWidth="100%"
centerContent
onClose={onClose}
dismissible={!submitting}
paneTitle={<FormattedMessage id={titleId} />}
footer={
<PaneFooter
renderStart={
<Button
id="clickable-cancel-request-form"
buttonStyle="default mega"
marginBottom0
onClick={onClose}
disabled={submitting}
>
<FormattedMessage id="stripes-core.button.cancel" />
</Button>
}
renderEnd={
<Button
type="submit"
disabled={submitting || submitDisabled}
onClick={submit}
buttonStyle="primary mega"
marginBottom0
>
<FormattedMessage id={submitLabelId} />
</Button>
}
/>
}
>
<form onSubmit={submit}>
{children}
</form>
<FormattedMessage id="ui-rs.confirmDirtyNavigate">
{prompt => <Prompt when={!pristine && !(submitting || submitSucceeded)} message={prompt[0]} />}
</FormattedMessage>
</Pane>
</Paneset>
);
};

export default PatronRequestFormPane;
24 changes: 23 additions & 1 deletion ui-rs/src/components/PatronRequestForm/formMapping.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import omit from 'lodash/omit';
import { CREATE, EDIT } from './operations';
import { ID_ARRAYS, extractIdentifiers } from '../../util/bibIdentifiers';

Expand Down Expand Up @@ -69,4 +70,25 @@ const formToBroker = (submittedRecord, { operation = CREATE } = {}) => {
};
};

export { brokerToForm, formToBroker };
// A new transaction linked to the original only by prevReqId, so the original's
// protocol header and any Retry linkage go.
const formToRevision = (submittedRecord, prevReqId) => {
const record = formToBroker(submittedRecord);
const illRequest = omit(record.illRequest, 'header');
const serviceInfo = omit(illRequest.serviceInfo, 'requestingAgencyPreviousRequestId');

return {
...record,
prevReqId,
illRequest: {
...illRequest,
serviceInfo: {
...serviceInfo,
requestType: 'New',
serviceLevel: serviceInfo.serviceLevel ?? { '#text': 'Standard' },
},
},
};
};

export { brokerToForm, formToBroker, formToRevision };
21 changes: 21 additions & 0 deletions ui-rs/src/components/PatronRequestForm/submissionError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';
import { FORM_ERROR } from 'final-form';
import { KeyValue } from '@folio/stripes/components';

// Return from onSubmit: Final Form then counts the submit as failed, which keeps
// the leave prompt armed.
const submissionError = (callout, labelId, err) => {
callout.sendCallout({
type: 'error',
message: (
<KeyValue
label={<FormattedMessage id={labelId} />}
value={err?.message || ''}
/>
),
});
return { [FORM_ERROR]: err?.message || labelId };
};

export default submissionError;
4 changes: 4 additions & 0 deletions ui-rs/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import EditRoute from './routes/EditRoute';
import PatronRequestsRoute from './routes/PatronRequestsRoute';
import PullSlipRoute from './routes/PullSlipRoute';
import PullSlipsRoute from './routes/PullSlipsRoute';
import RerequestRoute from './routes/RerequestRoute';
import ViewRoute from './routes/ViewRoute';

const ResourceSharing = (props) => {
Expand Down Expand Up @@ -64,6 +65,9 @@ const ResourceSharing = (props) => {
{appName === 'request' &&
<Route path={`${path}/requests/:id/edit`} component={EditRoute} />
}
{appName === 'request' &&
<Route path={`${path}/requests/:id/rerequest`} component={RerequestRoute} />
}
<Route path={`${path}/requests/pullslips`} component={PullSlipsRoute} />
<Route path={`${path}/requests/:id/pullslip`} component={PullSlipRoute} />
<Redirect
Expand Down
77 changes: 16 additions & 61 deletions ui-rs/src/routes/CreateRoute.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { useContext } from 'react';
import { FormattedMessage } from 'react-intl';
import { Form } from 'react-final-form';
import { useMutation, useQueryClient } from 'react-query';
import { Prompt, useHistory, useLocation } from 'react-router-dom';
import { Button, Pane, Paneset, PaneFooter, KeyValue } from '@folio/stripes/components';
import { useHistory, useLocation } from 'react-router-dom';
import { CalloutContext } from '@folio/stripes/core';
import { useCloseDirect, useOkapiKy } from '@projectreshare/stripes-reshare';
import PatronRequestForm from '../components/PatronRequestForm';
import PatronRequestFormPane from '../components/PatronRequestForm/PatronRequestFormPane';
import submissionError from '../components/PatronRequestForm/submissionError';
import useOptions from '../components/PatronRequestForm/useOptions';
import { formToBroker } from '../components/PatronRequestForm/formMapping';
import handleSISelect from '../components/PatronRequestForm/handleSISelect';
Expand Down Expand Up @@ -71,69 +71,24 @@ const CreateRoute = () => {
};
try {
await creator.mutateAsync(newRecord);
return undefined;
} catch (err) {
callout.sendCallout({
type: 'error',
message: (
<KeyValue
label={<FormattedMessage id="ui-rs.create.error" />}
value={err?.message || ''}
/>
),
});
return submissionError(callout, 'ui-rs.create.error', err);
}
};

return (
<Paneset>
<Form onSubmit={submit} initialValues={initialValues} mutators={{ handleSISelect }} keepDirtyOnReinitialize>
{({ form, handleSubmit, pristine, submitting, submitSucceeded }) => (
<Pane
defaultWidth="100%"
centerContent
onClose={close}
dismissible
footer={
<PaneFooter
renderStart={
<Button
id="clickable-cancel-create-request"
buttonStyle="default mega"
marginBottom0
onClick={close}
>
<FormattedMessage id="stripes-core.button.cancel" />
</Button>
}
renderEnd={
<Button
type="submit"
disabled={pristine || submitting}
onClick={handleSubmit}
buttonStyle="primary mega"
marginBottom0
>
<FormattedMessage id="ui-rs.createPatronRequest" />
</Button>
}
/>
}
paneTitle={<FormattedMessage id="ui-rs.createPatronRequest" />}
>
<form onSubmit={handleSubmit}>
<PatronRequestForm
selectOptions={options}
onSISelect={form.mutators.handleSISelect}
autopopulate={autopopulate}
/>
</form>
<FormattedMessage id="ui-rs.confirmDirtyNavigate">
{prompt => <Prompt when={!pristine && !(submitting || submitSucceeded)} message={prompt[0]} />}
</FormattedMessage>
</Pane>
)}
</Form>
</Paneset>
<Form onSubmit={submit} initialValues={initialValues} mutators={{ handleSISelect }} keepDirtyOnReinitialize>
{({ form, pristine }) => (
<PatronRequestFormPane titleId="ui-rs.createPatronRequest" submitDisabled={pristine} onClose={close}>
<PatronRequestForm
selectOptions={options}
onSISelect={form.mutators.handleSISelect}
autopopulate={autopopulate}
/>
</PatronRequestFormPane>
)}
</Form>
);
};

Expand Down
Loading
Loading