@dbp-topics/sublibrary
Version:
[GitLab Repository](https://github.com/digital-blueprint/sublibrary-app) | [npmjs package](https://www.npmjs.com/package/@dbp-topics/sublibrary) | [Unpkg CDN](https://unpkg.com/browse/@dbp-topics/sublibrary/) | [Sublibrary Bundle](https://gitlab.tugraz.at
391 lines (344 loc) • 14 kB
JavaScript
import {createInstance, i18nKey} from './i18n.js';
import {css, html} from 'lit';
import {ScopedElementsMixin} from '@open-wc/scoped-elements';
import {LibraryElement} from './library-element.js';
import {PersonSelect} from '@dbp-toolkit/person-select';
import {LibraryBookOfferSelect} from './library-book-offer-select.js';
import * as commonUtils from '@dbp-toolkit/common/utils';
import * as commonStyles from '@dbp-toolkit/common/styles';
import {MiniSpinner, Button} from '@dbp-toolkit/common';
import {classMap} from 'lit/directives/class-map.js';
import {getPersonDisplayName} from './utils.js';
import {LibrarySelect} from './library-select.js';
import {ReloadButton} from './reload-button.js';
class LibraryCreateLoan extends ScopedElementsMixin(LibraryElement) {
constructor() {
super();
this.auth = {};
this._i18n = createInstance();
this.lang = this._i18n.language;
this.entryPointUrl = '';
this.bookOfferId = '';
this.bookOffer = null;
this.personId = '';
this.person = null;
this.status = null;
this.sublibraryIri = '';
this.sublibrary = null;
this.sendButtonDisabled = true;
}
static get scopedElements() {
return {
'dbp-library-select': LibrarySelect,
'dbp-person-select': PersonSelect,
'dbp-sublibrary-book-offer-select': LibraryBookOfferSelect,
'dbp-mini-spinner': MiniSpinner,
'dbp-button': Button,
'dbp-reload-button': ReloadButton,
};
}
static get properties() {
return {
...super.properties,
lang: {type: String},
entryPointUrl: {type: String, attribute: 'entry-point-url'},
bookOfferId: {type: String, attribute: 'book-offer-id', reflect: true},
bookOffer: {type: Object, attribute: false},
personId: {type: String, attribute: 'person-id', reflect: true},
status: {type: Object},
sublibraryIri: {type: String, attribute: 'sublibrary-iri', reflect: true},
sendButtonDisabled: {type: Boolean, attribute: false},
auth: {type: Object},
};
}
connectedCallback() {
super.connectedCallback();
}
update(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === 'lang') {
this._i18n.changeLanguage(this.lang);
}
});
super.update(changedProperties);
}
static get styles() {
// language=css
return css`
${commonStyles.getThemeCSS()}
${commonStyles.getGeneralCSS()}
${commonStyles.getNotificationCSS()}
.hidden {
display: none;
}
#create-loan-block {
display: none;
}
dbp-sublibrary-book-offer-select {
width: 100%;
margin-right: 4px;
}
.book-offer-select-container {
display: flex;
}
`;
}
async onBookSelectChanged(e) {
await this.updateCreateLoan();
}
async updateCreateLoan() {
const select = this.shadowRoot.querySelector("dbp-sublibrary-book-offer-select");
let bookOffer = select.dataset.object;
const createLoanBlock = this.shadowRoot.querySelector('#create-loan-block');
const loansLoadingIndicator = this.shadowRoot.querySelector('#loans-loading');
this.status = null;
if (!bookOffer) {
this.status = null;
this.bookOffer = null;
this.bookOfferId = '';
createLoanBlock.style.display = 'none';
return;
}
bookOffer = JSON.parse(bookOffer);
const bookOfferId = bookOffer['@id'];
this.bookOffer = bookOffer;
this.bookOfferId = bookOfferId;
const apiUrl = this.entryPointUrl + this.bookOfferId + '/loans';
// set book-offer-id of the custom element
this.setAttribute('book-offer-id', this.bookOfferId);
// fire a change event
this.dispatchEvent(
new CustomEvent('change', {
detail: {
type: 'book-offer-id',
value: this.bookOfferId,
},
})
);
// TODO: check if library of book matches person's functions
loansLoadingIndicator.style.display = 'block';
let result = null;
try {
// check if there are already loans on this book offer
result = await fetch(apiUrl, {
headers: {
'Content-Type': 'application/ld+json',
Authorization: 'Bearer ' + this.auth.token,
},
});
if (!result.ok) throw result;
result = await result.json();
} catch (error) {
await this.handleFetchError(error, this._i18n.t('renew-loan.error-load-loans-summary'));
return;
} finally {
loansLoadingIndicator.style.display = 'none';
}
const loans = result['hydra:member'];
if (loans.length > 0) {
this.status = {
summary: i18nKey('create-loan.error-existing-loans-summary'),
body: i18nKey('create-loan.error-existing-loans-body'),
type: 'danger',
};
} else {
this.status = {
summary: i18nKey('create-loan.info-no-existing-loans-summary'),
body: i18nKey('create-loan.info-no-existing-loans-body'),
type: 'info',
};
createLoanBlock.style.display = 'block';
}
}
getSublibraryCode() {
//console.log('getSublibraryCode() sublibraryIri = ' + this.sublibraryIri);
// until the API understands this:
//this.sublibraryIri == '/organizations/1263-F2190';
// extracting the orgUnitCode (F2190) is done here:
return this.sublibrary.code;
}
onPersonSelectChanged(e) {
const select = e.target;
const person = JSON.parse(select.dataset.object);
const personId = person['@id'];
this.sendButtonDisabled = false;
this.personId = personId;
this.person = person;
// fire a change event
this.dispatchEvent(
new CustomEvent('change', {
detail: {
type: 'person-id',
value: this.personId,
},
})
);
}
async onSubmitClicked(e) {
e.preventDefault();
const button = e.currentTarget;
try {
await this.onSubmitClickedInternal(e);
} finally {
button.stop();
}
}
async onSubmitClickedInternal(e) {
const dateSelect = this._("input[type='date']");
const timeSelect = this._("input[type='time']");
let isoString = dateSelect.value;
if (timeSelect.value) isoString += 'T' + timeSelect.value;
const date = new Date(isoString);
// check if selected date is in the past
if (date < new Date()) {
this.status = {
summary: this._i18n.t('error-summary'),
body: this._i18n.t('renew-loan.error-renew-loan-date-in-past'),
type: 'danger',
};
return;
}
const apiUrl = this.entryPointUrl + this.bookOfferId + '/loans';
const data = {
borrower: this.personId,
library: this.getSublibraryCode(),
endTime: date.toISOString(),
};
let response = await fetch(apiUrl, {
method: 'POST',
headers: {
Accept: 'application/ld+json',
'Content-Type': 'application/json',
Authorization: 'Bearer ' + this.auth.token,
},
body: JSON.stringify(data),
});
if (response.ok) {
// clear book offer select to hide "loan" button
const bookOfferSelect = this._(
this.getScopedTagName('dbp-sublibrary-book-offer-select')
);
bookOfferSelect.clear();
this.status = {
summary: i18nKey('create-loan.success-summary'),
body: this._i18n.t('create-loan.success-body', {
personName: getPersonDisplayName(this.person),
}),
type: 'info',
};
} else {
await this.handleFetchError(response);
}
}
onSublibraryChanged(e) {
this.sublibraryIri = e.detail.value;
this.sublibrary = e.detail.object;
}
onReloadButtonClicked(e) {
this.updateCreateLoan();
}
render() {
const minDate = new Date().toISOString();
let date = new Date();
date.setMonth(date.getMonth() + 1);
const loanDate = date.toISOString();
const i18n = this._i18n;
return html`
<form
class="${classMap({
hidden: !this.isLoggedIn() || !this.hasLibraryPermissions() || this.isLoading(),
})}">
<div class="field">
<label class="label">${i18n.t('organization-select.label')}</label>
<div class="control">
<dbp-library-select
subscribe="lang:lang,entry-point-url:entry-point-url,auth:auth"
value="${this.sublibraryIri}"
@change="${this.onSublibraryChanged}"></dbp-library-select>
</div>
</div>
<div class="field">
<label class="label">${i18n.t('person-select.headline')}</label>
<div class="control">
<dbp-person-select
subscribe="lang:lang,entry-point-url:entry-point-url,auth:auth"
@change=${this.onPersonSelectChanged}
value="${this.personId}"
show-details>
</dbp-person-select>
</div>
</div>
<div class="field">
<label class="label">${i18n.t('library-book-offer-select.headline')}</label>
<div class="control book-offer-select-container">
<dbp-sublibrary-book-offer-select
subscribe="auth:auth,lang:lang,entry-point-url:entry-point-url,auth:auth"
@change=${this.onBookSelectChanged}
@unselect=${this.onBookSelectChanged}
value="${this.bookOfferId}"
sublibrary-iri="${this.sublibraryIri}"></dbp-sublibrary-book-offer-select>
<dbp-reload-button
?disabled=${!this.bookOffer}
@click=${this.onReloadButtonClicked}
title="${this.bookOffer
? i18n.t('shelving.button-refresh-title', {
name: this.bookOffer.name,
})
: ''}"
></dbp-reload-button>
</div>
</div>
<dbp-mini-spinner
id="loans-loading"
text="${i18n.t('create-loan.mini-spinner-text')}"
style="font-size: 2em; display: none;"></dbp-mini-spinner>
<div id="create-loan-block">
<div class="field">
<label class="label">${i18n.t('renew-loan.end-date')}</label>
<input
class="input"
type="date"
min="${commonUtils.dateToInputDateString(minDate)}"
value="${commonUtils.dateToInputDateString(loanDate)}" />
<input type="time" class="hidden" value="23:59:59" />
</div>
<div class="field">
<div class="control">
<dbp-button
id="send"
@click=${this.onSubmitClicked}
value="${i18n.t('create-loan.submit')}"
?disabled="${this.sendButtonDisabled}"
type=""></dbp-button>
</div>
</div>
</div>
${this.status
? html`
<br />
<div class="notification is-${this.status.type}">
<h4>${i18n.t(this.status.summary)}</h4>
${i18n.t(this.status.body)}
</div>
`
: ``}
</form>
<div
class="notification is-warning ${classMap({
hidden: this.isLoggedIn() || this.isLoading(),
})}">
${i18n.t('error-login-message')}
</div>
<div
class="notification is-danger ${classMap({
hidden: this.hasLibraryPermissions() || !this.isLoggedIn() || this.isLoading(),
})}">
${i18n.t('error-permission-message')}
</div>
<div class="${classMap({hidden: !this.isLoading()})}">
<dbp-mini-spinner></dbp-mini-spinner>
</div>
`;
}
}
commonUtils.defineCustomElement('dbp-sublibrary-create-loan', LibraryCreateLoan);