Specify a model while creating a VSP
[sdc.git] / openecomp-ui / src / sdc-app / onboarding / softwareProduct / landingPage / SoftwareProductLandingPageView.jsx
1 /*
2  * Copyright © 2016-2018 European Support Limited
3  * Modifications Copyright (C) 2021 Nordix Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 import React from 'react';
18 import PropTypes from 'prop-types';
19 import classnames from 'classnames';
20 import Dropzone from 'react-dropzone';
21
22 import i18n from 'nfvo-utils/i18n/i18n.js';
23 import Configuration from 'sdc-app/config/Configuration.js';
24 import DraggableUploadFileBox from 'nfvo-components/fileupload/DraggableUploadFileBox.jsx';
25 import VnfRepositorySearchBox from 'nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx';
26
27 import { SVGIcon } from 'onap-ui-react';
28 import SoftwareProductComponentsList from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js';
29
30 const SoftwareProductPropType = PropTypes.shape({
31     name: PropTypes.string,
32     description: PropTypes.string,
33     version: PropTypes.string,
34     id: PropTypes.string,
35     categoryId: PropTypes.string,
36     vendorId: PropTypes.string,
37     licenseType: PropTypes.string,
38     status: PropTypes.string,
39     licensingData: PropTypes.object,
40     validationData: PropTypes.object,
41     selectedModelList: PropTypes.arrayOf(PropTypes.string)
42 });
43
44 const ComponentPropType = PropTypes.shape({
45     id: PropTypes.string,
46     name: PropTypes.string,
47     displayName: PropTypes.string,
48     description: PropTypes.string
49 });
50
51 class SoftwareProductLandingPageView extends React.Component {
52     state = {
53         fileName: '',
54         dragging: false,
55         files: []
56     };
57
58     constructor(props) {
59         super(props);
60         this.getExternalLicenceFeatureState = this.getExternalLicenceFeatureState.bind(
61             this
62         );
63     }
64
65     static propTypes = {
66         currentSoftwareProduct: SoftwareProductPropType,
67         isReadOnlyMode: PropTypes.bool,
68         componentsList: PropTypes.arrayOf(ComponentPropType),
69         version: PropTypes.object,
70         onLicenseChange: PropTypes.func,
71         onUpload: PropTypes.func,
72         onUploadConfirmation: PropTypes.func,
73         onInvalidFileSizeUpload: PropTypes.func,
74         onComponentSelect: PropTypes.func,
75         onAddComponent: PropTypes.func
76     };
77     componentDidMount() {
78         const {
79             onCandidateInProcess,
80             currentSoftwareProduct,
81             isCertified
82         } = this.props;
83         if (currentSoftwareProduct.candidateOnboardingOrigin && !isCertified) {
84             onCandidateInProcess(currentSoftwareProduct.id);
85         }
86     }
87
88     licenceChange = (e, currentSoftwareProduct, onLicenseChange) => {
89         currentSoftwareProduct.licenseType = e.target.value
90             ? e.target.value
91             : 'INTERNAL';
92         onLicenseChange(currentSoftwareProduct);
93     };
94
95     getExternalLicenceFeatureState() {
96         var licenseFeature = this.props.features.find(
97             feature => feature.name === 'EXTERNAL_LICENSE'
98         );
99         return licenseFeature ? licenseFeature.active : true;
100     }
101
102     render() {
103         let {
104             currentSoftwareProduct,
105             isReadOnlyMode,
106             isManual,
107             onLicenseChange
108         } = this.props;
109         let licenceChange = this.licenceChange;
110         return (
111             <div className="software-product-landing-wrapper">
112                 <Dropzone
113                     className={classnames('software-product-landing-view', {
114                         'active-dragging': this.state.dragging
115                     })}
116                     onDrop={files =>
117                         this.handleImportSubmit(files, isReadOnlyMode, isManual)
118                     }
119                     onDragEnter={() =>
120                         this.handleOnDragEnter(isReadOnlyMode, isManual)
121                     }
122                     onDragLeave={() => this.setState({ dragging: false })}
123                     multiple={false}
124                     disableClick={true}
125                     ref="fileInput"
126                     name="fileInput"
127                     accept=".zip, .csar">
128                     <div className="draggable-wrapper">
129                         <div className="software-product-landing-view-top">
130                             <div className="row">
131                                 <ProductSummary
132                                     currentSoftwareProduct={
133                                         currentSoftwareProduct
134                                     }
135                                     licenceChange={licenceChange}
136                                     onLicenseChange={onLicenseChange}
137                                     externalLicenceEnabled={this.getExternalLicenceFeatureState()}
138                                 />
139                                 {this.renderProductDetails(
140                                     isManual,
141                                     isReadOnlyMode
142                                 )}
143                             </div>
144                         </div>
145                     </div>
146                 </Dropzone>
147                 <SoftwareProductComponentsList />
148             </div>
149         );
150     }
151
152     handleOnDragEnter(isReadOnlyMode, isManual) {
153         if (!isReadOnlyMode && !isManual) {
154             this.setState({ dragging: true });
155         }
156     }
157
158     renderProductDetails(isManual, isReadOnlyMode) {
159         let { onBrowseVNF, currentSoftwareProduct } = this.props;
160
161         if (Configuration.get('showBrowseVNF')) {
162             return (
163                 <div className="details-panel">
164                     {!isManual && (
165                         <div>
166                             <div className="software-product-landing-view-heading-title">
167                                 {i18n('Software Product Attachments')}
168                             </div>
169                             <VnfRepositorySearchBox
170                                 dataTestId="upload-btn"
171                                 isReadOnlyMode={isReadOnlyMode}
172                                 className={classnames(
173                                     'software-product-landing-view-top-block-col-upl showVnf',
174                                     { disabled: isReadOnlyMode }
175                                 )}
176                                 onClick={() => this.refs.fileInput.open()}
177                                 onBrowseVNF={() =>
178                                     onBrowseVNF(currentSoftwareProduct)
179                                 }
180                             />
181                         </div>
182                     )}
183                 </div>
184             );
185         } else {
186             return (
187                 <div className="details-panel">
188                     {!isManual && (
189                         <div>
190                             <div className="software-product-landing-view-heading-title">
191                                 {i18n('Software Product Attachments')}
192                             </div>
193                             <DraggableUploadFileBox
194                                 dataTestId="upload-btn"
195                                 isReadOnlyMode={isReadOnlyMode}
196                                 className={classnames(
197                                     'software-product-landing-view-top-block-col-upl',
198                                     { disabled: isReadOnlyMode }
199                                 )}
200                                 onClick={() => this.refs.fileInput.open()}
201                                 onBrowseVNF={() => onBrowseVNF()}
202                             />
203                         </div>
204                     )}
205                 </div>
206             );
207         }
208     }
209
210     handleImportSubmit(files, isReadOnlyMode, isManual) {
211         if (isReadOnlyMode || isManual) {
212             return;
213         }
214         if (files[0] && files[0].size) {
215             this.setState({
216                 fileName: files[0].name,
217                 dragging: false,
218                 complete: '0'
219             });
220             this.startUploading(files);
221         } else {
222             this.setState({
223                 dragging: false
224             });
225             this.props.onInvalidFileSizeUpload();
226         }
227     }
228
229     startUploading(files) {
230         let {
231             onUpload,
232             currentSoftwareProduct,
233             onUploadConfirmation
234         } = this.props;
235
236         let { validationData } = currentSoftwareProduct;
237
238         if (!(files && files.length)) {
239             return;
240         }
241         let file = files[0];
242         let formData = new FormData();
243         formData.append('upload', file);
244         this.refs.fileInput.value = '';
245
246         if (validationData) {
247             onUploadConfirmation(currentSoftwareProduct.id, formData);
248         } else {
249             onUpload(currentSoftwareProduct.id, formData);
250         }
251     }
252 }
253
254 const ProductSummary = ({
255     currentSoftwareProduct,
256     licenceChange,
257     onLicenseChange,
258     externalLicenceEnabled
259 }) => {
260     let {
261         name = '',
262         description = '',
263         vendorName = '',
264         fullCategoryDisplayName = '',
265         selectedModelList = []
266     } = currentSoftwareProduct;
267     return (
268         <div className="details-panel">
269             <div className="software-product-landing-view-heading-title">
270                 {i18n('Software Product Details')}
271             </div>
272             <div className="software-product-landing-view-top-block">
273                 <div className="details-container">
274                     <div className="single-detail-section title-section">
275                         <div className="single-detail-section title-text">
276                             {name}
277                         </div>
278                     </div>
279                     <div className="details-section">
280                         <div className="multiple-details-section">
281                             <div className="detail-col">
282                                 <div className="title">{i18n('Vendor')}</div>
283                                 <div className="description">{vendorName}</div>
284                             </div>
285                             <div className="detail-col">
286                                 <div className="title">{i18n('Category')}</div>
287                                 <div className="description">
288                                     {fullCategoryDisplayName}
289                                 </div>
290                             </div>
291                             <div className="detail-col">
292                                 <div className="title">{i18n('Model')}</div>
293                                 <div className="description">
294                                     {selectedModelList.length > 0 ? (
295                                         <ul>
296                                             {selectedModelList.map(value => (
297                                                 <li>{value}</li>
298                                             ))}
299                                         </ul>
300                                     ) : (
301                                         i18n('model.sdc.label')
302                                     )}
303                                 </div>
304                             </div>
305                             <div className="detail-col">
306                                 <div className="title extra-large">
307                                     {i18n('License Agreement')}
308                                 </div>
309                                 <div className="description">
310                                     <LicenseAgreement
311                                         licenceChange={licenceChange}
312                                         currentSoftwareProduct={
313                                             currentSoftwareProduct
314                                         }
315                                         onLicenseChange={onLicenseChange}
316                                         externalLicenceEnabled={
317                                             externalLicenceEnabled
318                                         }
319                                     />
320                                 </div>
321                             </div>
322                         </div>
323                         <div className="single-detail-section">
324                             <div className="title">{i18n('Description')}</div>
325                             <div className="description">{description}</div>
326                         </div>
327                     </div>
328                 </div>
329             </div>
330         </div>
331     );
332 };
333
334 const LicenseAgreementWithExternal = ({
335     licenceChange,
336     currentSoftwareProduct,
337     onLicenseChange
338 }) => {
339     return (
340         <div className="missing-license">
341             <form>
342                 <input
343                     type="radio"
344                     value="INTERNAL"
345                     id="INTERNAL"
346                     onChange={event =>
347                         licenceChange(
348                             event,
349                             currentSoftwareProduct,
350                             onLicenseChange
351                         )
352                     }
353                     checked={currentSoftwareProduct.licenseType === 'INTERNAL'}
354                     name="license"
355                 />
356                 <div className="description licenceLabel">
357                     {i18n('Internal license')}
358                 </div>
359                 <br />
360                 <input
361                     type="radio"
362                     value="EXTERNAL"
363                     id="EXTERNAL"
364                     onChange={event =>
365                         licenceChange(
366                             event,
367                             currentSoftwareProduct,
368                             onLicenseChange
369                         )
370                     }
371                     checked={currentSoftwareProduct.licenseType === 'EXTERNAL'}
372                     name="license"
373                 />
374                 <div className="description licenceLabel">
375                     {i18n('External license')}
376                 </div>
377             </form>
378         </div>
379     );
380 };
381
382 const LicenseAgreementWithoutExternal = ({
383     licenceChange,
384     currentSoftwareProduct,
385     onLicenseChange
386 }) => {
387     if (!currentSoftwareProduct.licenseAgreementName) {
388         return (
389             <div
390                 className="missing-license clickable"
391                 onClick={event =>
392                     licenceChange(
393                         event,
394                         currentSoftwareProduct,
395                         onLicenseChange
396                     )
397                 }>
398                 <SVGIcon color="warning" name="exclamationTriangleFull" />
399                 <div className="warning-text">{i18n('Missing')}</div>
400             </div>
401         );
402     }
403     return <div>{currentSoftwareProduct.licenseAgreementName}</div>;
404 };
405
406 const LicenseAgreement = ({
407     licenceChange,
408     currentSoftwareProduct,
409     onLicenseChange,
410     externalLicenceEnabled
411 }) => {
412     if (externalLicenceEnabled) {
413         return (
414             <LicenseAgreementWithExternal
415                 licenceChange={licenceChange}
416                 currentSoftwareProduct={currentSoftwareProduct}
417                 onLicenseChange={onLicenseChange}
418             />
419         );
420     } else {
421         return (
422             <LicenseAgreementWithoutExternal
423                 licenceChange={licenceChange}
424                 currentSoftwareProduct={currentSoftwareProduct}
425                 onLicenseChange={onLicenseChange}
426             />
427         );
428     }
429 };
430
431 export default SoftwareProductLandingPageView;