2 * Copyright © 2016-2018 European Support Limited
3 * Modifications Copyright (C) 2021 Nordix Foundation.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 import React from 'react';
18 import PropTypes from 'prop-types';
19 import classnames from 'classnames';
20 import Dropzone from 'react-dropzone';
22 import i18n from 'nfvo-utils/i18n/i18n.js';
23 import VnfRepositorySearchBox from 'nfvo-components/vnfMarketPlace/VnfRepositorySearchBox.jsx';
25 import { SVGIcon } from 'onap-ui-react';
26 import SoftwareProductComponentsList from 'sdc-app/onboarding/softwareProduct/components/SoftwareProductComponents.js';
27 import VspUploadStatus from 'sdc-app/onboarding/softwareProduct/landingPage/VspUploadStatus';
28 import ProgressBar from 'react-bootstrap/lib/ProgressBar';
30 const SoftwareProductPropType = PropTypes.shape({
31 name: PropTypes.string,
32 description: PropTypes.string,
33 version: 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)
44 const ComponentPropType = PropTypes.shape({
46 name: PropTypes.string,
47 displayName: PropTypes.string,
48 description: PropTypes.string
51 class SoftwareProductLandingPageView extends React.Component {
58 showProgressBar: false
63 this.getExternalLicenceFeatureState = this.getExternalLicenceFeatureState.bind(
69 currentSoftwareProduct: SoftwareProductPropType,
70 isReadOnlyMode: PropTypes.bool,
71 componentsList: PropTypes.arrayOf(ComponentPropType),
72 version: PropTypes.object,
73 onLicenseChange: PropTypes.func,
74 onUpload: PropTypes.func,
75 fetchUploadStatus: PropTypes.func,
76 onUploadConfirmation: PropTypes.func,
77 onInvalidFileSizeUpload: PropTypes.func,
78 onComponentSelect: PropTypes.func,
79 onAddComponent: PropTypes.func
85 currentSoftwareProduct,
88 if (currentSoftwareProduct.candidateOnboardingOrigin && !isCertified) {
89 onCandidateInProcess(currentSoftwareProduct.id);
91 this.keepCheckingUploadStatus();
94 componentWillUnmount() {
95 this.stopUploadStatusChecking();
98 keepCheckingUploadStatus(initialDelayInMs = 0, updatePeriodInMs = 10000) {
99 this.stopUploadStatusChecking();
100 setTimeout(() => this.updateUploadStatus(), initialDelayInMs);
101 this.uploadStatusInterval = setInterval(
102 () => this.updateUploadStatus(),
107 stopUploadStatusChecking() {
108 clearInterval(this.uploadStatusInterval);
111 updateUploadStatus() {
112 const currentVspId = this.props.currentSoftwareProduct.id;
114 .fetchUploadStatus(currentVspId)
115 .then(uploadStatusResponse => {
116 const vspUploadStatus = new VspUploadStatus(
120 uploadStatus: vspUploadStatus
124 console.error('Could not retrieve upload status', error)
128 licenceChange = (e, currentSoftwareProduct, onLicenseChange) => {
129 currentSoftwareProduct.licenseType = e.target.value
132 onLicenseChange(currentSoftwareProduct);
135 getExternalLicenceFeatureState() {
136 const licenseFeature = this.props.features.find(
137 feature => feature.name === 'EXTERNAL_LICENSE'
139 return licenseFeature ? licenseFeature.active : true;
144 currentSoftwareProduct,
149 let licenceChange = this.licenceChange;
151 <div className="software-product-landing-wrapper">
153 className={classnames('software-product-landing-view', {
154 'active-dragging': this.state.dragging
157 this.handleImportSubmit(files, isReadOnlyMode, isManual)
160 this.handleOnDragEnter(isReadOnlyMode, isManual)
162 onDragLeave={() => this.setState({ dragging: false })}
167 accept=".zip, .csar">
168 <div className="draggable-wrapper">
169 <div className="software-product-landing-view-top">
170 <div className="row">
172 currentSoftwareProduct={
173 currentSoftwareProduct
175 licenceChange={licenceChange}
176 onLicenseChange={onLicenseChange}
177 externalLicenceEnabled={this.getExternalLicenceFeatureState()}
179 <div className="details-panel">
180 {this.renderProductAttachments(
188 <SoftwareProductComponentsList />
193 handleOnDragEnter(isReadOnlyMode, isManual) {
194 if (!isReadOnlyMode && !isManual) {
195 this.setState({ dragging: true });
199 isUploadInProgress() {
201 this.state.uploadStatus.complete !== undefined &&
202 !this.state.uploadStatus.complete
206 renderProductAttachments(isReadOnlyMode) {
207 let { onBrowseVNF, currentSoftwareProduct } = this.props;
209 if (this.isUploadInProgress()) {
212 <div className="software-product-landing-view-heading-title">
213 {i18n('Software Product Attachments')}
215 <div className="software-product-landing-view-top-block-col-upl ">
216 <div className="upload-status-text">
217 {this.state.uploadStatus.statusToString()}
218 {this.state.showProgressBar && (
219 <ProgressBar now={this.state.uploadProgress} />
228 <div className="software-product-landing-view-heading-title">
229 {i18n('Software Product Attachments')}
231 <VnfRepositorySearchBox
232 dataTestId="upload-btn"
233 isReadOnlyMode={isReadOnlyMode}
234 className={classnames(
235 'software-product-landing-view-top-block-col-upl',
236 { disabled: isReadOnlyMode }
238 onClick={() => this.refs.fileInput.open()}
239 onBrowseVNF={() => onBrowseVNF(currentSoftwareProduct)}
245 handleImportSubmit(files, isReadOnlyMode, isManual) {
246 if (isReadOnlyMode || isManual) {
249 if (files[0] && files[0].size) {
251 fileName: files[0].name,
255 this.startUploading(files);
260 this.props.onInvalidFileSizeUpload();
264 onUploadStart = vspUploadStatus => {
266 uploadStatus: vspUploadStatus
268 this.stopUploadStatusChecking();
269 this.showProgressBar();
272 onUploadProgress = progressEvent => {
273 const vspUploadStatus = new VspUploadStatus({
274 status: VspUploadStatus.UPLOADING,
278 uploadStatus: vspUploadStatus
280 const percentCompleted = Math.round(
281 progressEvent.loaded * 100 / progressEvent.total
283 if (percentCompleted === 100) {
284 this.keepCheckingUploadStatus(5000);
285 this.resetUploadProgress(2000);
287 this.setState({ uploadProgress: percentCompleted });
290 onUploadFinished = () => {
291 this.updateUploadStatus();
295 this.setState({ showProgressBar: true });
299 this.setState({ showProgressBar: false });
302 resetUploadProgress(milliseconds) {
304 this.setState({ uploadProgress: 0 });
305 this.hideProgressBar();
309 startUploading(files) {
312 currentSoftwareProduct,
316 let { validationData } = currentSoftwareProduct;
318 if (!(files && files.length)) {
322 let formData = new FormData();
323 formData.append('upload', file);
324 this.refs.fileInput.value = '';
326 if (validationData) {
327 onUploadConfirmation(
328 currentSoftwareProduct.id,
330 vspUploadStatus => this.onUploadStart(vspUploadStatus),
331 this.onUploadProgress,
332 this.onUploadFinished
336 currentSoftwareProduct.id,
338 vspUploadStatus => this.onUploadStart(vspUploadStatus),
339 this.onUploadProgress,
340 this.onUploadFinished
346 const ProductSummary = ({
347 currentSoftwareProduct,
350 externalLicenceEnabled
356 fullCategoryDisplayName = '',
357 selectedModelList = []
358 } = currentSoftwareProduct;
360 <div className="details-panel">
361 <div className="software-product-landing-view-heading-title">
362 {i18n('Software Product Details')}
364 <div className="software-product-landing-view-top-block">
365 <div className="details-container">
366 <div className="single-detail-section title-section">
367 <div className="single-detail-section title-text">
371 <div className="details-section">
372 <div className="multiple-details-section">
373 <div className="detail-col">
374 <div className="title">{i18n('Vendor')}</div>
375 <div className="description">{vendorName}</div>
377 <div className="detail-col">
378 <div className="title">{i18n('Category')}</div>
379 <div className="description">
380 {fullCategoryDisplayName}
383 <div className="detail-col">
384 <div className="title">{i18n('Model')}</div>
385 <div className="description">
386 {selectedModelList.length > 0 ? (
388 {selectedModelList.map(value => (
393 i18n('model.sdc.label')
397 <div className="detail-col">
398 <div className="title extra-large">
399 {i18n('License Agreement')}
401 <div className="description">
403 licenceChange={licenceChange}
404 currentSoftwareProduct={
405 currentSoftwareProduct
407 onLicenseChange={onLicenseChange}
408 externalLicenceEnabled={
409 externalLicenceEnabled
415 <div className="single-detail-section">
416 <div className="title">{i18n('Description')}</div>
417 <div className="description">{description}</div>
426 const LicenseAgreementWithExternal = ({
428 currentSoftwareProduct,
432 <div className="missing-license">
441 currentSoftwareProduct,
445 checked={currentSoftwareProduct.licenseType === 'INTERNAL'}
448 <div className="description licenceLabel">
449 {i18n('Internal license')}
459 currentSoftwareProduct,
463 checked={currentSoftwareProduct.licenseType === 'EXTERNAL'}
466 <div className="description licenceLabel">
467 {i18n('External license')}
474 const LicenseAgreementWithoutExternal = ({
476 currentSoftwareProduct,
479 if (!currentSoftwareProduct.licenseAgreementName) {
482 className="missing-license clickable"
486 currentSoftwareProduct,
490 <SVGIcon color="warning" name="exclamationTriangleFull" />
491 <div className="warning-text">{i18n('Missing')}</div>
495 return <div>{currentSoftwareProduct.licenseAgreementName}</div>;
498 const LicenseAgreement = ({
500 currentSoftwareProduct,
502 externalLicenceEnabled
504 if (externalLicenceEnabled) {
506 <LicenseAgreementWithExternal
507 licenceChange={licenceChange}
508 currentSoftwareProduct={currentSoftwareProduct}
509 onLicenseChange={onLicenseChange}
514 <LicenseAgreementWithoutExternal
515 licenceChange={licenceChange}
516 currentSoftwareProduct={currentSoftwareProduct}
517 onLicenseChange={onLicenseChange}
523 export default SoftwareProductLandingPageView;