import React, {Component} from 'react';
import {CKEditor} from '@ckeditor/ckeditor5-react';
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
import * as apiCalls from "../apiCalls/apiCalls";
import handleError from "../shared/failureHandler";
import Input from "../components/Input";
import ButtonWithProgress from "../components/ButtonWithProgress";
import LocationPicker from "../components/LocationPicker";
import PageContentContainer from "../components/PageContentContainer";
import Breadcrumbs from "../components/Breadcrumbs";
import BreadcrumbsLink from "../components/BreadcrumbsLink";
import PageInfo from "../components/PageInfo";
/**
* Page where institution manager can add new exhibits
*/
class AddExhibitPage extends Component {
/**
* initial state of page
*/
initState = {
name: "",
infoLabelText: "",
buildingId: null,
roomId: null,
showcaseId: null,
encodedImage: null,
imageSelect: "",
encodedInfoLabel: null,
infoLabelSelect: "",
pendingApiCall: false,
created: false,
errors: {},
}
/**
* page state
*/
state = this.initState;
/**
* Called on text input values change
* @param event input event
*/
onChange = (event) => {
// delete error to given field
const errors = {...this.state.errors};
delete errors[event.target.name];
// update state
this.setState({errors, [event.target.name]: event.target.value, created: false});
}
/**
* Called when new image is selected
* @param event input event
*/
onImageSelect = (event) => {
const errors = {...this.state.errors};
const name = event.target.name;
// delete input error message
if(name === "imageSelect") {
delete errors["encodedImage"];
}
else {
delete errors["encodedInfoLabel"];
}
// update state with new errors and close alert that new exhibit was created
this.setState({errors, [name]: event.target.value, created: false});
if (event.target.files.length === 0) {
return;
}
// if user chose file
const file = event.target.files[0];
let reader = new FileReader();
reader.onloadend = () => {
// update state with new base64 encoded selected image
if(name === "imageSelect") {
this.setState({encodedImage: reader.result});
}
else {
this.setState({encodedInfoLabel: reader.result});
}
}
// read image
reader.readAsDataURL(file);
}
/**
* Clears image from state
* @param name img name
*/
clearImage = (name) => {
const errors = {...this.state.errors};
if(name === "imageSelect") {
// delete errors of image input and its fields in state
delete errors["encodedImage"];
this.setState({errors, encodedImage: null, imageSelect: "",});
}
else {
// delete errors of image input and its fields in state
delete errors["encodedInfoLabel"];
this.setState({errors, encodedInfoLabel: null, infoLabelSelect: "",});
}
}
/**
* Called when info label text is chagned
* @param event input event
* @param editor input editor
*/
onInfoLabelTextChange = (event, editor) => {
const errors = {...this.state.errors};
delete errors["infoLabelText"];
// update state with new value, no errors and closed alert of created exhibit
this.setState({infoLabelText: editor.getData(), created: false, errors});
}
/**
* Called when new exhibit is created
*/
onClickCreate = (e) => {
e.preventDefault();
this.setState({pendingApiCall: true});
// extract exhibit from state
const exhibit = {
name: this.state.name,
infoLabelText: this.state.infoLabelText,
buildingId: this.state.buildingId,
roomId: this.state.roomId,
showcaseId: this.state.showcaseId,
encodedImage: this.state.encodedImage,
encodedInfoLabel: this.state.encodedInfoLabel,
}
// send new exhibit to server
apiCalls.addExhibit(exhibit).then(response => {
let currBuildingId = this.state.buildingId;
let currRoomId = this.state.roomId;
let currShowcaseId = this.state.showcaseId;
this.setState({...this.initState}, () => this.setState({created: true, buildingId: currBuildingId, roomId: currRoomId, showcaseId: currShowcaseId}));
}).catch(error => {
// react on unauthorized state
return handleError(error);
}).catch(apiError => {
// react on errors in user input
let errors = {...this.state.errors};
if (apiError.response.data && apiError.response.data.validationErrors) {
errors = {...apiError.response.data.validationErrors}
}
this.setState({pendingApiCall: false, errors});
});
}
/**
* updates new info about selected building, room and showcase
* @param buildingId building id
* @param roomId room id
* @param showcaseId showcase id
*/
updateLocation = (buildingId, roomId, showcaseId) => {
this.setState({buildingId, roomId, showcaseId});
}
/**
* renders page
* @returns {JSX.Element} page
*/
render() {
const {
name,
infoLabelText,
encodedImage,
imageSelect,
encodedInfoLabel,
infoLabelSelect,
pendingApiCall,
created,
errors,
} = this.state;
// define disable submit value
let disabledSubmit = false;
if (name === "" || encodedInfoLabel === null) {
disabledSubmit = true;
}
// return page
return (
<PageContentContainer>
<Breadcrumbs>
<BreadcrumbsLink to="/myInstitution" name="My Institution"/>
<li className="breadcrumb-item active">New Exhibit</li>
</Breadcrumbs>
<PageInfo name="New Exhibit">Add a new exhibit with an information label for translation</PageInfo>
<form>
{
created &&
<div className="alert alert-success" role="alert">
Exhibit has been created
</div>
}
<div className="form-group">
<Input type="file"
onlyImage
value={imageSelect}
name="imageSelect"
label="Exhibit image"
placeholder="Select exhibit image"
onChange={this.onImageSelect}
hasError={errors.encodedImage && true}
error={errors.encodedImage}
/>
{
encodedImage &&
<div>
<img className="img-fluid sizedImg img-thumbnail mt-2" src={encodedImage} alt="upload" />
<br />
<button className="btn btn-danger btn-lg mt-2" onClick={() => this.clearImage("imageSelect")}>
<i className="fa fa-times" /> Clear
</button>
</div>
}
</div>
<div className="form-group">
<Input type="file"
onlyImage
value={infoLabelSelect}
name="infoLabelSelect"
label="Information label*"
placeholder="Select information label"
onChange={this.onImageSelect}
hasError={errors.encodedInfoLabel && true}
error={errors.encodedInfoLabel}
/>
{
encodedInfoLabel &&
<div>
<img className="img-fluid mt-2" src={encodedInfoLabel} alt="upload" />
<br />
<button className="btn btn-danger btn-lg mt-2" onClick={() => this.clearImage("infoLabelSelect")}>
<i className="fa fa-times" /> Clear
</button>
</div>
}
</div>
<div className="form-group">
<Input
label="Name*"
placeholder="Enter name" name="name" value={name}
onChange={this.onChange}
hasError={errors.name && true}
error={errors.name}
/>
</div>
<div className="form-group">
<label className="font-weight-bold">Information label text</label>
<CKEditor editor={ClassicEditor}
config={{removePlugins: ['Table', 'CKFinder', 'Link', 'TableToolbar', 'EasyImage', 'MediaEmbed', 'ImageCaption', 'ImageStyle', 'ImageToolbar', 'ImageUpload']}}
data={infoLabelText} onChange={this.onInfoLabelTextChange}/>
{
errors.infoLabelText &&
<div className="text-danger">
<small>{errors.infoLabelText}</small>
</div>
}
</div>
<div className="form-group">
<LocationPicker update={this.updateLocation} />
</div>
<ButtonWithProgress onClick={(e) => this.onClickCreate(e)}
className="btn btn-primary w-100 my-2"
disabled={pendingApiCall || disabledSubmit}
pendingApiCall={pendingApiCall}
hasChildren>
<i className="fa fa-paper-plane" /> New exhibit
</ButtonWithProgress>
</form>
</PageContentContainer>
);
}
}
export default AddExhibitPage;