1 import React from 'react';
2 import classnames from 'classnames';
3 import Dropzone from 'react-dropzone';
6 import i18n from 'nfvo-utils/i18n/i18n.js';
7 import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
8 import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
10 import FontAwesome from 'react-fontawesome';
11 import SoftwareProductLandingPageUploadConfirmationModal from './SoftwareProductLandingPageUploadConfirmationModal.jsx';
14 const SoftwareProductPropType = React.PropTypes.shape({
15 name: React.PropTypes.string,
16 description: React.PropTypes.string,
17 version: React.PropTypes.string,
18 id: React.PropTypes.string,
19 categoryId: React.PropTypes.string,
20 vendorId: React.PropTypes.string,
21 status: React.PropTypes.string,
22 licensingData: React.PropTypes.object,
23 validationData: React.PropTypes.object
26 const ComponentPropType = React.PropTypes.shape({
27 id: React.PropTypes.string,
28 name: React.PropTypes.string,
29 displayName: React.PropTypes.string,
30 description: React.PropTypes.string
33 class SoftwareProductLandingPageView extends React.Component {
43 currentSoftwareProduct: SoftwareProductPropType,
44 isReadOnlyMode: React.PropTypes.bool,
45 componentsList: React.PropTypes.arrayOf(ComponentPropType),
46 onDetailsSelect: React.PropTypes.func,
47 onAttachmentsSelect: React.PropTypes.func,
48 onUpload: React.PropTypes.func,
49 onUploadConfirmation: React.PropTypes.func,
50 onInvalidFileSizeUpload: React.PropTypes.func,
51 onComponentSelect: React.PropTypes.func,
52 onAddComponent: React.PropTypes.func
56 let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props;
58 <div className='software-product-landing-wrapper'>
60 className={classnames('software-product-landing-view', {'active-dragging': this.state.dragging})}
61 onDrop={files => this.handleImportSubmit(files, isReadOnlyMode)}
62 onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)}
63 onDragLeave={() => this.setState({dragging:false})}
70 <div className='draggable-wrapper'>
71 <div className='software-product-landing-view-top'>
73 {this.renderProductSummary(currentSoftwareProduct)}
74 {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)}
80 componentsList.length > 0 && this.renderComponents()
82 <SoftwareProductLandingPageUploadConfirmationModal confirmationButtonText={i18n('Continue')}/>
87 handleOnDragEnter(isReadOnlyMode) {
88 if (!isReadOnlyMode) {
89 this.setState({dragging: true});
93 renderProductSummary(currentSoftwareProduct) {
94 let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct;
95 let {onDetailsSelect} = this.props;
97 <div className='details-panel'>
98 <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div>
100 className='software-product-landing-view-top-block clickable'
101 onClick={() => onDetailsSelect(currentSoftwareProduct)}>
102 <div className='details-container'>
103 <div className='single-detail-section title-section'>
108 <div className='multiple-details-section'>
109 <div className='detail-col' >
110 <div className='title'>{i18n('Vendor')}</div>
111 <div className='description'>{vendorName}</div>
113 <div className='detail-col'>
114 <div className='title'>{i18n('Category')}</div>
115 <div className='description'>{fullCategoryDisplayName}</div>
117 <div className='detail-col'>
118 <div className='title extra-large'>{i18n('License Agreement')}</div>
119 <div className='description'>
120 {this.renderLicenseAgreement(licenseAgreementName)}
124 <div className='single-detail-section'>
125 <div className='title'>{i18n('Description')}</div>
126 <div className='description'>{description}</div>
134 renderProductDetails(currentSoftwareProduct, isReadOnlyMode) {
135 let {validationData} = currentSoftwareProduct;
136 let {onAttachmentsSelect} = this.props;
138 heatTemplates: validationData ? '1' : '0',
144 <div className='details-panel'>
145 <div className='software-product-landing-view-heading-title'>{i18n('Software Product Attachments')}</div>
146 <div className='software-product-landing-view-top-block'>
148 className='software-product-landing-view-top-block-col'
149 onClick={() => onAttachmentsSelect(currentSoftwareProduct)}>
151 <div className='attachment-details'>{i18n('HEAT Templates')} (<span
152 className='attachment-details-count'>{details.heatTemplates}</span>)
154 <div className='attachment-details'>{i18n('Images')} (<span
155 className='attachment-details-count'>{details.images}</span>)
157 <div className='attachment-details'>{i18n('Other Artifacts')} (<span
158 className='attachment-details-count'>{details.otherArtifacts}</span>)
163 className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}>
164 <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
165 <div className='or-text'>{i18n('or')}</div>
166 <div className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}>
167 <span className='primary-btn-text'>{i18n('Select file')}</span>
176 const {localFilter} = this.state;
180 title={i18n('Virtual Function Components')}
181 filterValue={localFilter}
182 placeholder={i18n('Filter Components')}
183 onFilter={filter => this.setState({localFilter: filter})}>
184 {this.filterList().map(component => this.renderComponentsListItem(component))}
189 renderComponentsListItem(component) {
190 let {id: componentId, name, displayName, description = ''} = component;
191 let {currentSoftwareProduct: {id}, onComponentSelect} = this.props;
194 key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()}
195 className='list-editor-item-view'
196 onSelect={() => onComponentSelect({id, componentId})}>
197 <div className='list-editor-item-view-field'>
198 <div className='title'>{i18n('Component')}</div>
199 <div className='name'>{displayName}</div>
201 <div className='list-editor-item-view-field'>
202 <div className='title'>{i18n('Description')}</div>
203 <div className='description'>{description}</div>
205 </ListEditorItemView>
209 renderLicenseAgreement(licenseAgreementName) {
210 if (!licenseAgreementName) {
211 return (<FontAwesome name='exclamation-triangle' className='warning-icon'/>);
213 return (licenseAgreementName);
218 let {componentsList = []} = this.props;
220 let {localFilter} = this.state;
221 if (localFilter.trim()) {
222 const filter = new RegExp(escape(localFilter), 'i');
223 return componentsList.filter(({displayName = '', description = ''}) => {
224 return escape(displayName).match(filter) || escape(description).match(filter);
228 return componentsList;
232 handleImportSubmit(files, isReadOnlyMode) {
233 if (isReadOnlyMode) {
236 if (files[0] && files[0].size) {
238 fileName: files[0].name,
242 this.startUploading(files);
245 this.props.onInvalidFileSizeUpload();
250 startUploading(files) {
251 let {onUpload, currentSoftwareProduct, onUploadConfirmation} = this.props;
253 let {validationData} = currentSoftwareProduct;
255 if (!(files && files.length)) {
259 let formData = new FormData();
260 formData.append('upload', file);
261 this.refs.fileInput.value = '';
263 if (validationData) {
264 onUploadConfirmation(currentSoftwareProduct.id, formData);
266 onUpload(currentSoftwareProduct.id, formData);
272 export default SoftwareProductLandingPageView;