2015-06-26 00:38:40 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import React from 'react';
|
2016-01-11 17:52:32 +01:00
|
|
|
import { Link } from 'react-router';
|
2015-11-03 10:57:41 +01:00
|
|
|
import Moment from 'moment';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
|
|
|
import Row from 'react-bootstrap/lib/Row';
|
|
|
|
import Col from 'react-bootstrap/lib/Col';
|
|
|
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|
|
|
|
2016-01-11 19:34:39 +01:00
|
|
|
import EditionActions from '../../actions/edition_actions';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2016-01-11 19:34:39 +01:00
|
|
|
import DetailProperty from './detail_property';
|
|
|
|
import EditionActionPanel from './edition_action_panel';
|
|
|
|
import FurtherDetails from './further_details';
|
|
|
|
import HistoryIterator from './history_iterator';
|
|
|
|
import LicenseDetail from './license_detail';
|
2015-07-08 22:54:07 +02:00
|
|
|
import MediaContainer from './media_container';
|
2016-01-11 19:34:39 +01:00
|
|
|
import Note from './note';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2016-01-11 19:34:39 +01:00
|
|
|
import CollapsibleParagraph from '../ascribe_collapsible/collapsible_paragraph';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2016-01-11 19:34:39 +01:00
|
|
|
import Form from '../ascribe_forms/form';
|
|
|
|
import Property from '../ascribe_forms/property';
|
2015-07-08 22:54:07 +02:00
|
|
|
|
2015-11-03 10:39:01 +01:00
|
|
|
import AclProxy from '../acl_proxy';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2015-08-07 15:08:02 +02:00
|
|
|
import ApiUrls from '../../constants/api_urls';
|
2015-10-12 15:25:21 +02:00
|
|
|
import AscribeSpinner from '../ascribe_spinner';
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2015-07-09 15:59:16 +02:00
|
|
|
import { getLangText } from '../../utils/lang_utils';
|
2015-06-29 16:46:12 +02:00
|
|
|
|
2015-09-30 18:30:50 +02:00
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
/**
|
|
|
|
* This is the component that implements display-specific functionality
|
|
|
|
*/
|
|
|
|
let Edition = React.createClass({
|
|
|
|
propTypes: {
|
2016-01-12 15:07:38 +01:00
|
|
|
currentUser: React.PropTypes.object.isRequired,
|
|
|
|
edition: React.PropTypes.object.isRequired,
|
|
|
|
whitelabel: React.PropTypes.object.isRequired,
|
|
|
|
|
2015-10-28 11:26:54 +01:00
|
|
|
actionPanelButtonListType: React.PropTypes.func,
|
2015-12-08 14:18:31 +01:00
|
|
|
coaError: React.PropTypes.object,
|
2016-01-11 19:34:39 +01:00
|
|
|
furtherDetailsType: React.PropTypes.func,
|
2015-11-23 10:46:20 +01:00
|
|
|
loadEdition: React.PropTypes.func
|
2015-06-26 00:38:40 +02:00
|
|
|
},
|
|
|
|
|
2015-10-28 11:26:54 +01:00
|
|
|
getDefaultProps() {
|
|
|
|
return {
|
|
|
|
furtherDetailsType: FurtherDetails
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
render() {
|
2016-01-18 14:46:39 +01:00
|
|
|
const { actionPanelButtonListType,
|
|
|
|
coaError,
|
|
|
|
currentUser,
|
|
|
|
edition,
|
|
|
|
furtherDetailsType: FurtherDetailsType,
|
2016-02-08 14:08:54 +01:00
|
|
|
loadEdition,
|
|
|
|
whitelabel } = this.props;
|
2015-10-28 11:26:54 +01:00
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
return (
|
|
|
|
<Row>
|
2015-12-23 09:37:03 +01:00
|
|
|
<Col md={6} className="ascribe-print-col-left">
|
2015-07-03 12:35:45 +02:00
|
|
|
<MediaContainer
|
2016-01-11 17:52:32 +01:00
|
|
|
content={edition}
|
2016-01-11 19:34:39 +01:00
|
|
|
currentUser={currentUser}
|
2016-01-12 15:07:38 +01:00
|
|
|
refreshObject={loadEdition} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</Col>
|
2015-12-23 09:37:03 +01:00
|
|
|
<Col md={6} className="ascribe-edition-details ascribe-print-col-right">
|
2015-07-13 17:09:44 +02:00
|
|
|
<div className="ascribe-detail-header">
|
2016-01-12 15:07:38 +01:00
|
|
|
<hr className="hidden-print" style={{marginTop: 0}} />
|
2016-01-11 17:52:32 +01:00
|
|
|
<h1 className="ascribe-detail-title">{edition.title}</h1>
|
2016-02-05 10:38:59 +01:00
|
|
|
<DetailProperty label="CREATED BY" value={edition.artist_name} />
|
2016-03-04 11:10:58 +01:00
|
|
|
<DetailProperty
|
|
|
|
label="YEAR OF CREATION"
|
|
|
|
value={Moment(edition.date_created, 'YYYY-MM-DD').year()} />
|
2016-01-12 15:07:38 +01:00
|
|
|
<hr />
|
2015-07-13 17:09:44 +02:00
|
|
|
</div>
|
2015-06-26 00:38:40 +02:00
|
|
|
<EditionSummary
|
2016-01-11 17:52:32 +01:00
|
|
|
actionPanelButtonListType={actionPanelButtonListType}
|
|
|
|
edition={edition}
|
|
|
|
currentUser={currentUser}
|
2016-01-12 15:07:38 +01:00
|
|
|
handleSuccess={loadEdition}
|
|
|
|
whitelabel={whitelabel} />
|
2015-06-26 00:38:40 +02:00
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Certificate of Authenticity')}
|
2016-01-11 17:52:32 +01:00
|
|
|
show={edition.acl.acl_coa === true}>
|
2015-06-26 00:38:40 +02:00
|
|
|
<CoaDetails
|
2016-01-11 17:52:32 +01:00
|
|
|
coa={edition.coa}
|
|
|
|
coaError={coaError}
|
2016-01-12 15:07:38 +01:00
|
|
|
editionId={edition.bitcoin_id} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Provenance/Ownership History')}
|
2016-02-09 12:07:23 +01:00
|
|
|
show={!!(edition.ownership_history && edition.ownership_history.length)}>
|
2016-01-12 15:07:38 +01:00
|
|
|
<HistoryIterator history={edition.ownership_history} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Consignment History')}
|
2016-02-09 12:07:23 +01:00
|
|
|
show={!!(edition.consign_history && edition.consign_history.length)}>
|
2016-01-12 15:07:38 +01:00
|
|
|
<HistoryIterator history={edition.consign_history} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Loan History')}
|
2016-02-09 12:07:23 +01:00
|
|
|
show={!!(edition.loan_history && edition.loan_history.length)}>
|
2016-01-12 15:07:38 +01:00
|
|
|
<HistoryIterator history={edition.loan_history} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
2015-07-13 14:30:24 +02:00
|
|
|
<CollapsibleParagraph
|
2016-02-08 14:50:24 +01:00
|
|
|
title={getLangText('Notes')}
|
2016-01-11 17:52:32 +01:00
|
|
|
show={!!(currentUser.username || edition.acl.acl_edit || edition.public_note)}>
|
2015-08-21 15:04:38 +02:00
|
|
|
<Note
|
2016-01-11 17:52:32 +01:00
|
|
|
id={() => {return {'bitcoin_id': edition.bitcoin_id}; }}
|
2015-08-21 15:04:38 +02:00
|
|
|
label={getLangText('Personal note (private)')}
|
2016-01-11 17:52:32 +01:00
|
|
|
defaultValue={edition.private_note ? edition.private_note : null}
|
2015-08-21 16:38:18 +02:00
|
|
|
placeholder={getLangText('Enter your comments ...')}
|
2015-08-21 15:04:38 +02:00
|
|
|
editable={true}
|
2015-08-21 16:38:18 +02:00
|
|
|
successMessage={getLangText('Private note saved')}
|
2015-08-21 15:04:38 +02:00
|
|
|
url={ApiUrls.note_private_edition}
|
2016-01-12 15:07:38 +01:00
|
|
|
currentUser={currentUser} />
|
2015-08-21 15:04:38 +02:00
|
|
|
<Note
|
2016-01-11 17:52:32 +01:00
|
|
|
id={() => {return {'bitcoin_id': edition.bitcoin_id}; }}
|
2015-12-02 16:51:56 +01:00
|
|
|
label={getLangText('Personal note (public)')}
|
2016-01-11 17:52:32 +01:00
|
|
|
defaultValue={edition.public_note ? edition.public_note : null}
|
2015-08-21 16:38:18 +02:00
|
|
|
placeholder={getLangText('Enter your comments ...')}
|
2016-01-11 17:52:32 +01:00
|
|
|
editable={!!edition.acl.acl_edit}
|
2016-05-02 11:13:24 +02:00
|
|
|
show={!!(edition.public_note || edition.acl.acl_edit)}
|
2015-08-21 16:38:18 +02:00
|
|
|
successMessage={getLangText('Public edition note saved')}
|
2015-08-21 15:04:38 +02:00
|
|
|
url={ApiUrls.note_public_edition}
|
2016-01-12 15:07:38 +01:00
|
|
|
currentUser={currentUser} />
|
2015-07-13 14:30:24 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
<CollapsibleParagraph
|
|
|
|
title={getLangText('Further Details')}
|
2016-02-09 12:07:23 +01:00
|
|
|
show={!!(edition.acl.acl_edit || Object.keys(edition.extra_data).length || edition.other_data.length)}>
|
2015-10-28 11:26:54 +01:00
|
|
|
<FurtherDetailsType
|
2016-01-11 17:52:32 +01:00
|
|
|
editable={edition.acl.acl_edit}
|
|
|
|
pieceId={edition.parent}
|
|
|
|
extraData={edition.extra_data}
|
|
|
|
otherData={edition.other_data}
|
|
|
|
handleSuccess={loadEdition} />
|
2015-07-13 14:30:24 +02:00
|
|
|
</CollapsibleParagraph>
|
2016-01-12 15:07:38 +01:00
|
|
|
<CollapsibleParagraph title={getLangText('SPOOL Details')}>
|
|
|
|
<SpoolDetails edition={edition} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let EditionSummary = React.createClass({
|
|
|
|
propTypes: {
|
2016-01-12 15:07:38 +01:00
|
|
|
currentUser: React.PropTypes.object.isRequired,
|
|
|
|
edition: React.PropTypes.object.isRequired,
|
|
|
|
whitelabel: React.PropTypes.object.isRequired,
|
|
|
|
|
2015-10-28 11:26:54 +01:00
|
|
|
actionPanelButtonListType: React.PropTypes.func,
|
2015-09-30 12:12:14 +02:00
|
|
|
handleSuccess: React.PropTypes.func
|
2015-06-26 00:38:40 +02:00
|
|
|
},
|
|
|
|
|
2016-01-12 15:07:38 +01:00
|
|
|
getStatus() {
|
|
|
|
const { status } = this.props.edition;
|
2015-09-28 16:32:59 +02:00
|
|
|
|
2016-01-12 15:07:38 +01:00
|
|
|
return status.length ? (
|
|
|
|
<DetailProperty
|
|
|
|
label="STATUS"
|
|
|
|
value={status.join(', ').replace(/_/g, ' ')} />
|
|
|
|
) : null;
|
2015-07-08 22:54:07 +02:00
|
|
|
},
|
2015-07-14 14:45:33 +02:00
|
|
|
|
2015-07-08 22:54:07 +02:00
|
|
|
render() {
|
2016-01-12 15:07:38 +01:00
|
|
|
const { actionPanelButtonListType, currentUser, edition, handleSuccess, whitelabel } = this.props;
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-12-23 09:39:02 +01:00
|
|
|
<DetailProperty
|
2015-07-30 11:48:47 +02:00
|
|
|
label={getLangText('EDITION')}
|
2016-01-12 15:07:38 +01:00
|
|
|
value={edition.edition_number + ' ' + getLangText('of') + ' ' + edition.num_editions} />
|
2015-12-23 09:39:02 +01:00
|
|
|
<DetailProperty
|
2015-07-30 11:48:47 +02:00
|
|
|
label={getLangText('ID')}
|
2016-01-12 15:07:38 +01:00
|
|
|
value={edition.bitcoin_id}
|
2015-07-30 11:48:47 +02:00
|
|
|
ellipsis={true} />
|
2016-03-04 11:10:58 +01:00
|
|
|
<DetailProperty
|
|
|
|
label="DATE OF TIMESTAMPING"
|
|
|
|
value={Moment(edition.datetime_registered).format('MMM. DD, YYYY, h:mm:ss')} />
|
2015-12-23 09:39:02 +01:00
|
|
|
<DetailProperty
|
2015-07-30 11:48:47 +02:00
|
|
|
label={getLangText('OWNER')}
|
2016-01-12 15:07:38 +01:00
|
|
|
value={edition.owner} />
|
|
|
|
<LicenseDetail license={edition.license_type} />
|
2015-07-08 22:54:07 +02:00
|
|
|
{this.getStatus()}
|
2015-12-07 10:48:46 +01:00
|
|
|
{/*
|
|
|
|
`acl_view` is always available in `edition.acl`, therefore if it has
|
|
|
|
no more than 1 key, we're hiding the `DetailProperty` actions as otherwise
|
|
|
|
`AclInformation` would show up
|
|
|
|
*/}
|
2016-01-12 15:07:38 +01:00
|
|
|
<AclProxy show={currentUser.email && Object.keys(edition.acl).length > 1}>
|
2015-12-23 09:39:02 +01:00
|
|
|
<DetailProperty
|
|
|
|
label={getLangText('ACTIONS')}
|
|
|
|
className="hidden-print">
|
2015-11-03 10:39:01 +01:00
|
|
|
<EditionActionPanel
|
|
|
|
actionPanelButtonListType={actionPanelButtonListType}
|
|
|
|
currentUser={currentUser}
|
2016-01-12 15:07:38 +01:00
|
|
|
edition={edition}
|
|
|
|
handleSuccess={handleSuccess}
|
|
|
|
whitelabel={whitelabel} />
|
2015-12-23 09:39:02 +01:00
|
|
|
</DetailProperty>
|
2015-11-03 10:39:01 +01:00
|
|
|
</AclProxy>
|
2015-06-26 00:38:40 +02:00
|
|
|
<hr/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let CoaDetails = React.createClass({
|
|
|
|
propTypes: {
|
2015-12-08 14:18:31 +01:00
|
|
|
editionId: React.PropTypes.string,
|
2016-01-11 17:52:32 +01:00
|
|
|
coa: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.number,
|
|
|
|
React.PropTypes.string,
|
|
|
|
React.PropTypes.object
|
|
|
|
]),
|
2015-12-08 14:18:31 +01:00
|
|
|
coaError: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
contactOnIntercom() {
|
2015-12-23 09:50:38 +01:00
|
|
|
const { coaError, editionId } = this.props;
|
|
|
|
|
|
|
|
window.Intercom('showNewMessage', getLangText("Hi, I'm having problems generating a Certificate of Authenticity for Edition: %s", editionId));
|
|
|
|
console.logGlobal(new Error(`Coa couldn't be created for edition: ${editionId}`), coaError);
|
2015-06-26 00:38:40 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
2016-01-04 18:06:26 +01:00
|
|
|
const { coa, coaError } = this.props;
|
2015-12-23 09:46:30 +01:00
|
|
|
let coaDetailElement;
|
2016-01-04 18:06:26 +01:00
|
|
|
|
2015-12-23 09:46:30 +01:00
|
|
|
if (coaError) {
|
|
|
|
coaDetailElement = [
|
|
|
|
<p>{getLangText('There was an error generating your Certificate of Authenticity.')}</p>,
|
|
|
|
<p>
|
|
|
|
{getLangText('Try to refresh the page. If this happens repeatedly, please ')}
|
|
|
|
<a style={{ cursor: 'pointer' }} onClick={this.contactOnIntercom}>{getLangText('contact us')}</a>.
|
|
|
|
</p>
|
|
|
|
];
|
2016-01-04 18:06:26 +01:00
|
|
|
} else if (coa && coa.url_safe) {
|
2015-12-23 09:46:30 +01:00
|
|
|
coaDetailElement = [
|
|
|
|
<div
|
|
|
|
className="notification-contract-pdf"
|
|
|
|
style={{paddingBottom: '1em'}}>
|
|
|
|
<embed
|
|
|
|
className="embed-form"
|
|
|
|
src={coa.url_safe}
|
|
|
|
alt="pdf"
|
|
|
|
pluginspage="http://www.adobe.com/products/acrobat/readstep2.html"/>
|
|
|
|
</div>,
|
|
|
|
<div className="text-center ascribe-button-list">
|
|
|
|
<a href={coa.url_safe} target="_blank">
|
|
|
|
<button className="btn btn-default btn-xs">
|
|
|
|
{getLangText('Download')} <Glyphicon glyph="cloud-download"/>
|
|
|
|
</button>
|
|
|
|
</a>
|
|
|
|
<Link to="/coa_verify">
|
|
|
|
<button className="btn btn-default btn-xs">
|
|
|
|
{getLangText('Verify')} <Glyphicon glyph="check"/>
|
|
|
|
</button>
|
|
|
|
</Link>
|
2015-12-08 14:18:31 +01:00
|
|
|
</div>
|
2015-12-23 09:46:30 +01:00
|
|
|
];
|
|
|
|
} else if (typeof coa === 'string') {
|
|
|
|
coaDetailElement = coa;
|
|
|
|
} else {
|
|
|
|
coaDetailElement = [
|
|
|
|
<AscribeSpinner color='dark-blue' size='md'/>,
|
|
|
|
<p>{getLangText("Just a sec, we're generating your COA")}</p>,
|
|
|
|
<p>{getLangText('(you may leave the page)')}</p>
|
|
|
|
];
|
2015-12-08 14:18:31 +01:00
|
|
|
}
|
2015-06-26 00:38:40 +02:00
|
|
|
|
2015-12-23 09:46:30 +01:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<div className="text-center hidden-print">
|
|
|
|
{coaDetailElement}
|
2015-06-26 00:38:40 +02:00
|
|
|
</div>
|
2015-12-23 09:46:30 +01:00
|
|
|
{/* Hide the COA and just show that it's a seperate document when printing */}
|
|
|
|
<div className="visible-print ascribe-coa-print-placeholder">
|
|
|
|
{getLangText('The COA is available as a seperate document')}
|
2015-07-22 00:30:51 +02:00
|
|
|
</div>
|
2015-07-17 23:28:59 +02:00
|
|
|
</div>
|
|
|
|
);
|
2015-06-26 00:38:40 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-07-03 12:35:45 +02:00
|
|
|
let SpoolDetails = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
2015-12-23 09:48:45 +01:00
|
|
|
const { edition: {
|
|
|
|
bitcoin_id: bitcoinId,
|
|
|
|
hash_as_address: hashAsAddress,
|
|
|
|
btc_owner_address_noprefix: bitcoinOwnerAddress
|
|
|
|
} } = this.props;
|
|
|
|
|
|
|
|
const bitcoinIdValue = (
|
|
|
|
<a className="anchor-no-expand-print"
|
|
|
|
target="_blank"
|
|
|
|
href={'https://www.blocktrail.com/BTC/address/' + bitcoinId}>
|
|
|
|
{bitcoinId}
|
|
|
|
</a>
|
2015-07-03 12:35:45 +02:00
|
|
|
);
|
|
|
|
|
2015-12-23 09:48:45 +01:00
|
|
|
const hashOfArtwork = (
|
|
|
|
<a className="anchor-no-expand-print"
|
|
|
|
target="_blank"
|
|
|
|
href={'https://www.blocktrail.com/BTC/address/' + hashAsAddress}>
|
|
|
|
{hashAsAddress}
|
|
|
|
</a>
|
2015-07-03 12:35:45 +02:00
|
|
|
);
|
|
|
|
|
2015-12-23 09:48:45 +01:00
|
|
|
const ownerAddress = (
|
|
|
|
<a className="anchor-no-expand-print"
|
|
|
|
target="_blank"
|
|
|
|
href={'https://www.blocktrail.com/BTC/address/' + bitcoinOwnerAddress}>
|
|
|
|
{bitcoinOwnerAddress}
|
|
|
|
</a>
|
2015-07-03 12:35:45 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Form >
|
|
|
|
<Property
|
|
|
|
name='artwork_id'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Artwork ID')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{bitcoinIdValue}</pre>
|
|
|
|
</Property>
|
|
|
|
<Property
|
|
|
|
name='hash_of_artwork'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Hash of Artwork, title, etc')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{hashOfArtwork}</pre>
|
|
|
|
</Property>
|
|
|
|
<Property
|
|
|
|
name='owner_address'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Owned by SPOOL address')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{ownerAddress}</pre>
|
|
|
|
</Property>
|
|
|
|
<hr />
|
|
|
|
</Form>);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
2016-01-12 15:07:38 +01:00
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
export default Edition;
|