diff --git a/angular.json b/angular.json index 9f6e5d8a63302629366d9e305c41df452961c4c4..203b5717c86123104f115a9c6502559cc58a9040 100644 --- a/angular.json +++ b/angular.json @@ -24,7 +24,11 @@ "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "styles": ["src/styles.scss", + "./node_modules/quill/dist/quill.core.css", + "./node_modules/quill/dist/quill.bubble.css", + "./node_modules/quill/dist/quill.snow.css" + ], "scripts": [] }, "configurations": { @@ -107,7 +111,11 @@ "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": ["src/favicon.ico", "src/assets"], - "styles": ["src/styles.scss"], + "styles": ["src/styles.scss", + "./node_modules/quill/dist/quill.core.css", + "./node_modules/quill/dist/quill.bubble.css", + "./node_modules/quill/dist/quill.snow.css" + ], "scripts": [], "karmaConfig": "karma.conf.js" } diff --git a/package-lock.json b/package-lock.json index 7803d551e32ab96283d21b8c27b72aff3425a6c0..7990a0cbb337e38e284e011d09e2643127e14844 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "@angular/platform-browser-dynamic": "^18.0.2", "@angular/router": "^18.0.2", "jwt-decode": "^4.0.0", + "ngx-quill": "^26.0.1", + "quill": "^2.0.2", "rxjs": "~7.8.1", "tslib": "^2.6.2", "zone.js": "~0.14.2" @@ -8985,8 +8987,7 @@ "node_modules/fast-diff": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", - "dev": true + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" }, "node_modules/fast-fifo": { "version": "1.3.2", @@ -11455,12 +11456,27 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -12175,6 +12191,22 @@ "node": ">= 0.4.0" } }, + "node_modules/ngx-quill": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/ngx-quill/-/ngx-quill-26.0.1.tgz", + "integrity": "sha512-jiG4YKrGRdtv3g3jno36N5nHNuyCnOmGcHWnvadNy/w/T+MTFEkjyIN4if9KVPWqXmaXkC3/07njNuWpCJRtSQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.0", + "quill": "^2.0.0", + "rxjs": "^7.0.0" + } + }, "node_modules/nice-napi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", @@ -12984,6 +13016,11 @@ "node": "^16.14.0 || >=18.0.0" } }, + "node_modules/parchment": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", + "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -13701,6 +13738,38 @@ "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "dev": true }, + "node_modules/quill": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz", + "integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash-es": "^4.17.21", + "parchment": "^3.0.0", + "quill-delta": "^5.1.0" + }, + "engines": { + "npm": ">=8.2.3" + } + }, + "node_modules/quill-delta": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", + "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", + "dependencies": { + "fast-diff": "^1.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/quill/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index bdc3c89287515092199ff73e34f638a480ddb15c..2a4846e515c9bb3659ab32903c49260696d37433 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "@angular/platform-browser-dynamic": "^18.0.2", "@angular/router": "^18.0.2", "jwt-decode": "^4.0.0", + "ngx-quill": "^26.0.1", + "quill": "^2.0.2", "rxjs": "~7.8.1", "tslib": "^2.6.2", "zone.js": "~0.14.2" @@ -52,4 +54,4 @@ "puppeteer": "^21.5.1", "typescript": "~5.4.5" } -} \ No newline at end of file +} diff --git a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.html b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.html index 39a34fa92022e9f68df2714f5cd92ed04e47969f..ee246ac72eda3df19310425cf803c52f87358461 100644 --- a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.html +++ b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.html @@ -20,56 +20,156 @@ <div style="display: flex; flex-direction: column"> <mat-form-field> <mat-label>Select a catalog</mat-label> - <mat-select formControlName="catalog"> + <mat-select + formControlName="catalog" + (selectionChange)="onChangeCatalog($event)" + > @for (catalog of catalogs; track catalog) { <mat-option [value]="catalog">{{ catalog.name }}</mat-option> } </mat-select> </mat-form-field> - <mat-form-field> - <mat-label>Model name</mat-label> - <input matInput formControlName="name" /> - </mat-form-field> - <mat-form-field> - <mat-label>Model description</mat-label> - <input matInput formControlName="description" /> - </mat-form-field> - <div style="display: flex; gap: 20px; width: 100%"> - <mat-form-field style="width: 100%"> - <mat-label>Select category</mat-label> - <mat-select formControlName="category"> - @for (catalog of catalogs; track catalog) { - <mat-option [value]="catalog">{{ catalog.name }}</mat-option> - } - </mat-select> - </mat-form-field> - <mat-form-field style="width: 100%"> - <mat-label>Select toolkitType</mat-label> - <mat-select formControlName="toolkitType"> - @for (catalog of catalogs; track catalog) { - <mat-option [value]="catalog">{{ catalog.name }}</mat-option> + @if (publishToMarketPlaceForm.controls["catalog"].value) { + <div style="display: flex; flex-direction: column"> + <mat-form-field> + <mat-label>Model name</mat-label> + <input matInput formControlName="name" /> + </mat-form-field> + <div> + <span>Model description</span> + @if (showDescriptionEditor) { + <quill-editor + [styles]="{ height: '200px' }" + (onFocus)="focus($event)" + (onBlur)="blur($event)" + (onEditorCreated)="created($event)" + formControlName="description" + ></quill-editor> } - </mat-select> - </mat-form-field> - </div> - <mat-form-field> - <mat-label>Model license profile</mat-label> - <input matInput formControlName="licenseProfile" /> - </mat-form-field> + </div> + <div style="display: flex; gap: 20px; width: 100%"> + <mat-form-field style="width: 100%"> + <mat-label>Select category</mat-label> + <mat-select formControlName="category"> + @for (category of categories; track category) { + <mat-option [value]="category">{{ + category.name + }}</mat-option> + } + </mat-select> + </mat-form-field> + <mat-form-field style="width: 100%"> + <mat-label>Select toolkitType</mat-label> + <mat-select formControlName="toolkitType"> + @for (toolkitType of toolkitTypes; track toolkitType) { + <mat-option [value]="toolkitType">{{ + toolkitType.name + }}</mat-option> + } + </mat-select> + </mat-form-field> + </div> + <!--license profile start--> + <div style="display: flex; flex-direction: column"> + <div style="display: flex; align-items: center; gap: 40px"> + <span>Model license profile</span> + <button + (click)="onLicenseProfileClick()" + mat-raised-button + color="primary" + > + Add/Edit license profile + </button> + </div> + </div> + @if (addEditLicenseProfile) { + <div style="display: flex; width: 100%"> + <gp-model-details-license-profile + [isExistingLicenseProfile]="true" + ></gp-model-details-license-profile> + </div> + } + <!--license profile end--> + <!--upload documents start--> + <div + style=" + display: flex; + flex-direction: row; + width: 100%; + gap: 20px; + align-items: center; + " + > + <span style="width: 20%">Upload image model </span> + + <mat-form-field style="height: 70px; flex-grow: 1"> + <input matInput [value]="imageFile?.name" /> + + <input + #fileDropRef + type="file" + id="fileInput" + name="fileInput" + (change)="selectImageFile($event)" + hidden + formControlName="image" + /> + </mat-form-field> + + <button + style="height: 48px" + color="primary" + mat-raised-button + (click)="onClickUploadImageFile()" + > + Upload file + </button> + </div> + <!--upload documents end--> + <mat-form-field> + <mat-label>Model tags</mat-label> + <input matInput formControlName="tags" /> + </mat-form-field> + <!--upload image start--> + <div + style=" + display: flex; + flex-direction: row; + width: 100%; + gap: 20px; + align-items: center; + " + > + <span style="width: 20%">Upload image model </span> + + <mat-form-field style="height: 70px; flex-grow: 1"> + <input matInput [value]="imageFile?.name" /> + + <input + #fileDropRef + type="file" + id="fileInput" + name="fileInput" + (change)="selectImageFile($event)" + hidden + formControlName="image" + /> + </mat-form-field> + + <button + style="height: 48px" + color="primary" + mat-raised-button + (click)="onClickUploadImageFile()" + > + Upload file + </button> + </div> + <!--upload image end--> + </div> + } - <mat-form-field> - <mat-label>Model documents</mat-label> - <input matInput formControlName="documents" /> - </mat-form-field> - <mat-form-field> - <mat-label>Model tags</mat-label> - <input matInput formControlName="tags" /> - </mat-form-field> - <mat-form-field> - <mat-label>Model image</mat-label> - <input matInput formControlName="image" /> - </mat-form-field> <div style=" display: flex; diff --git a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.ts b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.ts index 79d28973887c53d0c3674cf6f8fd655c655d5e50..6f86a1f286685612a3a438374271d2f212d5cee6 100644 --- a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.ts +++ b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.ts @@ -6,6 +6,7 @@ import { Catalog, CatalogFilter, Filter, + LicenseProfileModel, PublicSolutionDetailsModel, ToolkitTypeCode, toolkitTypeCodeMap, @@ -25,6 +26,12 @@ import { MatFormFieldModule } from '@angular/material/form-field'; import { MatSelectModule } from '@angular/material/select'; import { MatInputModule } from '@angular/material/input'; import { FiltersService } from 'src/app/core/services/filters.service'; +import { QuillModule } from 'ngx-quill'; +import { UploadLicenseProfileComponent } from '../upload-license-profile/upload-license-profile.component'; +import { SharedDataService } from 'src/app/core/services/shared-data/shared-data.service'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { AlertService } from 'src/app/core/services/alert.service'; +import { ModelDetailsLicenseProfileComponent } from '../model-details-license-profile/model-details-license-profile.component'; @Component({ selector: 'gp-publish-to-marketplace-page', @@ -39,6 +46,8 @@ import { FiltersService } from 'src/app/core/services/filters.service'; MatFormFieldModule, MatSelectModule, MatInputModule, + QuillModule, + ModelDetailsLicenseProfileComponent, ], templateUrl: './publish-to-marketplace-page.component.html', styleUrl: './publish-to-marketplace-page.component.scss', @@ -53,14 +62,31 @@ export class PublishToMarketplacePageComponent implements OnInit { categories: Filter[] = []; toolkitTypes: Filter[] = []; + imageFile!: { content: File; name: string }; + documentFile!: { content: File; name: string }; + licenseProfile!: { content: File; name: string }; + + blured = false; + focused = false; + + showDescriptionEditor: boolean = false; + addEditLicenseProfile: boolean = false; + constructor( private activatedRoute: ActivatedRoute, private privateCatalogsService: PrivateCatalogsService, private formBuilder: FormBuilder, private filtersService: FiltersService, + private sharedDataService: SharedDataService, + public dialog: MatDialog, + private alertService: AlertService, private publicSolutionsService: PublicSolutionsService, ) { + this.buildForm(); + } + + buildForm() { this.publishToMarketPlaceForm = this.formBuilder.group({ catalog: [null, [Validators.required]], name: [ @@ -89,22 +115,22 @@ export class PublishToMarketplacePageComponent implements OnInit { .getSolutionDetails(this.solutionId, this.revisionId) .subscribe({ next: (res) => { - this.solution = res; - console.log({ res }); - this.publishToMarketPlaceForm.setValue({ - name: res.name, - }); + if (res) { + this.solution = res; + this.publishToMarketPlaceForm.patchValue({ + name: res.name, + }); + } }, - error: (error) => console.log(), + error: (error) => {}, }); }); this.privateCatalogsService.getAllAvailableCatalogs().subscribe({ next: (res) => { - this.catalogs = res; - console.log({ res }); + if (res) this.catalogs = res; }, - error: (error) => console.log(), + error: (error) => {}, }); this.filtersService.getModelTypes().subscribe( @@ -119,8 +145,8 @@ export class PublishToMarketplacePageComponent implements OnInit { this.filtersService.getToolkitTypes().subscribe( (res: CatalogFilter[]) => { - this.toolkitTypes = res.map((catalog: CatalogFilter) => ({ - name: catalog.name, + this.toolkitTypes = res.map((toolkitType: CatalogFilter) => ({ + name: toolkitType.name, selected: false, })); }, @@ -137,9 +163,114 @@ export class PublishToMarketplacePageComponent implements OnInit { ); } - onChangeCatalog(catalog: Catalog) { - this.selectedCatalog = catalog; + onChangeCatalog(event: any) { + console.log({ event }); } submit() {} + + created(event: any) { + // tslint:disable-next-line:no-console + console.log(event); + } + + focus($event: any) { + // tslint:disable-next-line:no-console + console.log('focus', $event); + this.focused = true; + this.blured = false; + } + + blur($event: any) { + // tslint:disable-next-line:no-console + console.log('blur', $event); + this.focused = false; + this.blured = true; + } + + onaAddEditImage() {} + onAddEditDocuments() {} + onAddEditLicenseProfile() {} + + getFilenameExtension(filename: string): string { + // Split the filename by dot (.) and get the last element of the array + const parts = filename.split('.'); + return parts[parts.length - 1]; + } + + selectImageFile(event: any): void { + if (event.target.files && event.target.files[0]) { + const file: File = event.target.files[0]; + this.imageFile.content = file; + this.imageFile.name = file.name; + this.publishToMarketPlaceForm.patchValue({ image: file }); + const extensionFile = this.getFilenameExtension(this.imageFile.name); + } + } + + selectDocumentFile(event: any, file: { content: File; name: string }) { + if (event.target.files && event.target.files[0]) { + const file: File = event.target.files[0]; + this.documentFile.content = file; + this.documentFile.name = file.name; + this.publishToMarketPlaceForm.patchValue({ document: file }); + const extensionFile = this.getFilenameExtension(this.documentFile.name); + } + } + + selectLicenseProfileFile(event: any, file: { content: File; name: string }) { + if (event.target.files && event.target.files[0]) { + const file: File = event.target.files[0]; + this.licenseProfile.content = file; + this.licenseProfile.name = file.name; + this.publishToMarketPlaceForm.patchValue({ licenseProfile: file }); + const extensionFile = this.getFilenameExtension(this.licenseProfile.name); + } + } + + async onClickUploadImageFile() { + const dialogRef: MatDialogRef<UploadLicenseProfileComponent> = + this.dialog.open(UploadLicenseProfileComponent, { + data: { + dataKey: { + title: 'Upload image model', + isEditMode: false, + isCheckExtension: false, + action: (file: File) => {}, + isLicenseProfile: false, + isProcessEvent: false, + }, + }, + }); + + dialogRef.afterClosed().subscribe((result) => { + // This will be executed when the dialog is closed + // Reload data to fetch the updated license profile + }); + } + + onClickUploadDocumentFile() { + const dialogRef: MatDialogRef<UploadLicenseProfileComponent> = + this.dialog.open(UploadLicenseProfileComponent, { + data: { + dataKey: { + title: 'Upload document model', + isEditMode: false, + isCheckExtension: false, + action: (file: File) => {}, + isLicenseProfile: false, + isProcessEvent: false, + }, + }, + }); + + dialogRef.afterClosed().subscribe((result) => { + // This will be executed when the dialog is closed + // Reload data to fetch the updated license profile + }); + } + + onLicenseProfileClick() { + this.addEditLicenseProfile = true; + } }