@inveniosoftware/react-invenio-app-ils 2.0.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -697,7 +697,7 @@ const RECORDS_CONFIG = {
697
697
  }
698
698
  },
699
699
  ITEMS: {
700
- identifiersToDisplayInFrontside: [],
700
+ identifiersToDisplay: [],
701
701
  circulationRestrictions: [{
702
702
  value: 'NO_RESTRICTION',
703
703
  text: 'No restriction (4 weeks)'
@@ -4245,6 +4245,26 @@ const vocabularyApi = {
4245
4245
  serializer: serializer$1
4246
4246
  };
4247
4247
 
4248
+ /**
4249
+ * Resolves identifier scheme keys (e.g. "CALL_NUMBER") to their
4250
+ * human-readable titles from the vocabulary API.
4251
+ *
4252
+ * @param {Array<string>} identifiers
4253
+ * @param {string} identifierScheme
4254
+ * @returns {Promise<Array<{key: string, text: string}>>}
4255
+ */
4256
+ const fetchIdentifierTitles = async (identifiers, identifierScheme) => {
4257
+ const query = vocabularyApi.query().withType(identifierScheme);
4258
+ const response = await vocabularyApi.list(query.qs());
4259
+ return identifiers.map(scheme => {
4260
+ const vocabEntry = response.data.hits.find(entry => entry.metadata.key === scheme);
4261
+ return {
4262
+ key: scheme,
4263
+ text: vocabEntry ? vocabEntry.metadata.text : scheme
4264
+ };
4265
+ });
4266
+ };
4267
+
4248
4268
  class ScrollingMenuItem$1 extends Component {
4249
4269
  constructor(props) {
4250
4270
  super(props);
@@ -4317,6 +4337,14 @@ const prettyPrintBooleanValue = value => {
4317
4337
  const screenIsWiderThan = pixels => {
4318
4338
  return window.matchMedia(`(max-width: ${pixels}px)`).matches ? false : true;
4319
4339
  };
4340
+ const isPrivilegedUser = () => {
4341
+ const roles = _get(sessionManager, 'user.roles', []);
4342
+ return roles.includes('admin') || roles.includes('librarian');
4343
+ };
4344
+ const isDocumentOverbooked = async documentPid => {
4345
+ const response = await documentApi.get(documentPid);
4346
+ return _get(response, 'data.metadata.circulation.overbooked', false);
4347
+ };
4320
4348
 
4321
4349
  class MetadataTable extends Component {
4322
4350
  renderRows() {
@@ -6182,32 +6210,36 @@ class DocumentItemBody extends Component {
6182
6210
  items,
6183
6211
  shelfLink,
6184
6212
  documentDetails,
6185
- identifiersToDisplayInFrontside
6186
- } = this.props;
6187
- return items.map(item => /*#__PURE__*/React.createElement(Table.Row, {
6188
- key: item.pid
6189
- }, /*#__PURE__*/React.createElement(Table.Cell, {
6190
- "data-label": "Barcode",
6191
- className: "document-item-table-itemCell"
6192
- }, item.barcode), /*#__PURE__*/React.createElement(Table.Cell, {
6193
- "data-label": "Shelf",
6194
- className: "document-item-table-itemCell"
6195
- }, shelfLink !== null ? shelfLink(item, documentDetails) : _get(item, 'shelf')), identifiersToDisplayInFrontside.map(identifier => {
6196
- var _item$identifiers, _item$identifiers$fin;
6197
- return /*#__PURE__*/React.createElement(Table.Cell, {
6198
- key: identifier,
6199
- "data-label": identifier.text,
6213
+ identifiersToDisplay
6214
+ } = this.props;
6215
+ return items.map(item => {
6216
+ const itemIsAccessible = !_get(item, 'internal_location.restricted');
6217
+ const shelfValue = shelfLink ? shelfLink(item, documentDetails) : _get(item, 'shelf');
6218
+ return /*#__PURE__*/React.createElement(Table.Row, {
6219
+ key: item.pid
6220
+ }, /*#__PURE__*/React.createElement(Table.Cell, {
6221
+ "data-label": "Barcode",
6222
+ className: "document-item-table-itemCell"
6223
+ }, item.barcode), /*#__PURE__*/React.createElement(Table.Cell, {
6224
+ "data-label": "Shelf",
6200
6225
  className: "document-item-table-itemCell"
6201
- }, (_item$identifiers = item.identifiers) === null || _item$identifiers === void 0 ? void 0 : (_item$identifiers$fin = _item$identifiers.find(entry => {
6202
- return entry.scheme === identifier.key;
6203
- })) === null || _item$identifiers$fin === void 0 ? void 0 : _item$identifiers$fin.value);
6204
- }), /*#__PURE__*/React.createElement(Table.Cell, {
6205
- "data-label": "Status"
6206
- }, this.statusLabel(item)), /*#__PURE__*/React.createElement(Table.Cell, {
6207
- "data-label": "Medium"
6208
- }, getDisplayVal('ITEMS.mediums', item.medium)), /*#__PURE__*/React.createElement(Table.Cell, {
6209
- "data-label": "Restrictions"
6210
- }, getDisplayVal('ITEMS.circulationRestrictions', item.circulation_restriction))));
6226
+ }, itemIsAccessible ? shelfValue : /*#__PURE__*/React.createElement("p", null, "Available on Request")), identifiersToDisplay.map(identifier => {
6227
+ var _item$identifiers, _item$identifiers$fin;
6228
+ return /*#__PURE__*/React.createElement(Table.Cell, {
6229
+ key: identifier,
6230
+ "data-label": identifier.text,
6231
+ className: "document-item-table-itemCell"
6232
+ }, itemIsAccessible && ((_item$identifiers = item.identifiers) === null || _item$identifiers === void 0 ? void 0 : (_item$identifiers$fin = _item$identifiers.find(entry => {
6233
+ return entry.scheme === identifier.key;
6234
+ })) === null || _item$identifiers$fin === void 0 ? void 0 : _item$identifiers$fin.value));
6235
+ }), /*#__PURE__*/React.createElement(Table.Cell, {
6236
+ "data-label": "Status"
6237
+ }, this.statusLabel(item)), /*#__PURE__*/React.createElement(Table.Cell, {
6238
+ "data-label": "Medium"
6239
+ }, getDisplayVal('ITEMS.mediums', item.medium)), /*#__PURE__*/React.createElement(Table.Cell, {
6240
+ "data-label": "Restrictions"
6241
+ }, getDisplayVal('ITEMS.circulationRestrictions', item.circulation_restriction)));
6242
+ });
6211
6243
  }
6212
6244
  }
6213
6245
  DocumentItemBody.defaultProps = {
@@ -27008,7 +27040,11 @@ class AvailableItems$1 extends Component {
27008
27040
  query: '',
27009
27041
  filteredData: null,
27010
27042
  modalOpen: false,
27011
- checkoutItem: null
27043
+ checkoutItem: null,
27044
+ identifiersToDisplay: invenioConfig.ITEMS.identifiersToDisplay.map(s => ({
27045
+ key: s,
27046
+ text: s
27047
+ }))
27012
27048
  };
27013
27049
  }
27014
27050
  componentDidMount() {
@@ -27017,6 +27053,25 @@ class AvailableItems$1 extends Component {
27017
27053
  loan
27018
27054
  } = this.props;
27019
27055
  fetchAvailableItems(loan.metadata.document_pid);
27056
+ this.loadIdentifierTitles();
27057
+ }
27058
+ componentWillUnmount() {
27059
+ if (this.cancellableFetchIdentifierTitles) {
27060
+ this.cancellableFetchIdentifierTitles.cancel();
27061
+ }
27062
+ }
27063
+ async loadIdentifierTitles() {
27064
+ this.cancellableFetchIdentifierTitles = withCancel(fetchIdentifierTitles(invenioConfig.ITEMS.identifiersToDisplay, invenioConfig.VOCABULARIES.item.identifier.scheme));
27065
+ try {
27066
+ let result = await this.cancellableFetchIdentifierTitles.promise;
27067
+ this.setState({
27068
+ identifiersToDisplay: result
27069
+ });
27070
+ } catch (error) {
27071
+ if (error !== 'UNMOUNTED') {
27072
+ console.error('Error fetching identifier titles for items.', error);
27073
+ }
27074
+ }
27020
27075
  }
27021
27076
  assignItemButton(item) {
27022
27077
  const {
@@ -27087,18 +27142,31 @@ class AvailableItems$1 extends Component {
27087
27142
  data
27088
27143
  } = this.props;
27089
27144
  const {
27090
- filteredData
27145
+ filteredData,
27146
+ identifiersToDisplay
27091
27147
  } = this.state;
27148
+ const identifierColumns = identifiersToDisplay.map(identifier => ({
27149
+ title: identifier.text,
27150
+ field: `metadata.identifiers`,
27151
+ formatter: _ref2 => {
27152
+ var _row$metadata$identif;
27153
+ let {
27154
+ row
27155
+ } = _ref2;
27156
+ const entry = (_row$metadata$identif = row.metadata.identifiers) === null || _row$metadata$identif === void 0 ? void 0 : _row$metadata$identif.find(id => id.scheme === identifier.key);
27157
+ return entry === null || entry === void 0 ? void 0 : entry.value;
27158
+ }
27159
+ }));
27092
27160
  const columns = [{
27093
27161
  title: 'PID',
27094
27162
  field: 'metadata.pid'
27095
27163
  }, {
27096
27164
  title: 'Barcode',
27097
27165
  field: 'metadata.barcode',
27098
- formatter: _ref2 => {
27166
+ formatter: _ref3 => {
27099
27167
  let {
27100
27168
  row
27101
- } = _ref2;
27169
+ } = _ref3;
27102
27170
  return /*#__PURE__*/React.createElement(Link$1, {
27103
27171
  to: BackOfficeRoutes.itemDetailsFor(row.metadata.pid)
27104
27172
  }, row.metadata.barcode);
@@ -27115,7 +27183,7 @@ class AvailableItems$1 extends Component {
27115
27183
  }, {
27116
27184
  title: 'Shelf',
27117
27185
  field: 'metadata.shelf'
27118
- }, {
27186
+ }, ...identifierColumns, {
27119
27187
  title: 'Actions',
27120
27188
  field: '',
27121
27189
  formatter: this.rowActionButton
@@ -28104,15 +28172,20 @@ const LoanDetails = connect(mapStateToProps$K, mapDispatchToProps$z)(LoanDetails
28104
28172
  class _SearchDateRange extends Component {
28105
28173
  constructor() {
28106
28174
  super(...arguments);
28107
- this.onDateChange = newFilter => {
28175
+ this.onDateChange = (field, value) => {
28108
28176
  const {
28109
28177
  currentQueryState,
28110
28178
  updateQueryState
28111
28179
  } = this.props;
28112
- const filters = currentQueryState.filters;
28113
- filters.push(newFilter);
28180
+ const updatedFilters = currentQueryState.filters.filter(_ref => {
28181
+ let [name] = _ref;
28182
+ return name !== field;
28183
+ });
28184
+ if (value !== '') {
28185
+ updatedFilters.push([field, value]);
28186
+ }
28114
28187
  return updateQueryState({
28115
- filters: filters
28188
+ filters: updatedFilters
28116
28189
  });
28117
28190
  };
28118
28191
  }
@@ -28124,8 +28197,8 @@ class _SearchDateRange extends Component {
28124
28197
  } = this.props;
28125
28198
  let fromDate = '';
28126
28199
  let toDate = '';
28127
- filters.forEach(_ref => {
28128
- let [name, value] = _ref;
28200
+ filters.forEach(_ref2 => {
28201
+ let [name, value] = _ref2;
28129
28202
  if (name === 'loans_from_date') fromDate = value;
28130
28203
  if (name === 'loans_to_date') toDate = value;
28131
28204
  });
@@ -28137,12 +28210,12 @@ class _SearchDateRange extends Component {
28137
28210
  maxDate: toDate,
28138
28211
  defaultValue: fromDate,
28139
28212
  placeholder: "From",
28140
- handleDateChange: value => this.onDateChange(['loans_from_date', value])
28213
+ handleDateChange: value => this.onDateChange('loans_from_date', value)
28141
28214
  })), /*#__PURE__*/React.createElement(Card.Content, null, /*#__PURE__*/React.createElement(DatePicker$1, {
28142
28215
  minDate: fromDate,
28143
28216
  defaultValue: toDate,
28144
28217
  placeholder: "To",
28145
- handleDateChange: value => this.onDateChange(['loans_to_date', value])
28218
+ handleDateChange: value => this.onDateChange('loans_to_date', value)
28146
28219
  })));
28147
28220
  }
28148
28221
  }
@@ -28253,6 +28326,11 @@ const schema$3 = () => {
28253
28326
  physical_location: {
28254
28327
  type: 'string',
28255
28328
  title: 'Physical location'
28329
+ },
28330
+ restricted: {
28331
+ type: 'boolean',
28332
+ title: 'Restricted access (not accessible to patrons)',
28333
+ default: false
28256
28334
  }
28257
28335
  }
28258
28336
  };
@@ -28273,6 +28351,8 @@ const uiSchema$3 = title => {
28273
28351
  }, {
28274
28352
  physical_location: 8,
28275
28353
  notes: 8
28354
+ }, {
28355
+ restricted: 8
28276
28356
  }],
28277
28357
  'custom:root': {
28278
28358
  'custom:formTitle': title
@@ -29342,6 +29422,16 @@ class InternalLocationList$1 extends Component {
29342
29422
  }, {
29343
29423
  title: 'Location e-mail',
29344
29424
  field: 'metadata.location.email'
29425
+ }, {
29426
+ title: 'Restricted access',
29427
+ formatter: _ref2 => {
29428
+ let {
29429
+ row
29430
+ } = _ref2;
29431
+ return row.metadata.restricted ? /*#__PURE__*/React.createElement("i", {
29432
+ className: "check icon"
29433
+ }) : null;
29434
+ }
29345
29435
  }, {
29346
29436
  title: 'Actions',
29347
29437
  field: '',
@@ -29505,19 +29595,108 @@ const mapDispatchToProps$v = dispatch => ({
29505
29595
  });
29506
29596
  const LocationList = connect(mapStateToProps$G, mapDispatchToProps$v)(LocationList$1);
29507
29597
 
29598
+ const OverbookedConfirmModal = _ref => {
29599
+ let {
29600
+ open,
29601
+ onClose,
29602
+ onConfirm,
29603
+ overbookedDocuments
29604
+ } = _ref;
29605
+ const isMultiple = overbookedDocuments.length > 1;
29606
+ return /*#__PURE__*/React.createElement(Modal, {
29607
+ size: "small",
29608
+ open: open,
29609
+ onClose: onClose
29610
+ }, /*#__PURE__*/React.createElement(Modal.Header, null, "Item in high demand!"), /*#__PURE__*/React.createElement(Modal.Content, null, overbookedDocuments.map((doc, index) => /*#__PURE__*/React.createElement("p", {
29611
+ key: doc.loanRequestId || doc.title || index
29612
+ }, "There is another patron waiting for \"", /*#__PURE__*/React.createElement("strong", null, doc.title), "\"", ' ', doc.loanRequestId && /*#__PURE__*/React.createElement(React.Fragment, null, "(", /*#__PURE__*/React.createElement("a", {
29613
+ href: `/backoffice/loans/${doc.loanRequestId}`
29614
+ }, "See loan request"), ")"), ".")), /*#__PURE__*/React.createElement("p", null, /*#__PURE__*/React.createElement("strong", null, "Do you still want to extend your ", isMultiple ? 'loans' : 'loan', "?"))), /*#__PURE__*/React.createElement(Modal.Actions, null, /*#__PURE__*/React.createElement(Button, {
29615
+ secondary: true,
29616
+ onClick: onClose
29617
+ }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
29618
+ primary: true,
29619
+ onClick: onConfirm
29620
+ }, "Extend")));
29621
+ };
29622
+
29508
29623
  class PatronBulkExtendLoans$1 extends Component {
29509
29624
  constructor() {
29510
29625
  super(...arguments);
29511
29626
  this.state = {
29512
- open: false
29627
+ open: false,
29628
+ showOverbookedConfirm: false,
29629
+ overbookedDocuments: [],
29630
+ isChecking: false
29631
+ };
29632
+ this.open = async () => {
29633
+ this.setState({
29634
+ open: true
29635
+ });
29636
+ if (!isPrivilegedUser()) return;
29637
+ this.setState({
29638
+ isChecking: true
29639
+ });
29640
+ try {
29641
+ const {
29642
+ patronPid
29643
+ } = this.props;
29644
+ // Get all active loans for the patron
29645
+ const query = loanApi.query().withPatronPid(patronPid).withState(invenioConfig.CIRCULATION.loanActiveStates).withSize(invenioConfig.APP.PATRON_PROFILE_MAX_RESULTS_SIZE).sortByNewest().qs();
29646
+ const response = await loanApi.list(query);
29647
+ const loans = response.data.hits;
29648
+ const checks = loans.map(async loan => {
29649
+ const documentPid = _get(loan, 'metadata.document.pid');
29650
+ const isOverbooked = await isDocumentOverbooked(documentPid);
29651
+ if (isOverbooked) {
29652
+ return {
29653
+ title: loan.metadata.document.title,
29654
+ loanRequestId: loan.id
29655
+ };
29656
+ }
29657
+ return null;
29658
+ });
29659
+ const results = await Promise.all(checks);
29660
+ const overbookedDocuments = results.filter(Boolean);
29661
+ this.setState({
29662
+ overbookedDocuments,
29663
+ isChecking: false
29664
+ });
29665
+ } catch (error) {
29666
+ console.error('Failed to fetch overbooked documents', error);
29667
+ this.setState({
29668
+ isChecking: false
29669
+ });
29670
+ }
29671
+ };
29672
+ this.close = () => {
29673
+ this.setState({
29674
+ open: false,
29675
+ showOverbookedConfirm: false,
29676
+ overbookedDocuments: []
29677
+ });
29513
29678
  };
29514
- this.open = () => this.setState({
29515
- open: true
29516
- });
29517
- this.close = () => this.setState({
29518
- open: false
29519
- });
29520
29679
  this.handleSubmitExtend = () => {
29680
+ const {
29681
+ bulkLoanExtension,
29682
+ patronPid
29683
+ } = this.props;
29684
+ const {
29685
+ overbookedDocuments
29686
+ } = this.state;
29687
+
29688
+ // If the user is privileged and there are overbooked docs
29689
+ if (isPrivilegedUser() && overbookedDocuments.length > 0) {
29690
+ this.setState({
29691
+ showOverbookedConfirm: true
29692
+ });
29693
+ return;
29694
+ }
29695
+ // Otherwise extend directly
29696
+ bulkLoanExtension(patronPid);
29697
+ this.close();
29698
+ };
29699
+ this.confirmBulkExtension = () => {
29521
29700
  const {
29522
29701
  bulkLoanExtension,
29523
29702
  patronPid
@@ -29528,16 +29707,26 @@ class PatronBulkExtendLoans$1 extends Component {
29528
29707
  }
29529
29708
  render() {
29530
29709
  const {
29531
- patronPid,
29532
- bulkLoanExtension,
29533
29710
  isLoading,
29534
29711
  disabled,
29712
+ patronPid,
29713
+ bulkLoanExtension,
29535
29714
  ...uiProps
29536
29715
  } = this.props;
29537
29716
  const {
29538
- open
29717
+ open,
29718
+ showOverbookedConfirm,
29719
+ overbookedDocuments,
29720
+ isChecking
29539
29721
  } = this.state;
29540
- return /*#__PURE__*/React.createElement(Modal, {
29722
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(OverbookedConfirmModal, {
29723
+ open: showOverbookedConfirm,
29724
+ onClose: () => this.setState({
29725
+ showOverbookedConfirm: false
29726
+ }),
29727
+ onConfirm: this.confirmBulkExtension,
29728
+ overbookedDocuments: overbookedDocuments
29729
+ }), /*#__PURE__*/React.createElement(Modal, {
29541
29730
  open: open,
29542
29731
  onClose: this.close,
29543
29732
  onOpen: this.open,
@@ -29557,8 +29746,9 @@ class PatronBulkExtendLoans$1 extends Component {
29557
29746
  onClick: this.close
29558
29747
  }, "Cancel"), /*#__PURE__*/React.createElement(Button, {
29559
29748
  onClick: this.handleSubmitExtend,
29560
- primary: true
29561
- }, "Extend the loans")));
29749
+ primary: true,
29750
+ loading: isChecking
29751
+ }, "Extend the loans"))));
29562
29752
  }
29563
29753
  }
29564
29754
  PatronBulkExtendLoans$1.defaultProps = {
@@ -35278,21 +35468,6 @@ class DocumentStats extends Component {
35278
35468
  class DocumentItem extends Component {
35279
35469
  constructor(props) {
35280
35470
  super(props);
35281
- this.fetchIdentifiersToDisplayInFrontsideTitles = () => {
35282
- const query = vocabularyApi.query().withType(invenioConfig.VOCABULARIES.item.identifier.scheme);
35283
- vocabularyApi.list(query.qs()).then(response => {
35284
- const identifiersToDisplayInFrontside = this.state.identifiersToDisplayInFrontside.map(identifier => {
35285
- const vocabEntry = response.data.hits.find(entry => entry.metadata.key === identifier.key);
35286
- return {
35287
- ...identifier,
35288
- text: vocabEntry ? vocabEntry.metadata.text : identifier.text
35289
- };
35290
- });
35291
- this.setState({
35292
- identifiersToDisplayInFrontside
35293
- });
35294
- });
35295
- };
35296
35471
  this.toggleItems = () => {
35297
35472
  const {
35298
35473
  isShowingAll
@@ -35301,18 +35476,10 @@ class DocumentItem extends Component {
35301
35476
  isShowingAll: !isShowingAll
35302
35477
  });
35303
35478
  };
35304
- const _identifiersToDisplayInFrontside = invenioConfig.ITEMS.identifiersToDisplayInFrontside.map(identifier => ({
35305
- key: identifier,
35306
- text: identifier
35307
- }));
35308
35479
  this.state = {
35309
35480
  isShowingAll: false,
35310
- itemAmountLimit: 5,
35311
- identifiersToDisplayInFrontside: _identifiersToDisplayInFrontside
35481
+ itemAmountLimit: 5
35312
35482
  };
35313
- if (_identifiersToDisplayInFrontside.length > 0) {
35314
- this.fetchIdentifiersToDisplayInFrontsideTitles();
35315
- }
35316
35483
  }
35317
35484
  get moreItemsToLoad() {
35318
35485
  const {
@@ -35328,12 +35495,12 @@ class DocumentItem extends Component {
35328
35495
  internalLocationName,
35329
35496
  items,
35330
35497
  documentDetails,
35331
- showTitle
35498
+ showTitle,
35499
+ identifiersToDisplay
35332
35500
  } = this.props;
35333
35501
  const {
35334
35502
  isShowingAll,
35335
- itemAmountLimit,
35336
- identifiersToDisplayInFrontside
35503
+ itemAmountLimit
35337
35504
  } = this.state;
35338
35505
  const previewArrayOfItems = items.slice(0, itemAmountLimit);
35339
35506
  const completeArrayOfItems = items;
@@ -35351,14 +35518,14 @@ class DocumentItem extends Component {
35351
35518
  id: "DocumentDetails.DocumentItem.TableHeader"
35352
35519
  }, /*#__PURE__*/React.createElement(Table.Row, {
35353
35520
  "data-test": "header"
35354
- }, /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Barcode"), /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Shelf"), identifiersToDisplayInFrontside.map(identifier => /*#__PURE__*/React.createElement(Table.HeaderCell, {
35521
+ }, /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Barcode"), /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Shelf"), identifiersToDisplay.map(identifier => /*#__PURE__*/React.createElement(Table.HeaderCell, {
35355
35522
  key: identifier.key
35356
35523
  }, identifier.text)), /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Status"), /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Medium"), /*#__PURE__*/React.createElement(Table.HeaderCell, null, "Loan restriction")))), /*#__PURE__*/React.createElement(Table.Body, null, /*#__PURE__*/React.createElement(Overridable, {
35357
35524
  id: "DocumentDetails.DocumentItemTableBody"
35358
35525
  }, /*#__PURE__*/React.createElement(DocumentItemBody, {
35359
35526
  items: itemsToShow,
35360
35527
  documentDetails: documentDetails,
35361
- identifiersToDisplayInFrontside: identifiersToDisplayInFrontside
35528
+ identifiersToDisplay: identifiersToDisplay
35362
35529
  }))), this.moreItemsToLoad && /*#__PURE__*/React.createElement(Table.Footer, {
35363
35530
  fullWidth: true,
35364
35531
  "data-test": "footer",
@@ -35450,7 +35617,8 @@ class DocumentTabs extends Component {
35450
35617
  };
35451
35618
  this.createInternalLocationTables = locationsObject => {
35452
35619
  const {
35453
- activeInternalLocation
35620
+ activeInternalLocation,
35621
+ identifiersToDisplay
35454
35622
  } = this.state;
35455
35623
  const sortedInternalLocationEntries = this.sortedLocationEntries(Object.entries(locationsObject));
35456
35624
  if (activeInternalLocation) {
@@ -35464,7 +35632,8 @@ class DocumentTabs extends Component {
35464
35632
  internalLocationName: internalLocationName,
35465
35633
  items: items,
35466
35634
  documentDetails: this.documentDetails,
35467
- showTitle: activeInternalLocation !== internalLocationName
35635
+ showTitle: activeInternalLocation !== internalLocationName,
35636
+ identifiersToDisplay: identifiersToDisplay
35468
35637
  });
35469
35638
  }
35470
35639
  return sortedInternalLocationEntries.map(_ref6 => {
@@ -35475,7 +35644,8 @@ class DocumentTabs extends Component {
35475
35644
  internalLocationName: internalLocationName,
35476
35645
  items: items,
35477
35646
  documentDetails: this.documentDetails,
35478
- showTitle: activeInternalLocation !== internalLocationName
35647
+ showTitle: activeInternalLocation !== internalLocationName,
35648
+ identifiersToDisplay: identifiersToDisplay
35479
35649
  });
35480
35650
  });
35481
35651
  };
@@ -35499,9 +35669,34 @@ class DocumentTabs extends Component {
35499
35669
  const [firstLocationName] = this.locationEntries[0];
35500
35670
  const [_firstInternalLocationName] = this.firstInternalLocationEntry(firstLocationName);
35501
35671
  this.state = {
35502
- activeInternalLocation: _firstInternalLocationName
35672
+ activeInternalLocation: _firstInternalLocationName,
35673
+ identifiersToDisplay: invenioConfig.ITEMS.identifiersToDisplay.map(s => ({
35674
+ key: s,
35675
+ text: s
35676
+ }))
35503
35677
  };
35504
35678
  }
35679
+ componentDidMount() {
35680
+ this.loadIdentifierTitles();
35681
+ }
35682
+ componentWillUnmount() {
35683
+ if (this.cancellableFetchIdentifierTitles) {
35684
+ this.cancellableFetchIdentifierTitles.cancel();
35685
+ }
35686
+ }
35687
+ async loadIdentifierTitles() {
35688
+ this.cancellableFetchIdentifierTitles = withCancel(fetchIdentifierTitles(invenioConfig.ITEMS.identifiersToDisplay, invenioConfig.VOCABULARIES.item.identifier.scheme));
35689
+ try {
35690
+ let result = await this.cancellableFetchIdentifierTitles.promise;
35691
+ this.setState({
35692
+ identifiersToDisplay: result
35693
+ });
35694
+ } catch (error) {
35695
+ if (error !== 'UNMOUNTED') {
35696
+ console.error('Error fetching identifier titles for items.', error);
35697
+ }
35698
+ }
35699
+ }
35505
35700
  get locations() {
35506
35701
  const {
35507
35702
  locationsObject
@@ -38906,6 +39101,36 @@ class LoanExtendButton extends Component {
38906
39101
  constructor(props) {
38907
39102
  super(props);
38908
39103
  this.triggerExtension = async () => {
39104
+ const {
39105
+ loan,
39106
+ onError
39107
+ } = this.props;
39108
+ const isUserPrivileged = isPrivilegedUser();
39109
+ if (isUserPrivileged) {
39110
+ this.setState({
39111
+ isLoading: true
39112
+ });
39113
+ try {
39114
+ const documentPid = _get(loan, 'metadata.document.pid');
39115
+ const isOverbooked = await isDocumentOverbooked(documentPid);
39116
+ if (isOverbooked) {
39117
+ this.setState({
39118
+ isLoading: false,
39119
+ showOverbookedConfirm: true
39120
+ });
39121
+ return;
39122
+ }
39123
+ } catch (error) {
39124
+ onError('Something went wrong while checking if the literature is overbooked.', error);
39125
+ this.setState({
39126
+ isLoading: false
39127
+ });
39128
+ return;
39129
+ }
39130
+ }
39131
+ await this.performExtension();
39132
+ };
39133
+ this.performExtension = async () => {
38909
39134
  const {
38910
39135
  loan,
38911
39136
  onSuccess,
@@ -38923,7 +39148,8 @@ class LoanExtendButton extends Component {
38923
39148
  this.cancellableExtendLoan = withCancel(this.extendLoan(extendUrl, document.pid, patronPid));
38924
39149
  const response = await this.cancellableExtendLoan.promise;
38925
39150
  this.setState({
38926
- isLoading: false
39151
+ isLoading: false,
39152
+ showOverbookedConfirm: false
38927
39153
  });
38928
39154
  const documentTitle = document.title;
38929
39155
  onSuccess(INFO_MESSAGES.SUCCESS(documentTitle, DateTime.fromISO(response.data.metadata.end_date)));
@@ -38934,6 +39160,11 @@ class LoanExtendButton extends Component {
38934
39160
  onError(error.response.data.message);
38935
39161
  }
38936
39162
  };
39163
+ this.hideOverbookedConfirm = () => {
39164
+ this.setState({
39165
+ showOverbookedConfirm: false
39166
+ });
39167
+ };
38937
39168
  this.validate = loan => {
38938
39169
  const hasExtendAction = _has(loan, 'availableActions.extend');
38939
39170
  if (!hasExtendAction) return {
@@ -38961,7 +39192,8 @@ class LoanExtendButton extends Component {
38961
39192
  };
38962
39193
  };
38963
39194
  this.state = {
38964
- isLoading: false
39195
+ isLoading: false,
39196
+ showOverbookedConfirm: false
38965
39197
  };
38966
39198
  }
38967
39199
  componentWillUnmount() {
@@ -38980,11 +39212,21 @@ class LoanExtendButton extends Component {
38980
39212
  loan
38981
39213
  } = this.props;
38982
39214
  const {
38983
- isLoading
39215
+ isLoading,
39216
+ showOverbookedConfirm
38984
39217
  } = this.state;
38985
39218
  const validation = this.validate(loan);
38986
39219
  const isDisabled = !validation.isValid;
38987
- return /*#__PURE__*/React.createElement(React.Fragment, null, isDisabled && /*#__PURE__*/React.createElement(Popup, {
39220
+ const overbookedDocuments = [{
39221
+ title: _get(loan, 'metadata.document.title', 'Unknown title'),
39222
+ loanRequestId: loan.id
39223
+ }];
39224
+ return /*#__PURE__*/React.createElement(React.Fragment, null, showOverbookedConfirm && /*#__PURE__*/React.createElement(OverbookedConfirmModal, {
39225
+ open: showOverbookedConfirm,
39226
+ onClose: this.hideOverbookedConfirm,
39227
+ onConfirm: this.performExtension,
39228
+ overbookedDocuments: overbookedDocuments
39229
+ }), isDisabled && /*#__PURE__*/React.createElement(Popup, {
38988
39230
  content: validation.msg,
38989
39231
  trigger: /*#__PURE__*/React.createElement(Icon, {
38990
39232
  name: "info"