2 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13 * or implied. See the License for the specific language governing
14 * permissions and limitations under the License.
16 import React from 'react';
17 import classnames from 'classnames';
18 import Dropzone from 'react-dropzone';
21 import i18n from 'nfvo-utils/i18n/i18n.js';
22 import ListEditorView from 'nfvo-components/listEditor/ListEditorView.jsx';
23 import ListEditorItemView from 'nfvo-components/listEditor/ListEditorItemView.jsx';
24 import ListEditorItemViewField from 'nfvo-components/listEditor/ListEditorItemViewField.jsx';
26 import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx';
28 const SoftwareProductPropType = React.PropTypes.shape({
29 name: React.PropTypes.string,
30 description: React.PropTypes.string,
31 version: React.PropTypes.object,
32 id: React.PropTypes.string,
33 categoryId: React.PropTypes.string,
34 vendorId: React.PropTypes.string,
35 status: React.PropTypes.string,
36 licensingData: React.PropTypes.object,
37 validationData: React.PropTypes.object
40 const ComponentPropType = React.PropTypes.shape({
41 id: React.PropTypes.string,
42 name: React.PropTypes.string,
43 displayName: React.PropTypes.string,
44 description: React.PropTypes.string
47 class SoftwareProductLandingPageView extends React.Component {
57 currentSoftwareProduct: SoftwareProductPropType,
58 isReadOnlyMode: React.PropTypes.bool,
59 componentsList: React.PropTypes.arrayOf(ComponentPropType),
60 onDetailsSelect: React.PropTypes.func,
61 onAttachmentsSelect: React.PropTypes.func,
62 onUpload: React.PropTypes.func,
63 onUploadConfirmation: React.PropTypes.func,
64 onInvalidFileSizeUpload: React.PropTypes.func,
65 onComponentSelect: React.PropTypes.func,
66 onAddComponent: React.PropTypes.func
70 let {currentSoftwareProduct, isReadOnlyMode, componentsList = []} = this.props;
72 <div className='software-product-landing-wrapper'>
74 className={classnames('software-product-landing-view', {'active-dragging': this.state.dragging})}
75 onDrop={files => this.handleImportSubmit(files, isReadOnlyMode)}
76 onDragEnter={() => this.handleOnDragEnter(isReadOnlyMode)}
77 onDragLeave={() => this.setState({dragging:false})}
84 <div className='draggable-wrapper'>
85 <div className='software-product-landing-view-top'>
87 {this.renderProductSummary(currentSoftwareProduct)}
88 {this.renderProductDetails(currentSoftwareProduct, isReadOnlyMode)}
94 componentsList.length > 0 && this.renderComponents()
100 handleOnDragEnter(isReadOnlyMode) {
101 if (!isReadOnlyMode) {
102 this.setState({dragging: true});
106 renderProductSummary(currentSoftwareProduct) {
107 let {name = '', description = '', vendorName = '', fullCategoryDisplayName = '', licenseAgreementName = ''} = currentSoftwareProduct;
108 let {onDetailsSelect} = this.props;
110 <div className='details-panel'>
111 <div className='software-product-landing-view-heading-title'>{i18n('Software Product Details')}</div>
113 className='software-product-landing-view-top-block clickable'
114 onClick={() => onDetailsSelect(currentSoftwareProduct)}>
115 <div className='details-container'>
116 <div className='single-detail-section title-section'>
117 <div className='single-detail-section title-text'>
121 <div className='details-section'>
122 <div className='multiple-details-section'>
123 <div className='detail-col' >
124 <div className='title'>{i18n('Vendor')}</div>
125 <div className='description'>{vendorName}</div>
127 <div className='detail-col'>
128 <div className='title'>{i18n('Category')}</div>
129 <div className='description'>{fullCategoryDisplayName}</div>
131 <div className='detail-col'>
132 <div className='title extra-large'>{i18n('License Agreement')}</div>
133 <div className='description'>
134 {this.renderLicenseAgreement(licenseAgreementName)}
138 <div className='single-detail-section'>
139 <div className='title'>{i18n('Description')}</div>
140 <div className='description'>{description}</div>
149 renderProductDetails(currentSoftwareProduct, isReadOnlyMode) {
150 let {validationData} = currentSoftwareProduct;
151 let {onAttachmentsSelect} = this.props;
153 heatTemplates: validationData ? '1' : '0',
159 <div className='details-panel'>
160 <div className='software-product-landing-view-heading-title'>{i18n('Software Product Attachments')}</div>
161 <div className='software-product-landing-view-top-block'>
163 className='software-product-landing-view-top-block-col'
164 onClick={() => onAttachmentsSelect(currentSoftwareProduct)}>
166 <div className='attachment-details'>{i18n('HEAT Templates')} (<span
167 className='attachment-details-count'>{details.heatTemplates}</span>)
172 className={classnames('software-product-landing-view-top-block-col-upl', {'disabled': isReadOnlyMode})}>
173 <div className='drag-text'>{i18n('Drag & drop for upload')}</div>
174 <div className='or-text'>{i18n('or')}</div>
175 <div data-test-id='upload-btn' className='upload-btn primary-btn' onClick={() => this.refs.fileInput.open()}>
176 <span className='primary-btn-text'>{i18n('Select file')}</span>
185 const {localFilter} = this.state;
189 title={i18n('Virtual Function Components')}
190 filterValue={localFilter}
191 placeholder={i18n('Filter Components')}
192 onFilter={value => this.setState({localFilter: value})}
194 {this.filterList().map(component => this.renderComponentsListItem(component))}
199 renderComponentsListItem(component) {
200 let {id: componentId, name, displayName, description = ''} = component;
201 let {currentSoftwareProduct: {id}, onComponentSelect} = this.props;
204 key={name + Math.floor(Math.random() * (100 - 1) + 1).toString()}
205 className='list-editor-item-view'
206 onSelect={() => onComponentSelect({id, componentId})}>
207 <ListEditorItemViewField>
208 <div className='name'>{displayName}</div>
209 </ListEditorItemViewField>
210 <ListEditorItemViewField>
211 <div className='description'>{description}</div>
212 </ListEditorItemViewField>
213 </ListEditorItemView>
217 renderLicenseAgreement(licenseAgreementName) {
218 if (licenseAgreementName !== null && !licenseAgreementName) {
219 return (<div className='missing-license'><SVGIcon name='exclamation-triangle-full'/><div className='warning-text'>{i18n('Missing')}</div></div>);
221 return (licenseAgreementName);
226 let {componentsList = []} = this.props;
228 let {localFilter} = this.state;
229 if (localFilter.trim()) {
230 const filter = new RegExp(escape(localFilter), 'i');
231 return componentsList.filter(({displayName = '', description = ''}) => {
232 return escape(displayName).match(filter) || escape(description).match(filter);
236 return componentsList;
240 handleImportSubmit(files, isReadOnlyMode) {
241 if (isReadOnlyMode) {
244 if (files[0] && files[0].size) {
246 fileName: files[0].name,
250 this.startUploading(files);
256 this.props.onInvalidFileSizeUpload();
261 startUploading(files) {
262 let {onUpload, currentSoftwareProduct, onUploadConfirmation} = this.props;
264 let {validationData} = currentSoftwareProduct;
266 if (!(files && files.length)) {
270 let formData = new FormData();
271 formData.append('upload', file);
272 this.refs.fileInput.value = '';
274 if (validationData) {
275 onUploadConfirmation(currentSoftwareProduct.id, formData);
277 onUpload(currentSoftwareProduct.id, formData);
283 export default SoftwareProductLandingPageView;