Specify a model while creating a VSP
[sdc.git] / openecomp-ui / src / sdc-app / onboarding / softwareProduct / details / SoftwareProductDetailsView.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
14  * or implied. See the License for the specific language governing
15  * permissions and limitations under the License.
16  */
17 import React, { Component } from 'react';
18 import PropTypes from 'prop-types';
19
20 import i18n from 'nfvo-utils/i18n/i18n.js';
21 import sortByStringProperty from 'nfvo-utils/sortByStringProperty.js';
22 import Form from 'nfvo-components/input/validation/Form.jsx';
23 import Input from 'nfvo-components/input/validation/Input.jsx';
24 import InputOptions from 'nfvo-components/input/validation/InputOptions.jsx';
25 import GridSection from 'nfvo-components/grid/GridSection.jsx';
26 import GridItem from 'nfvo-components/grid/GridItem.jsx';
27 import SoftwareProductCategoriesHelper from 'sdc-app/onboarding/softwareProduct/SoftwareProductCategoriesHelper.js';
28 import { forms } from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
29 import { versionStatus } from 'sdc-app/common/helpers/ItemsHelperConstants.js';
30
31 const DeprecatedVlmInfo = ({ vendorName, onVendorRemove }) => {
32     return (
33         <div className="depricated-vlm-info">
34             <Input
35                 data-test-id="vsp-vendor-name"
36                 isRequired={true}
37                 onClick={() => onVendorRemove()}
38                 label={i18n('Vendor')}
39                 type="select"
40                 value={`${vendorName} (Archived)`}>
41                 <option
42                     key={vendorName}
43                     value={`${vendorName} (Archived)`}>{`${vendorName} (Archived)`}</option>
44             </Input>
45         </div>
46     );
47 };
48
49 class GeneralSection extends React.Component {
50     static propTypes = {
51         vendorId: PropTypes.string,
52         name: PropTypes.string,
53         description: PropTypes.string,
54         subCategory: PropTypes.string,
55         selectedModelList: PropTypes.arrayOf(PropTypes.string),
56         softwareProductCategories: PropTypes.array,
57         finalizedLicenseModelList: PropTypes.array,
58         onDataChanged: PropTypes.func.isRequired,
59         onVendorParamChanged: PropTypes.func.isRequired,
60         onSelectSubCategory: PropTypes.func.isRequired,
61         isVendorArchived: PropTypes.bool,
62         onArchivedVendorRemove: PropTypes.func
63     };
64
65     onVendorParamChanged(e) {
66         const selectedIndex = e.target.selectedIndex;
67         const vendorId = e.target.options[selectedIndex].value;
68         this.props.onVendorParamChanged(
69             { vendorId },
70             forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
71         );
72     }
73
74     onSelectSubCategory(e) {
75         const selectedIndex = e.target.selectedIndex;
76         const subCategory = e.target.options[selectedIndex].value;
77         this.props.onSelectSubCategory(subCategory);
78     }
79     onVendorRemove() {
80         const {
81             finalizedLicenseModelList,
82             vendorName,
83             onVendorParamChanged
84         } = this.props;
85         this.props.onArchivedVendorRemove({
86             finalizedLicenseModelList,
87             onVendorParamChanged,
88             vendorName
89         });
90     }
91
92     render() {
93         let { genericFieldInfo } = this.props;
94         return (
95             <div>
96                 {genericFieldInfo && (
97                     <GridSection
98                         title={i18n('General')}
99                         className="grid-section-general">
100                         <GridItem>
101                             <Input
102                                 data-test-id="vsp-name"
103                                 label={i18n('Name')}
104                                 disabled
105                                 type="text"
106                                 value={this.props.name}
107                                 isRequired={true}
108                                 errorText={genericFieldInfo.name.errorText}
109                                 isValid={genericFieldInfo.name.isValid}
110                                 onChange={name =>
111                                     name.length <= 25 &&
112                                     this.props.onDataChanged(
113                                         { name },
114                                         forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
115                                     )
116                                 }
117                             />
118                             {this.props.isVendorArchived ? (
119                                 <DeprecatedVlmInfo
120                                     onVendorRemove={() => this.onVendorRemove()}
121                                     vendorName={this.props.vendorName}
122                                 />
123                             ) : (
124                                 <Input
125                                     data-test-id="vsp-vendor-name"
126                                     label={i18n('Vendor')}
127                                     type="select"
128                                     value={this.props.vendorId}
129                                     onChange={e =>
130                                         this.onVendorParamChanged(e)
131                                     }>
132                                     {sortByStringProperty(
133                                         this.props.finalizedLicenseModelList,
134                                         'name'
135                                     ).map(lm => (
136                                         <option key={lm.id} value={lm.id}>
137                                             {lm.name}
138                                         </option>
139                                     ))}
140                                 </Input>
141                             )}
142                             <Input
143                                 data-test-id="vsp-category-name"
144                                 label={i18n('Category')}
145                                 type="select"
146                                 value={this.props.subCategory}
147                                 onChange={e => this.onSelectSubCategory(e)}>
148                                 {this.props.softwareProductCategories.map(
149                                     category =>
150                                         category.subcategories && (
151                                             <optgroup
152                                                 key={category.name}
153                                                 label={category.name}>
154                                                 {category.subcategories.map(
155                                                     sub => (
156                                                         <option
157                                                             key={sub.uniqueId}
158                                                             value={
159                                                                 sub.uniqueId
160                                                             }>{`${sub.name} (${
161                                                             category.name
162                                                         })`}</option>
163                                                     )
164                                                 )}
165                                             </optgroup>
166                                         )
167                                 )}
168                             </Input>
169                             <div className="form-group">
170                                 <label className="control-label">
171                                     {i18n('Model')}
172                                 </label>
173                                 <div>
174                                     {this.props.selectedModelList.length > 0 ? (
175                                         <ul>
176                                             {this.props.selectedModelList.map(
177                                                 value => <li>{value}</li>
178                                             )}
179                                         </ul>
180                                     ) : (
181                                         i18n('model.sdc.label')
182                                     )}
183                                 </div>
184                             </div>
185                         </GridItem>
186                         <GridItem colSpan={2} stretch>
187                             <Input
188                                 data-test-id="vsp-description"
189                                 label={i18n('Description')}
190                                 type="textarea"
191                                 isRequired={true}
192                                 isValid={genericFieldInfo.description.isValid}
193                                 errorText={
194                                     genericFieldInfo.description.errorText
195                                 }
196                                 value={this.props.description}
197                                 onChange={description =>
198                                     this.props.onDataChanged(
199                                         { description },
200                                         forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
201                                     )
202                                 }
203                             />
204                         </GridItem>
205                     </GridSection>
206                 )}
207             </div>
208         );
209     }
210 }
211 class LicensesSection extends React.Component {
212     static propTypes = {
213         onVendorParamChanged: PropTypes.func.isRequired,
214         vendorId: PropTypes.string,
215         licensingVersion: PropTypes.string,
216         licensingVersionsList: PropTypes.array,
217         licensingData: PropTypes.shape({
218             licenceAgreement: PropTypes.string,
219             featureGroups: PropTypes.array
220         }),
221         onFeatureGroupsChanged: PropTypes.func.isRequired,
222         onLicensingDataChanged: PropTypes.func.isRequired,
223         featureGroupsList: PropTypes.array,
224         licenseAgreementList: PropTypes.array,
225         isVendorArchived: PropTypes.bool,
226         licenseType: PropTypes.string
227     };
228
229     onVendorParamChanged(e) {
230         const selectedIndex = e.target.selectedIndex;
231         const licensingVersion = e.target.options[selectedIndex].value;
232         this.props.onVendorParamChanged(
233             { vendorId: this.props.vendorId, licensingVersion },
234             forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
235         );
236     }
237
238     onLicensingDataChanged(e) {
239         const selectedIndex = e.target.selectedIndex;
240         const licenseAgreement = e.target.options[selectedIndex].value;
241         this.props.onLicensingDataChanged({
242             licenseAgreement,
243             featureGroups: []
244         });
245     }
246
247     render() {
248         return (
249             <GridSection title={i18n('Licenses')}>
250                 <GridItem>
251                     <Input
252                         data-test-id="vsp-licensing-version"
253                         onChange={e => this.onVendorParamChanged(e)}
254                         value={this.props.licensingVersion || ''}
255                         label={i18n('Licensing Version')}
256                         disabled={
257                             this.props.isVendorArchived ||
258                             this.props.licenseType !== 'INTERNAL'
259                         }
260                         type="select">
261                         {this.props.licensingVersionsList.map(version => (
262                             <option key={version.enum} value={version.enum}>
263                                 {version.title}
264                             </option>
265                         ))}
266                     </Input>
267                 </GridItem>
268                 <GridItem>
269                     <Input
270                         data-test-id="vsp-license-agreement"
271                         label={i18n('License Agreement')}
272                         type="select"
273                         disabled={
274                             this.props.isVendorArchived ||
275                             this.props.licenseType !== 'INTERNAL'
276                         }
277                         value={
278                             this.props.licensingData.licenseAgreement
279                                 ? this.props.licensingData.licenseAgreement
280                                 : ''
281                         }
282                         onChange={e => this.onLicensingDataChanged(e)}>
283                         <option key="placeholder" value="">
284                             {i18n('Select...')}
285                         </option>
286                         {this.props.licenseAgreementList.map(la => (
287                             <option value={la.id} key={la.id}>
288                                 {la.name}
289                             </option>
290                         ))}
291                     </Input>
292                 </GridItem>
293                 <GridItem>
294                     {this.props.licensingData.licenseAgreement && (
295                         <InputOptions
296                             data-test-id="vsp-feature-group"
297                             type="select"
298                             isMultiSelect={true}
299                             onInputChange={() => {}}
300                             disabled={
301                                 this.props.isVendorArchived ||
302                                 this.props.licenseType !== 'INTERNAL'
303                             }
304                             onEnumChange={featureGroups =>
305                                 this.props.onFeatureGroupsChanged({
306                                     featureGroups
307                                 })
308                             }
309                             multiSelectedEnum={
310                                 this.props.licensingData.featureGroups
311                             }
312                             name="feature-groups"
313                             label={i18n('Feature Groups')}
314                             clearable={false}
315                             values={this.props.featureGroupsList}
316                         />
317                     )}
318                 </GridItem>
319             </GridSection>
320         );
321     }
322 }
323 const AvailabilitySection = props => (
324     <GridSection title={i18n('Availability')}>
325         <GridItem colSpan={2}>
326             <Input
327                 data-test-id="vsp-use-availability-zone"
328                 label={i18n('Use Availability Zones for High Availability')}
329                 type="checkbox"
330                 checked={
331                     props.dataMap[
332                         'general/availability/useAvailabilityZonesForHighAvailability'
333                     ]
334                 }
335                 value={
336                     props.dataMap[
337                         'general/availability/useAvailabilityZonesForHighAvailability'
338                     ]
339                 }
340                 onChange={aZone =>
341                     props.onQDataChanged({
342                         'general/availability/useAvailabilityZonesForHighAvailability': aZone
343                     })
344                 }
345             />
346         </GridItem>
347     </GridSection>
348 );
349 const RegionsSection = props => (
350     <GridSection title={i18n('Regions')}>
351         <GridItem>
352             <InputOptions
353                 data-test-id="vsp-regions"
354                 type="select"
355                 isMultiSelect={true}
356                 onInputChange={() => {}}
357                 onEnumChange={regions =>
358                     props.onQDataChanged({
359                         'general/regionsData/regions': regions
360                     })
361                 }
362                 multiSelectedEnum={props.dataMap['general/regionsData/regions']}
363                 name="vsp-regions"
364                 clearable={false}
365                 values={
366                     props.genericFieldInfo['general/regionsData/regions'].enum
367                 }
368             />
369         </GridItem>
370     </GridSection>
371 );
372 const StorageDataReplicationSection = props => (
373     <GridSection title={i18n('Storage Data Replication')}>
374         <GridItem>
375             <Input
376                 data-test-id="vsp-storage-rep-size"
377                 label={i18n('Storage Replication Size (GB)')}
378                 type="number"
379                 isValid={
380                     props.genericFieldInfo[
381                         'general/storageDataReplication/storageReplicationSize'
382                     ].isValid
383                 }
384                 errorText={
385                     props.genericFieldInfo[
386                         'general/storageDataReplication/storageReplicationSize'
387                     ].errorText
388                 }
389                 value={
390                     props.dataMap[
391                         'general/storageDataReplication/storageReplicationSize'
392                     ]
393                 }
394                 onChange={sRep =>
395                     props.onQDataChanged({
396                         'general/storageDataReplication/storageReplicationSize': sRep
397                     })
398                 }
399             />
400         </GridItem>
401         <GridItem>
402             <Input
403                 data-test-id="vsp-storage-rep-source"
404                 label={i18n('Storage Replication Source')}
405                 type="text"
406                 isValid={
407                     props.genericFieldInfo[
408                         'general/storageDataReplication/storageReplicationSource'
409                     ].isValid
410                 }
411                 errorText={
412                     props.genericFieldInfo[
413                         'general/storageDataReplication/storageReplicationSource'
414                     ].errorText
415                 }
416                 value={
417                     props.dataMap[
418                         'general/storageDataReplication/storageReplicationSource'
419                     ]
420                 }
421                 onChange={sRepSource =>
422                     props.onQDataChanged({
423                         'general/storageDataReplication/storageReplicationSource': sRepSource
424                     })
425                 }
426             />
427         </GridItem>
428         <GridItem>
429             <Input
430                 data-test-id="vsp-storage-rep-freq"
431                 label={i18n('Storage Replication Freq. (min)')}
432                 type="number"
433                 isValid={
434                     props.genericFieldInfo[
435                         'general/storageDataReplication/storageReplicationFrequency'
436                     ].isValid
437                 }
438                 errorText={
439                     props.genericFieldInfo[
440                         'general/storageDataReplication/storageReplicationFrequency'
441                     ].errorText
442                 }
443                 value={
444                     props.dataMap[
445                         'general/storageDataReplication/storageReplicationFrequency'
446                     ]
447                 }
448                 onChange={sRepFreq =>
449                     props.onQDataChanged({
450                         'general/storageDataReplication/storageReplicationFrequency': sRepFreq
451                     })
452                 }
453             />
454         </GridItem>
455         <GridItem>
456             <Input
457                 data-test-id="vsp-storage-rep-dest"
458                 label={i18n('Storage Replication Destination')}
459                 type="text"
460                 isValid={
461                     props.genericFieldInfo[
462                         'general/storageDataReplication/storageReplicationDestination'
463                     ].isValid
464                 }
465                 errorText={
466                     props.genericFieldInfo[
467                         'general/storageDataReplication/storageReplicationDestination'
468                     ].errorText
469                 }
470                 value={
471                     props.dataMap[
472                         'general/storageDataReplication/storageReplicationDestination'
473                     ]
474                 }
475                 onChange={sRepDest =>
476                     props.onQDataChanged({
477                         'general/storageDataReplication/storageReplicationDestination': sRepDest
478                     })
479                 }
480             />
481         </GridItem>
482     </GridSection>
483 );
484
485 class SoftwareProductDetails extends Component {
486     static propTypes = {
487         vendorName: PropTypes.string,
488         currentSoftwareProduct: PropTypes.shape({
489             id: PropTypes.string,
490             name: PropTypes.string,
491             description: PropTypes.string,
492             category: PropTypes.string,
493             subCategory: PropTypes.string,
494             vendorId: PropTypes.string,
495             vendorName: PropTypes.string,
496             licensingVersion: PropTypes.string,
497             licenseType: PropTypes.string,
498             licensingData: PropTypes.shape({
499                 licenceAgreement: PropTypes.string,
500                 featureGroups: PropTypes.array
501             })
502         }),
503         softwareProductCategories: PropTypes.array,
504         finalizedLicenseModelList: PropTypes.array,
505         licenseAgreementList: PropTypes.array,
506         featureGroupsList: PropTypes.array,
507         onSubmit: PropTypes.func.isRequired,
508         onDataChanged: PropTypes.func.isRequired,
509         onValidityChanged: PropTypes.func.isRequired,
510         qdata: PropTypes.object.isRequired,
511         onQDataChanged: PropTypes.func.isRequired,
512         onVendorParamChanged: PropTypes.func.isRequired
513     };
514
515     prepareDataForGeneralSection() {
516         let {
517             softwareProductCategories,
518             finalizedLicenseModelList,
519             onDataChanged,
520             currentSoftwareProduct,
521             genericFieldInfo,
522             isVendorArchived,
523             onArchivedVendorRemove
524         } = this.props;
525         let {
526             name,
527             description,
528             vendorId,
529             subCategory,
530             vendorName,
531             selectedModelList = []
532         } = currentSoftwareProduct;
533         return {
534             name,
535             description,
536             vendorId,
537             subCategory,
538             softwareProductCategories,
539             finalizedLicenseModelList,
540             onDataChanged,
541             onVendorParamChanged: args => this.onVendorParamChanged(args),
542             onSelectSubCategory: args => this.onSelectSubCategory(args),
543             genericFieldInfo,
544             vendorName,
545             selectedModelList,
546             isVendorArchived,
547             onArchivedVendorRemove
548         };
549     }
550
551     prepareDataForLicensesSection() {
552         let {
553             featureGroupsList,
554             licenseAgreementList,
555             currentSoftwareProduct,
556             isVendorArchived
557         } = this.props;
558         let {
559             vendorId,
560             licensingVersion,
561             licensingData = {},
562             licenseType
563         } = currentSoftwareProduct;
564         return {
565             onVendorParamChanged: args => this.onVendorParamChanged(args),
566             vendorId,
567             licensingVersion,
568             licensingVersionsList: this.buildLicensingVersionsListItems(),
569             licensingData,
570             onFeatureGroupsChanged: args => this.onFeatureGroupsChanged(args),
571             onLicensingDataChanged: args => this.onLicensingDataChanged(args),
572             featureGroupsList,
573             licenseAgreementList,
574             isVendorArchived,
575             licenseType
576         };
577     }
578
579     render() {
580         let { currentSoftwareProduct } = this.props;
581         let { qdata, onQDataChanged, dataMap, qGenericFieldInfo } = this.props;
582         let { isReadOnlyMode } = this.props;
583
584         return (
585             <div className="vsp-details-page">
586                 <Form
587                     ref={validationForm =>
588                         (this.validationForm = validationForm)
589                     }
590                     className="vsp-general-tab"
591                     hasButtons={false}
592                     formReady={null}
593                     isValid={this.props.isFormValid}
594                     onSubmit={() =>
595                         this.props.onSubmit(currentSoftwareProduct, qdata)
596                     }
597                     onValidityChanged={isValidityData =>
598                         this.props.onValidityChanged(isValidityData)
599                     }
600                     isReadOnlyMode={isReadOnlyMode}>
601                     <GeneralSection {...this.prepareDataForGeneralSection()} />
602                     <LicensesSection
603                         {...this.prepareDataForLicensesSection()}
604                     />
605                     <AvailabilitySection
606                         onQDataChanged={onQDataChanged}
607                         dataMap={dataMap}
608                     />
609                     <RegionsSection
610                         onQDataChanged={onQDataChanged}
611                         dataMap={dataMap}
612                         genericFieldInfo={qGenericFieldInfo}
613                     />
614                     <StorageDataReplicationSection
615                         onQDataChanged={onQDataChanged}
616                         dataMap={dataMap}
617                         genericFieldInfo={qGenericFieldInfo}
618                     />
619                 </Form>
620             </div>
621         );
622     }
623
624     onVendorParamChanged({ vendorId, licensingVersion }) {
625         let { finalizedLicenseModelList, onVendorParamChanged } = this.props;
626         if (!licensingVersion) {
627             const licensingVersionsList = this.buildLicensingVersionsListItems();
628             licensingVersion = licensingVersionsList[0].enum;
629         }
630
631         if (!vendorId) {
632             vendorId = finalizedLicenseModelList[0].id;
633         }
634
635         let vendorName =
636             finalizedLicenseModelList.find(
637                 licenseModelItem => licenseModelItem.id === vendorId
638             ).name || '';
639         let deltaData = {
640             vendorId,
641             vendorName,
642             licensingVersion,
643             licensingData: {}
644         };
645
646         onVendorParamChanged(deltaData, forms.VENDOR_SOFTWARE_PRODUCT_DETAILS);
647     }
648
649     buildLicensingVersionsListItems() {
650         let { licensingVersionsList } = this.props;
651
652         let licensingVersionsListItems = [
653             {
654                 enum: '',
655                 title: i18n('Select...')
656             }
657         ];
658
659         return licensingVersionsListItems.concat(
660             licensingVersionsList
661                 .filter(item => item.status === versionStatus.CERTIFIED)
662                 .map(version => ({
663                     enum: version.id,
664                     title: version.name
665                 }))
666         );
667     }
668
669     onFeatureGroupsChanged({ featureGroups }) {
670         this.onLicensingDataChanged({ featureGroups });
671     }
672
673     onLicensingDataChanged(deltaData) {
674         this.props.onDataChanged(
675             {
676                 licensingData: {
677                     ...this.props.currentSoftwareProduct.licensingData,
678                     ...deltaData
679                 }
680             },
681             forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
682         );
683     }
684
685     onSelectSubCategory(subCategory) {
686         let { softwareProductCategories, onDataChanged } = this.props;
687         let category = SoftwareProductCategoriesHelper.getCurrentCategoryOfSubCategory(
688             subCategory,
689             softwareProductCategories
690         );
691         onDataChanged(
692             { category, subCategory },
693             forms.VENDOR_SOFTWARE_PRODUCT_DETAILS
694         );
695     }
696
697     save() {
698         return this.validationForm.handleFormSubmit(new Event('dummy'));
699     }
700 }
701
702 export default SoftwareProductDetails;