From c9133ce3248c7e2d1151befab087ce8f88869e60 Mon Sep 17 00:00:00 2001 From: kaw67872 <kawtar.laariche@iais.fraunhofer.de> Date: Thu, 20 Jun 2024 22:18:43 +0200 Subject: [PATCH] #19: update publish to market place form --- src/app/core/config/api-config.ts | 4 + src/app/core/services/alert.service.ts | 2 +- .../core/services/private-catalogs.service.ts | 205 +++++++++++- .../core/services/public-solutions.service.ts | 4 +- .../shared-data/shared-data.service.ts | 6 + .../model-details/model-details.component.ts | 9 +- ...nage-publisher-authors-page.component.html | 194 +++++------- ...nage-publisher-authors-page.component.scss | 80 ++++- ...manage-publisher-authors-page.component.ts | 18 ++ .../model-details-description.component.ts | 2 - .../model-details-documents.component.ts | 2 +- ...model-details-license-profile.component.ts | 25 +- ...publish-to-marketplace-page.component.html | 151 ++++----- ...publish-to-marketplace-page.component.scss | 83 +++++ .../publish-to-marketplace-page.component.ts | 299 ++++++++++++++++-- .../rich-text-editor-dialog.component.html | 5 +- .../rich-text-editor-dialog.component.ts | 33 +- .../update-model-name-dialog.component.html | 46 +++ .../update-model-name-dialog.component.scss | 83 +++++ ...update-model-name-dialog.component.spec.ts | 23 ++ .../update-model-name-dialog.component.ts | 100 ++++++ src/app/shared/models/empty-models.model.ts | 41 +++ src/app/shared/models/index.ts | 1 + .../shared/models/public-solution.model.ts | 3 + src/app/shared/models/request-payloads.ts | 10 + 25 files changed, 1174 insertions(+), 255 deletions(-) create mode 100644 src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.html create mode 100644 src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.scss create mode 100644 src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.spec.ts create mode 100644 src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.ts create mode 100644 src/app/shared/models/empty-models.model.ts diff --git a/src/app/core/config/api-config.ts b/src/app/core/config/api-config.ts index b6e448f..ef0c8f3 100644 --- a/src/app/core/config/api-config.ts +++ b/src/app/core/config/api-config.ts @@ -61,4 +61,8 @@ export const apiConfig = { urlUserSolutions: '/api/user/solutions', urlSearchSolutionsByName: '/api/onboardingDocker/dockerSearchSolutions', urlAllCatalogsList: '/api/catalogs', + urlCreateTags: '/api/tags/create', + urlDeleteTag: '/api/dropTag', + urlAddTag: '/api/addTag', + urlGetAllTag: '/api/tags', }; diff --git a/src/app/core/services/alert.service.ts b/src/app/core/services/alert.service.ts index 06bec80..e8aef50 100644 --- a/src/app/core/services/alert.service.ts +++ b/src/app/core/services/alert.service.ts @@ -10,7 +10,7 @@ import { Alert } from '../../shared/models/alert.model'; export class AlertService { constructor(private snackBar: MatSnackBar) {} - notify(alert: Alert, duration: number): void { + notify(alert: Alert, duration?: number): void { const config: MatSnackBarConfig<Alert> = { duration: duration, verticalPosition: 'top', diff --git a/src/app/core/services/private-catalogs.service.ts b/src/app/core/services/private-catalogs.service.ts index bee923e..2e187e6 100644 --- a/src/app/core/services/private-catalogs.service.ts +++ b/src/app/core/services/private-catalogs.service.ts @@ -10,10 +10,14 @@ import { PublicSolution, PublicSolutionDetailsModel, PublicSolutionsRequestPayload, + Tag, ThreadModel, UserDetails, } from 'src/app/shared/models'; -import { CreateRatingRequestPayload } from 'src/app/shared/models/request-payloads'; +import { + CreateRatingRequestPayload, + UpdateSolutionRequestPayload, +} from 'src/app/shared/models/request-payloads'; import { MessageStatusModel } from 'src/app/shared/models/message-status.model'; @Injectable({ @@ -641,4 +645,203 @@ export class PrivateCatalogsService { }), ); } + + updateCompanyDescription( + description: string, + solutionId: string, + revisionId: string, + selectedCatalogId: string, + ) { + const request = { + request_body: { + description, + }, + }; + const url = + apiConfig.apiBackendURL + + '/api/solution/revision/' + + solutionId + + '/' + + revisionId + + '/' + + selectedCatalogId + + '/description'; + + return this._httpSharedService.post(url, undefined, request).pipe( + map((res) => res.response_body.description), + catchError((error) => { + throw error; + }), + ); + } + + /** + * update solution name or model category(modelType) or toolkitType + * A solution toolkitType could not be updated without select a modelType beforehand and vice versa + * @param solution + * @returns model details + */ + + updateSolution(solution: UpdateSolutionRequestPayload) { + const req = { + request_body: { + solution, + }, + }; + const url = + apiConfig.apiBackendURL + + apiConfig.urlSolutions + + '/' + + solution.solutionId; + + return this._httpSharedService.put(url, undefined, req).pipe( + tap((res) => { + console.log({ res }); + }), + catchError((error) => { + throw error; + }), + ); + } + + createTags(tag: Tag) { + const req = { + request_body: { + tag, + }, + }; + const url = apiConfig.apiBackendURL + apiConfig.urlCreateTags; + + return this._httpSharedService.post(url, undefined, req).pipe( + tap((res) => { + console.log({ res }); + }), + catchError((error) => { + throw error; + }), + ); + } + + deleteTag(tag: Tag, solutionId: string) { + const url = + apiConfig.apiBackendURL + + apiConfig.urlDeleteTag + + '/' + + solutionId + + '/tag/' + + tag; + + return this._httpSharedService.delete(url).pipe( + tap((res) => { + console.log({ res }); + }), + catchError((error) => { + throw error; + }), + ); + } + + updateAddTag(tag: Tag, solutionId: string) { + const url = + apiConfig.apiBackendURL + + apiConfig.urlAddTag + + '/' + + solutionId + + '/tag/' + + tag; + + return this._httpSharedService.put(url).pipe( + tap((res) => { + console.log({ res }); + }), + catchError((error) => { + throw error; + }), + ); + } + + getAllTag(): Observable<Tag[]> { + const url = apiConfig.apiBackendURL + apiConfig.urlGetAllTag; + + const req = { + request_body: { + page: 0, + }, + }; + + return this._httpSharedService.put(url, undefined, req).pipe( + tap((res) => { + console.log({ res }); + }), + map((res) => res.response_body.tags), + catchError((error) => { + throw error; + }), + ); + } + + setSolutionPicture(solutionId: string, image: File) { + const url = + apiConfig.apiBackendURL + + apiConfig.urlSolutions + + '/' + + solutionId + + '/picture'; + const formData = new FormData(); + formData.append('file', image); + + const req = new HttpRequest('POST', url, formData); + + return this.http.request(req); + } + + updateSolutionFiles( + solutionId: string, + revisionId: string, + selectedCatalogId: string, + file: File, + ) { + const uploadUrl = + '/api/solution/' + + solutionId + + '/revision/' + + revisionId + + '/' + + selectedCatalogId + + '/document'; + const formData = new FormData(); + formData.append('file', file); + + const req = new HttpRequest('POST', uploadUrl, formData); + + return this.http.request(req); + } + + getSolutionsFiles() {} + + DeleteSolutionsFiles( + solutionId: string, + revisionId: string, + selectedCatalogId: string, + documentId: string, + ) { + const url = + '/api/solution/' + + solutionId + + '/revision/' + + revisionId + + '/' + + selectedCatalogId + + '/document' + + documentId; + + return this._httpSharedService.delete(url).pipe( + tap((res) => { + console.log({ res }); + }), + catchError((error) => { + throw error; + }), + ); + } } diff --git a/src/app/core/services/public-solutions.service.ts b/src/app/core/services/public-solutions.service.ts index 98ce701..1e3ee32 100644 --- a/src/app/core/services/public-solutions.service.ts +++ b/src/app/core/services/public-solutions.service.ts @@ -243,7 +243,7 @@ export class PublicSolutionsService { ); return this._httpSharedService.get(url, undefined).pipe( - tap(), + map((res) => res.response_body), catchError((error) => { throw error; }), @@ -305,7 +305,7 @@ export class PublicSolutionsService { apiConfig.urlGetSolutionDescription(revisionId, selectedCatalogId); return this._httpSharedService.get(url, undefined).pipe( - tap(), + tap((res) => console.log({ res })), catchError((error) => { throw error; }), diff --git a/src/app/core/services/shared-data/shared-data.service.ts b/src/app/core/services/shared-data/shared-data.service.ts index bc50def..5f67c21 100644 --- a/src/app/core/services/shared-data/shared-data.service.ts +++ b/src/app/core/services/shared-data/shared-data.service.ts @@ -35,6 +35,8 @@ export class SharedDataService { latestRevisionId: '', modelTypeName: '', modelType: '', + tookitType: '', + tookitTypeName: '', revisions: [], catalogNames: [], publisher: '', @@ -100,6 +102,10 @@ export class SharedDataService { return this._authorListSubject.asObservable(); } + get authorListValue(): AuthorPublisherModel[] { + return this._authorListSubject.getValue(); + } + set authorList(value: AuthorPublisherModel[]) { this._authorListSubject.next(value); } diff --git a/src/app/features/model-details/model-details.component.ts b/src/app/features/model-details/model-details.component.ts index 23f4d67..5b36486 100644 --- a/src/app/features/model-details/model-details.component.ts +++ b/src/app/features/model-details/model-details.component.ts @@ -163,9 +163,6 @@ export class ModelDetailsComponent implements OnInit { .pipe(map((details) => !!details?.userId)); this.data$ = this.activatedRoute.params.pipe( - tap((params) => { - console.log({ params }); - }), map((params) => ({ solutionId: params['solutionId'] as string, revisionId: params['revisionId'] as string, @@ -288,11 +285,7 @@ export class ModelDetailsComponent implements OnInit { } setRevisionInService(revision: Revision): void { - this.sharedDataService.selectedRevision = { - version: revision.version, - revisionId: revision.revisionId, - onBoarded: revision.onBoarded, - }; + this.sharedDataService.selectedRevision = revision; } seSolutionInService(solution: PublicSolution): void { diff --git a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.html b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.html index 03d0f15..bb71e52 100644 --- a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.html +++ b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.html @@ -1,27 +1,14 @@ <div class="workflow-right-header workflow-header">Manage Authors</div> -<div style="display: flex; width: 100%; height: 60%; padding: 20px"> - <div - style=" - display: flex; - flex-direction: column; - padding: 10px; - width: 100%; - gap: 20px; - " - > +<div class="flex-container"> + <div class="column-layout"> <span>PUBLISHER</span> <mat-divider></mat-divider> - <form - style="display: flex; flex-direction: column; height: 100%" - [formGroup]="publisherForm" - > - <div - style="display: flex; flex-direction: column; flex-grow: 1; gap: 20px" - > + <form class="publisher-form-layout" [formGroup]="publisherForm"> + <div class="column-flex-grow"> <mat-label>Publisher Name </mat-label> - <div style="display: flex; flex-direction: column"> + <div class="flex-column"> <mat-form-field> - <input class="example-full-width" matInput formControlName="name" /> + <input class="full-width" matInput formControlName="name" /> @if (publisherForm.value.name) { <button matSuffix @@ -67,16 +54,16 @@ </div> </div> - <div style="display: flex; justify-content: space-between"> + <div class="button-row"> <button - style="margin-right: 20px; width: 135px" + class="discard-changes-button" mat-raised-button (click)="onCancelClick()" > Discard changes </button> <button - style="margin-right: 20px; width: 100px" + class="save-button" color="primary" mat-raised-button (click)="OnEditPublisherClick()" @@ -87,110 +74,87 @@ </div> </form> </div> - <mat-divider vertical style="height: auto"></mat-divider> - <div - style=" - display: flex; - flex-direction: column; - padding: 10px; - width: 100%; - height: 100%; - " - > - <div style="display: flex; flex-direction: column; gap: 20px; height: 100%"> - <span>AUTHORS</span> - <mat-divider></mat-divider> - <mat-chip-set #chipGrid> - @for (author of authorsList; track author) { - <mat-chip-row (removed)="onRemoveAuthorClick(author)"> - {{ author.name }} - <button matChipRemove [attr.aria-label]="'remove ' + author.name"> - <mat-icon>cancel</mat-icon> - </button> - </mat-chip-row> - } @empty { - <span>No authors listed.</span> - } - </mat-chip-set> - Please add additional names of one or more author of this model. - <form - style=" - display: flex; - flex-direction: column; - - height: 100%; - " - [formGroup]="authorForm" - #formDirective="ngForm" - > - <div style="display: flex; flex-direction: column; flex-grow: 1"> - <div style="display: flex; flex-direction: column"> - <mat-label class="asterisk--after">Name </mat-label> + <mat-divider vertical class="flexible-height-divider"></mat-divider> + <div class="author-form-layout"> + <span>AUTHORS</span> + <mat-divider></mat-divider> + <mat-chip-set #chipGrid> + @for (author of authorsList; track author) { + <mat-chip-row (removed)="onRemoveAuthorClick(author)"> + {{ author.name }} + <button matChipRemove [attr.aria-label]="'remove ' + author.name"> + <mat-icon>cancel</mat-icon> + </button> + </mat-chip-row> + } @empty { + <span>No authors listed.</span> + } + </mat-chip-set> + Please add additional names of one or more author of this model. + <form + class="flex-column full-height" + [formGroup]="authorForm" + #formDirective="ngForm" + > + <div class="flex-column flex-grow"> + <div class="flex-column"> + <mat-label class="asterisk--after">Name </mat-label> + <mat-form-field> + <input class="full-width" matInput formControlName="name" /> + @if (authorForm.value.name) { + <button + matSuffix + mat-icon-button + aria-label="Clear" + (click)="authorForm.controls['name'].setValue('')" + > + <mat-icon>close</mat-icon> + </button> + } + @if (authorForm.controls["contact"].hasError("required")) { + <mat-error class="modal-error">Name is required</mat-error> + } + </mat-form-field> + </div> + <div class="flex-column"> + <mat-label class="asterisk--after"> Contact Info</mat-label> + <div class="flex-column"> <mat-form-field> - <input - class="example-full-width" - matInput - formControlName="name" - /> - @if (authorForm.value.name) { + <input class="full-width" matInput formControlName="contact" /> + @if (authorForm.value.contact) { <button matSuffix mat-icon-button aria-label="Clear" - (click)="authorForm.controls['name'].setValue('')" + (click)="authorForm.controls['contact'].setValue('')" > <mat-icon>close</mat-icon> </button> } - @if (authorForm.controls["contact"].hasError("required")) { - <mat-error class="modal-error">Name is required</mat-error> - } + <mat-hint align="start" class="modal-note full-width" + >You can mention Email Address,URL and Phone Number + </mat-hint> </mat-form-field> - </div> - <div style="display: flex; flex-direction: column"> - <mat-label class="asterisk--after"> Contact Info</mat-label> - <div style="display: flex; flex-direction: column"> - <mat-form-field> - <input - class="example-full-width" - matInput - formControlName="contact" - /> - @if (authorForm.value.contact) { - <button - matSuffix - mat-icon-button - aria-label="Clear" - (click)="authorForm.controls['contact'].setValue('')" - > - <mat-icon>close</mat-icon> - </button> - } - <mat-hint align="start" class="modal-note example-full-width" - >You can mention Email Address,URL and Phone Number - </mat-hint> - </mat-form-field> - @if ( - authorForm.controls["contact"].hasError("required") && - authorForm.controls["contact"].touched - ) { - <mat-error class="modal-error" - >Contact info is required</mat-error - > - } - </div> + @if ( + authorForm.controls["contact"].hasError("required") && + authorForm.controls["contact"].touched + ) { + <mat-error class="modal-error" + >Contact info is required</mat-error + > + } </div> </div> - <button - color="primary" - style="align-self: end; margin-right: 20px; width: 100px" - mat-raised-button - (click)="onAddAuthorClick(formDirective)" - [disabled]="!authorForm.valid" - > - Add author - </button> - </form> - </div> + </div> + <button + color="primary" + class="add-author-button" + mat-raised-button + (click)="onAddAuthorClick(formDirective)" + [disabled]="!authorForm.valid" + > + Add author + </button> + </form> </div> </div> diff --git a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.scss b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.scss index b57625f..9b8b091 100644 --- a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.scss +++ b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.scss @@ -1,4 +1,4 @@ -.example-full-width { +.full-width { width: 100%; } .purple { @@ -40,3 +40,81 @@ margin-left: 4px; cursor: pointer; } + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-container { + display: flex; + width: 100%; + height: 60%; + padding: 20px; +} + +.column-layout { + display: flex; + flex-direction: column; + padding: 10px; + width: 100%; + gap: 20px; +} + +.publisher-form-layout { + display: flex; + flex-direction: column; + height: 100%; +} + +.column-flex-grow { + display: flex; + flex-direction: column; + flex-grow: 1; + gap: 20px; +} + +.button-row { + display: flex; + justify-content: space-between; +} + +.discard-changes-button { + margin-right: 20px; + width: 135px; +} + +.save-button { + margin-right: 20px; + width: 100px; +} + +.flexible-height-divider { + height: auto; +} + +.full-height { + height: 100%; +} + +.btn-save-author { + align-self: end; + margin-right: 20px; + width: 100px; +} + +.author-form-layout { + display: flex; + flex-direction: column; + gap: 20px; + height: 100%; +} + +.add-author-button { + align-self: end; + margin-right: 20px; + width: 100px; +} +.flex-grow { + flex-grow: 1; +} diff --git a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.ts b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.ts index 8b06d94..91967e7 100644 --- a/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.ts +++ b/src/app/shared/components/manage-publisher-authors-page/manage-publisher-authors-page.component.ts @@ -24,6 +24,7 @@ import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { DeleteUserDialogConfirmationActionComponent } from '../delete-user-dialog-confirmation-action/delete-user-dialog-confirmation-action.component'; import { Observable, map, of } from 'rxjs'; import { AlertService } from 'src/app/core/services/alert.service'; +import { SharedDataService } from 'src/app/core/services/shared-data/shared-data.service'; @Component({ selector: 'gp-manage-publisher-authors-page', @@ -57,6 +58,7 @@ export class ManagePublisherAuthorsPageComponent implements OnInit { private privateCatalogsService: PrivateCatalogsService, public dialog: MatDialog, private alertService: AlertService, + private sharedDataService: SharedDataService, ) { this.authorForm = this.formBuilder.group({ name: ['', Validators.required], @@ -90,6 +92,7 @@ export class ManagePublisherAuthorsPageComponent implements OnInit { this.privateCatalogsService.getAuthors(solutionId, revisionId).subscribe({ next: (res) => { this.authorsList = res; + this.updateAuthorsService(res); }, error: (error) => { console.error('Error fetching users:', error); @@ -141,6 +144,7 @@ export class ManagePublisherAuthorsPageComponent implements OnInit { this.alertService.notify(alert, 3000); this.authorForm.reset(); formDirective.resetForm(); + this.updateAuthorsService(res); }, error: (error) => { const alert: Alert = { @@ -293,4 +297,18 @@ export class ManagePublisherAuthorsPageComponent implements OnInit { clearPublisherNameValidation(control: AbstractControl<any, any>) { control.clearAsyncValidators(); } + + updateAuthorsService(authors: AuthorPublisherModel[]) { + // Get the current value directly + let authorsList = this.sharedDataService.authorListValue; + + if (!authorsList) { + console.log('no model details'); + // Properly initialize if it's not yet set + authorsList = []; + } + + // Safely assign new values + this.sharedDataService.authorList = authors; + } } diff --git a/src/app/shared/components/model-details-description/model-details-description.component.ts b/src/app/shared/components/model-details-description/model-details-description.component.ts index 5c71074..97f314f 100644 --- a/src/app/shared/components/model-details-description/model-details-description.component.ts +++ b/src/app/shared/components/model-details-description/model-details-description.component.ts @@ -41,7 +41,6 @@ export class ModelDetailsDescriptionComponent { solutionCompanyDesc = ''; checkFlag = true; ratedescriptioncheck = 0; - private subscriptions: Subscription = new Subscription(); constructor( private activatedRoute: ActivatedRoute, @@ -120,7 +119,6 @@ export class ModelDetailsDescriptionComponent { this.processDescription(this.description); }, (error) => { - console.log(error); this.description = ''; this.ratedescriptioncheck = 0; }, diff --git a/src/app/shared/components/model-details-documents/model-details-documents.component.ts b/src/app/shared/components/model-details-documents/model-details-documents.component.ts index 9d6785b..0a25b31 100644 --- a/src/app/shared/components/model-details-documents/model-details-documents.component.ts +++ b/src/app/shared/components/model-details-documents/model-details-documents.component.ts @@ -67,7 +67,7 @@ export class ModelDetailsDocumentsComponent implements OnInit, OnDestroy { ) .subscribe( (res) => { - this.supportingDocs = res.response_body; + this.supportingDocs = res; this.transformDocuments(); }, (err) => { diff --git a/src/app/shared/components/model-details-license-profile/model-details-license-profile.component.ts b/src/app/shared/components/model-details-license-profile/model-details-license-profile.component.ts index 149025e..e6b15c7 100644 --- a/src/app/shared/components/model-details-license-profile/model-details-license-profile.component.ts +++ b/src/app/shared/components/model-details-license-profile/model-details-license-profile.component.ts @@ -3,13 +3,13 @@ import { CommonModule } from '@angular/common'; import { SharedDataService } from 'src/app/core/services/shared-data/shared-data.service'; import { PublicSolutionsService } from 'src/app/core/services/public-solutions.service'; import { ActivatedRoute } from '@angular/router'; -import { Observable, Subscription, map, throwError } from 'rxjs'; +import { Observable, Subscription, map } from 'rxjs'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog, MatDialogRef } from '@angular/material/dialog'; import { CreateEditLicenseProfileComponent } from '../create-edit-license-profile/create-edit-license-profile.component'; import { UploadLicenseProfileComponent } from '../upload-license-profile/upload-license-profile.component'; import { BrowserStorageService } from 'src/app/core/services/storage/browser-storage.service'; -import { LicenseProfileModel } from '../../models'; +import { emptyLicenseProfileModel, LicenseProfileModel } from '../../models'; import { PrivateCatalogsService } from 'src/app/core/services/private-catalogs.service'; @Component({ @@ -121,26 +121,7 @@ export class ModelDetailsLicenseProfileComponent implements OnInit { this.isLoadingLicense = false; this.modelLicenseError = 'No license found'; console.error('Error: Response is undefined or null'); - this.modelLicense = { - $schema: '', - companyName: '', - contact: { - name: '', - URL: '', - email: '', - }, - copyright: { - year: 2021, - company: '', - suffix: '', - }, - intro: '', - keyword: '', - licenseName: '', - rtuRequired: false, - softwareType: '', - additionalInfo: '', - }; + this.modelLicense = emptyLicenseProfileModel; } }, (err) => { 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 3f4f0d5..bb0a9bf 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 @@ -1,23 +1,12 @@ <div class="workflow-right-header workflow-header">Publish to Marketplace</div> -<div style="display: flex; flex-direction: column; padding: 40px"> +<div class="container" *ngIf="data$ | async as data"> <!-- status stepper start--> <div></div> <!-- status stepper end--> - <!--Select catalog start--> - <div> - <!-- <mat-form-field> - <mat-label>Select a catalog</mat-label> - <mat-select [(ngModel)]="selectedCatalog" name="catalog"> - @for (catalog of catalogs; track catalog) { - <mat-option [value]="catalog">{{ catalog.name }}</mat-option> - } - </mat-select> - </mat-form-field> --> - </div> - <!--Select catalog end--> + <!-- Steps to submit publication start--> <form [formGroup]="publishToMarketPlaceForm" (ngSubmit)="submit()"> - <div style="display: flex; flex-direction: column"> + <div class="flex-column"> <mat-form-field> <mat-label>Select a catalog</mat-label> <mat-select @@ -31,22 +20,42 @@ </mat-form-field> @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> - - <button color="primary" (click)="addEditDescription()"> - <mat-icon>add</mat-icon> + <div class="flex-column"> + <div class="flex-row"> + <mat-form-field> + <mat-label>Model name</mat-label> + <input matInput formControlName="name" /> + </mat-form-field> + <button class="custom-icon" (click)="onClickEditModelName()"> + <mat-icon>edit</mat-icon> </button> </div> - <div style="display: flex; gap: 20px; width: 100%"> - <mat-form-field style="width: 100%"> + + <div class="flex-column"> + <div class="flex-row"> + <span>Model description</span> + <button class="custom-icon" (click)="addEditDescription()"> + @if (publishToMarketPlaceForm.value.description) { + <mat-icon>edit</mat-icon> + } @else { + <mat-icon>add</mat-icon> + } + </button> + </div> + + <div + *ngIf="publishToMarketPlaceForm.value.description" + [innerHTML]="publishToMarketPlaceForm.value.description" + ></div> + </div> + + <div class="select-category"> + <mat-form-field class="full-width"> <mat-label>Select category</mat-label> - <mat-select formControlName="category"> + <mat-select + formControlName="category" + [compareWith]="compareObjects" + > @for (category of categories; track category) { <mat-option [value]="category">{{ category.name @@ -54,9 +63,13 @@ } </mat-select> </mat-form-field> - <mat-form-field style="width: 100%"> + <mat-form-field class="full-width"> <mat-label>Select toolkitType</mat-label> - <mat-select formControlName="toolkitType"> + + <mat-select + formControlName="toolkitType" + [compareWith]="compareObjects" + > @for (toolkitType of toolkitTypes; track toolkitType) { <mat-option [value]="toolkitType">{{ toolkitType.name @@ -66,20 +79,21 @@ </mat-form-field> </div> <!--license profile start--> - <div style="display: flex; flex-direction: column"> - <div style="display: flex; align-items: center; gap: 40px"> + <div class="flex-column"> + <div class="license-container"> <span>Model license profile</span> - <button - (click)="onLicenseProfileClick()" - mat-raised-button - color="primary" - > - Add/Edit license profile + + <button class="custom-icon" (click)="onLicenseProfileClick()"> + @if (data.licenseProfile) { + <mat-icon>edit</mat-icon> + } @else { + <mat-icon>add</mat-icon> + } </button> </div> </div> @if (addEditLicenseProfile) { - <div style="display: flex; width: 100%"> + <div class="flex-row full-width"> <gp-model-details-license-profile [isExistingLicenseProfile]="true" ></gp-model-details-license-profile> @@ -87,18 +101,10 @@ } <!--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> + <div class="upload-file-containe"> + <span class="upload-file-text">Upload image model </span> - <mat-form-field style="height: 70px; flex-grow: 1"> + <mat-form-field class="mat-form-field-upload-file"> <input matInput [value]="imageFile?.name" /> <input @@ -113,7 +119,7 @@ </mat-form-field> <button - style="height: 48px" + class="upload-file-button" color="primary" mat-raised-button (click)="onClickUploadImageFile()" @@ -123,11 +129,11 @@ </div> <!--upload documents end--> <!--tags starts--> - <div style="display: flex; align-items: center"> - <mat-form-field class="example-chip-list" style="flex-grow: 1"> + <div class="tags-container"> + <mat-form-field class="flex-grow"> <mat-label>Add tags</mat-label> <mat-chip-grid #chipGrid formControlName="tags"> - @for (tag of tags; track tag) { + @for (tag of tags(); track tag) { <mat-chip-row (removed)="remove(tag)"> {{ tag.tag }} <button matChipRemove [attr.aria-label]="'remove ' + tag"> @@ -146,18 +152,10 @@ </div> <!--tags ends--> <!--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> + <div class="upload-file-container"> + <span class="upload-file-text">Upload image model </span> - <mat-form-field style="height: 70px; flex-grow: 1"> + <mat-form-field class="mat-form-field-upload-file"> <input matInput [value]="imageFile?.name" /> <input @@ -172,7 +170,7 @@ </mat-form-field> <button - style="height: 48px" + class="upload-file-button" color="primary" mat-raised-button (click)="onClickUploadImageFile()" @@ -184,18 +182,21 @@ </div> } - <div - style=" - display: flex; - align-items: center; - justify-content: space-between; - margin-top: 40px; - " - > - <button style="width: 20%" mat-raised-button color="primary"> + <div class="buttons-container"> + <button + (click)="unpublishModel()" + mat-raised-button + color="primary" + [disabled]="publishToMarketPlaceForm.invalid" + > Unpublish model </button> - <button style="width: 20%" mat-raised-button color="primary"> + <button + mat-raised-button + color="primary" + type="submit" + [disabled]="publishToMarketPlaceForm.invalid" + > Publish model </button> </div> diff --git a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.scss b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.scss index e69de29..5a9094b 100644 --- a/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.scss +++ b/src/app/shared/components/publish-to-marketplace-page/publish-to-marketplace-page.component.scss @@ -0,0 +1,83 @@ +@use "@angular/material" as mat; +@import "../../../../styles.scss"; + +.custom-icon { + background-color: transparent !important; + border: none !important; + color: mat.get-color-from-palette($graphene-ui-primary); + cursor: pointer; +} + +.container { + display: flex; + flex-direction: column; + padding: 40px; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-row { + display: flex; + flex-direction: row; +} + +.select-category { + display: flex; + gap: 20px; + width: 100%; +} + +.full-width { + width: 100%; +} + +.license-container { + display: flex; + align-items: center; + gap: 40px; +} + +.upload-file-container { + display: flex; + flex-direction: row; + width: 100%; + gap: 20px; + align-items: center; +} + +.upload-file-text { + width: 20%; +} + +.mat-form-field-upload-file { + height: 70px; + flex-grow: 1; +} + +.upload-file-button { + height: 48px; +} + +.tags-container { + display: flex; + align-items: center; +} + +.flex-grow { + flex-grow: 1; +} + +.buttons-container { + display: flex; + align-items: center; + justify-content: flex-end; + margin-top: 40px; + gap: 20px; + + button { + width: 20%; + } +} 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 6620a2c..bb86563 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 @@ -1,13 +1,16 @@ -import { Component, inject, OnInit } from '@angular/core'; +import { Component, inject, OnInit, Signal, signal } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ActivatedRoute } from '@angular/router'; import { PublicSolutionsService } from 'src/app/core/services/public-solutions.service'; import { + Alert, + AlertType, Catalog, CatalogFilter, Filter, LicenseProfileModel, PublicSolutionDetailsModel, + Revision, Tag, ToolkitTypeCode, toolkitTypeCodeMap, @@ -35,9 +38,37 @@ import { AlertService } from 'src/app/core/services/alert.service'; import { ModelDetailsLicenseProfileComponent } from '../model-details-license-profile/model-details-license-profile.component'; import { MatChipInputEvent, MatChipsModule } from '@angular/material/chips'; import { MatIconModule } from '@angular/material/icon'; -import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { LiveAnnouncer } from '@angular/cdk/a11y'; import { RichTextEditorDialogComponent } from '../rich-text-editor-dialog/rich-text-editor-dialog.component'; +import { + catchError, + combineLatest, + map, + Observable, + of, + switchMap, + tap, +} from 'rxjs'; +import { UpdateModelNameDialogComponent } from '../update-model-name-dialog/update-model-name-dialog.component'; +import { UpdateSolutionRequestPayload } from '../../models/request-payloads'; +interface RouteParams { + solutionId: string; + revisionId: string; +} + +interface ModelData { + solutionId: string; + revisionId: string; + solution?: PublicSolutionDetailsModel | null; // Allow null here if your data source might not provide a solution. + picture?: any | null; // Adjust type as needed + description?: any | null; // Use string or any, and allow for null if your fetch might fail + licenseProfile?: LicenseProfileModel | LicenseProfileModel[] | null; // Allow both array and null + category?: CatalogFilter | null; + toolkitType?: CatalogFilter | null; + image?: any | null; + tags?: Tag[]; + documents?: any[]; +} @Component({ selector: 'gp-publish-to-marketplace-page', @@ -65,23 +96,30 @@ export class PublishToMarketplacePageComponent implements OnInit { revisionId!: string; catalogs!: Catalog[]; selectedCatalog!: Catalog; + selectedToolkitType!: CatalogFilter; + selectedCategory!: CatalogFilter; publishToMarketPlaceForm!: FormGroup; solution!: PublicSolutionDetailsModel; - categories: Filter[] = []; - toolkitTypes: Filter[] = []; + categories: CatalogFilter[] = []; + toolkitTypes: CatalogFilter[] = []; imageFile!: { content: File; name: string }; documentFile!: { content: File; name: string }; licenseProfile!: { content: File; name: string }; - tags: Tag[] = []; + readonly tags = signal<Tag[]>([]); + descInnerHTML!: string; blured = false; focused = false; + data$: Observable<ModelData>; + addEditLicenseProfile: boolean = false; announcer = inject(LiveAnnouncer); + defaultSolutionData = {}; + constructor( private activatedRoute: ActivatedRoute, private privateCatalogsService: PrivateCatalogsService, @@ -94,6 +132,61 @@ export class PublishToMarketplacePageComponent implements OnInit { private publicSolutionsService: PublicSolutionsService, ) { this.buildForm(); + this.data$ = + this.activatedRoute.parent?.params.pipe( + switchMap(({ solutionId, revisionId }) => { + return this.publicSolutionsService + .getSolutionDetails(solutionId, revisionId) + .pipe( + switchMap((solution) => { + const documentStream = + this.selectedCatalog && this.selectedCatalog.catalogId + ? this.publicSolutionsService + .getSolutionDocuments( + solutionId, + revisionId, + this.selectedCatalog.catalogId, + ) + .pipe(catchError(() => of([]))) + : of([]); + const descriptionStream = + this.selectedCatalog && this.selectedCatalog.catalogId + ? this.publicSolutionsService + .getSolutionDescription( + revisionId, + this.selectedCatalog.catalogId, + ) + .pipe(catchError(() => of(''))) + : of(''); + return combineLatest({ + solutionId: of(solutionId), + revisionId: of(revisionId), + solution: of(solution || null), + picture: this.publicSolutionsService + .getPictureOfSolution(solutionId) + .pipe(catchError(() => of(null))), + licenseProfile: this.publicSolutionsService + .getLicenseFile(solutionId, revisionId) + .pipe(catchError(() => of(null))), + tags: this.privateCatalogsService.getAllTag(), + documents: documentStream, + description: descriptionStream, + }); + }), + tap((data) => { + this.processSideEffects(data); + }), + catchError((error) => { + console.error('Error in combined data stream:', error); + return of({} as ModelData); // Return an empty ModelData object on error + }), + ); + }), + catchError((error) => { + console.error('Error fetching parameters:', error); + return of({} as ModelData); // Provide a fallback empty ModelData object + }), + ) || of({} as ModelData); // } buildForm() { @@ -145,23 +238,31 @@ export class PublishToMarketplacePageComponent implements OnInit { this.filtersService.getModelTypes().subscribe( (res: CatalogFilter[]) => { - this.categories = res.map((catalog: CatalogFilter) => ({ - name: catalog.name, - selected: false, - })); + this.categories = res; }, (err: any) => {}, ); this.filtersService.getToolkitTypes().subscribe( (res: CatalogFilter[]) => { - this.toolkitTypes = res.map((toolkitType: CatalogFilter) => ({ - name: toolkitType.name, - selected: false, - })); + this.toolkitTypes = res; }, (err: any) => {}, ); + + /* this.sharedDataService.authorList$.subscribe((authors) => { + if (authors) { + if (authors?.length === 0) { + const alert: Alert = { + message: + 'You cannot publish the model without entering the author name. Please add author name in the "Manage Publisher/Authors" page to publish it.', + type: AlertType.Error, + }; + + this.alertService.notify(alert); + } + } + }); */ } convertToolkitTypesToCodes( @@ -175,9 +276,28 @@ export class PublishToMarketplacePageComponent implements OnInit { onChangeCatalog(event: any) { console.log({ event }); + this.selectedCatalog = event.value; + this.publicSolutionsService + .getSolutionDescription(this.revisionId, this.selectedCatalog.catalogId) + .subscribe({ + next: (res) => { + this.publishToMarketPlaceForm.patchValue({ + description: res.response_body.description, + }); + console.log('description function call', res); + console.log( + 'form value', + this.publishToMarketPlaceForm.value.description, + ); + error: (error: any) => console.log({ error }); + }, + }); + console.log({ event }); } - submit() {} + submit() { + console.log(this.publishToMarketPlaceForm.value); + } created(event: any) { // tslint:disable-next-line:no-console @@ -285,13 +405,16 @@ export class PublishToMarketplacePageComponent implements OnInit { } remove(tag: Tag) { - const index = this.tags.indexOf(tag); - - if (index >= 0) { - this.tags.splice(index, 1); + this.tags.update((tags: Tag[]) => { + const index = tags.indexOf(tag); + if (index < 0) { + return tags; + } + tags.splice(index, 1); this.announcer.announce(`Removed ${tag}`); - } + return [...tags]; + }); } addTag(event: MatChipInputEvent): void { @@ -300,13 +423,38 @@ export class PublishToMarketplacePageComponent implements OnInit { // Add our keyword if (value) { - this.tags.push(value); + this.tags.update((tags: Tag[]) => [...tags, value]); } // Clear the input value event.chipInput!.clear(); } + extractDescription(html: string): string { + return html.replace(/<[^>]+>/gm, ''); + } + + onAddEditDescription(htmlVal: string) { + console.log({ htmlVal }); + this.publishToMarketPlaceForm.patchValue({ description: htmlVal }); + this.privateCatalogsService + .updateCompanyDescription( + htmlVal, + this.solutionId, + this.revisionId, + this.selectedCatalog.catalogId, + ) + .subscribe({ + next: (res) => { + this.publishToMarketPlaceForm.patchValue({ + description: res.response_body.description, + }); + error: (error: any) => { + console.log(error); + }; + }, + }); + } addEditDescription() { const dialogRef: MatDialogRef<RichTextEditorDialogComponent> = this.dialog.open(RichTextEditorDialogComponent, { @@ -315,7 +463,10 @@ export class PublishToMarketplacePageComponent implements OnInit { title: 'Add model description', isEditMode: false, isCheckExtension: false, - action: (file: File) => {}, + descInnerHTML: this.publishToMarketPlaceForm.value.description, + action: (htmlVal: string) => { + this.onAddEditDescription(htmlVal); + }, }, }, }); @@ -325,4 +476,110 @@ export class PublishToMarketplacePageComponent implements OnInit { // Reload data to fetch the updated license profile }); } + + private processSideEffects(data: ModelData): void { + this.revisionId = data.revisionId; + this.solutionId = data.solutionId; + + console.log({ data }); + if (data.picture) + this.publishToMarketPlaceForm.patchValue({ image: data.picture }); + if (data.solution?.modelTypeName && data.solution.modelType) { + this.publishToMarketPlaceForm.get('category')?.setValue({ + name: data.solution?.modelTypeName, + code: data.solution.modelType, + }); + console.log( + 'category', + this.publishToMarketPlaceForm.get('category')?.value, + ); + } + + /* if (data.solution?.tookitType && data.solution.tookitTypeName) + this.publishToMarketPlaceForm.controls['toolkitType'].setValue({ + name: data.solution.tookitTypeName, + code: data.solution.tookitType, + }); */ + + if (data.solution?.tookitType && data.solution.tookitTypeName) + this.publishToMarketPlaceForm.controls['toolkitType'].patchValue({ + name: data.solution.tookitTypeName, + code: data.solution.tookitType, + }); + + if (data.documents) + this.publishToMarketPlaceForm.patchValue({ documents: data.documents }); + + if ( + data.solution && + data.solution?.solutionTagList.length && + data.solution.solutionTagList.length >= 1 + ) { + const tagsList = data.solution?.solutionTagList; + this.tags.update((tags: Tag[]) => [...tags, ...tagsList]); + this.publishToMarketPlaceForm.patchValue({ + tags: data.solution?.solutionTagList, + }); + } + if (data.description) { + const desc = this.extractDescription( + data.description.response_body.description, + ); + this.publishToMarketPlaceForm.patchValue({ + description: desc, + }); + } + if (data.solution && data.solution.catalogId) { + const solutionCatalog = this.catalogs.filter( + (c) => c.catalogId === data.solution?.catalogId, + )[0]; + this.publishToMarketPlaceForm.patchValue({ + catalog: solutionCatalog, + }); + } + console.log('form value', this.publishToMarketPlaceForm.value); + } + + editModelName(name: string) { + const updatedSolution: UpdateSolutionRequestPayload = { + ...this.solution, + name, + }; + this.privateCatalogsService.updateSolution(updatedSolution).subscribe({ + next: (res) => { + console.log('solution updated successfully ', res); + }, + error: (error) => { + console.log({ error }); + }, + }); + } + + onClickEditModelName() { + const version = this.solution.revisions.find( + (rv) => rv.revisionId === this.revisionId, + )?.version; + console.log({ version }); + const dialogRef: MatDialogRef<UpdateModelNameDialogComponent> = + this.dialog.open(UpdateModelNameDialogComponent, { + data: { + dataKey: { + modelData: this.solution, + version, + action: (name: string) => this.editModelName(name), + }, + }, + }); + + dialogRef.afterClosed().subscribe((result) => { + // This will be executed when the dialog is closed + // Reload data to fetch the updated license profile + }); + } + + unpublishModel() {} + + compareObjects(o1: CatalogFilter, o2: CatalogFilter): boolean { + return o1.name === o2.name && o1.code === o2.code; + } } diff --git a/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.html b/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.html index 9711c41..35f20eb 100644 --- a/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.html +++ b/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.html @@ -17,8 +17,9 @@ <p> {{ content }} </p> - - <quill-editor [styles]="{ height: '200px' }"></quill-editor></div + <form [formGroup]="form"> + <quill-editor formControlName="html"> </quill-editor> + </form></div ></mat-dialog-content> <mat-toolbar class="form-footer"> <button mat-dialog-close mat-raised-button style="margin-right: auto"> diff --git a/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.ts b/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.ts index 565a394..ef6d0e4 100644 --- a/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.ts +++ b/src/app/shared/components/rich-text-editor-dialog/rich-text-editor-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, OnInit } from '@angular/core'; +import { Component, Inject, OnInit, ViewChild } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialog, @@ -11,7 +11,9 @@ import { CommonModule } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; import { MatToolbarModule } from '@angular/material/toolbar'; import { MatIconModule } from '@angular/material/icon'; -import { QuillModule } from 'ngx-quill'; +import { QuillModule, QuillViewComponent } from 'ngx-quill'; +import Quill from 'quill'; +import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms'; @Component({ selector: 'gp-rich-text-editor-dialog', @@ -23,29 +25,48 @@ import { QuillModule } from 'ngx-quill'; MatToolbarModule, MatIconModule, QuillModule, + ReactiveFormsModule, ], templateUrl: './rich-text-editor-dialog.component.html', styleUrl: './rich-text-editor-dialog.component.scss', }) export class RichTextEditorDialogComponent implements OnInit { + @ViewChild('editorText') editorText!: QuillViewComponent; title!: string; content!: string; alertMessage!: string; + private quillEditor!: Quill; + private onChangeCallback!: (value: string) => void; + private onTouchedCallback!: () => void; + editorTextHtml!: any; + form!: FormGroup; + control!: string; + descInnerHTML!: string; constructor( public dialogRef: MatDialogRef<RichTextEditorDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any, private alertService: AlertService, - ) {} + private formBuilder: FormBuilder, + ) { + this.form = this.formBuilder.group({ + html: [null], + }); + } ngOnInit(): void { this.title = this.data.dataKey.title; this.content = this.data.dataKey.content; this.alertMessage = this.data.dataKey.alertMessage; + this.descInnerHTML = this.data.dataKey.descInnerHTML; + if (this.descInnerHTML) { + this.form.patchValue({ html: this.descInnerHTML }); + } } confirm() { - this.data.dataKey.action().subscribe({ + console.log('html', this.form.value); + this.data.dataKey.action(this.form.value.html).subscribe({ next: (res: any) => { this.alertService.notify( { message: this.alertMessage, type: AlertType.Success }, @@ -62,4 +83,8 @@ export class RichTextEditorDialogComponent implements OnInit { }, }); } + + onClickChange(event: any) { + console.log({ event }); + } } diff --git a/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.html b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.html new file mode 100644 index 0000000..096c89f --- /dev/null +++ b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.html @@ -0,0 +1,46 @@ +<mat-toolbar class="dialog-header"> + <div class="dialog-title"> + <h2>Update Model Name</h2> + </div> + <button + type="button" + mat-icon-button + [mat-dialog-close]="true" + class="close" + tabindex="-1" + > + <mat-icon aria-hidden="false" aria-label="Close icon">close</mat-icon> + </button> +</mat-toolbar> +<mat-dialog-content> + <p> + If you are uploading new version of previous model then use the same model + name. + </p> + <div class="star-rating-container"> + <form [formGroup]="updateModelNameForm"> + <div class="flex-column"> + <mat-form-field> + <mat-label>Model name</mat-label> + <input matInput formControlName="modelName" /> + @if (message !== "") { + <mat-error class="modal-error"> {{ message }}</mat-error> + } + </mat-form-field> + <mat-form-field> + <mat-label>Model Version</mat-label> + <input matInput formControlName="modelVersion" [disabled]="true" /> + </mat-form-field> + <mat-form-field> + <mat-label>Model Id</mat-label> + <input matInput formControlName="modelId " [disabled]="true" /> + </mat-form-field> + </div> + </form></div +></mat-dialog-content> +<mat-toolbar class="form-footer"> + <button mat-dialog-close mat-raised-button class="cancel-button"> + Cancel + </button> + <button color="primary" mat-raised-button (click)="update()">Update</button> +</mat-toolbar> diff --git a/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.scss b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.scss new file mode 100644 index 0000000..db8482d --- /dev/null +++ b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.scss @@ -0,0 +1,83 @@ +@use "@angular/material" as mat; +@import "../../../../styles.scss"; + +.form-container { + width: 100%; +} + +.asterix--after:after { + content: "*"; + color: red; +} + +.login-form { + display: flex; + flex-direction: column; + padding: 10px; +} + +.mat-mdc-dialog-content { + max-height: 75vh; + min-width: 800px; +} + +.dialog-header { + background-color: mat.get-color-from-palette($graphene-ui-primary); + height: 39px; + color: #fff; + + h2 { + color: #fff; + font-size: 16px; + font-weight: 600; + margin: 0; + } +} + +.version { + font-size: 12px; + color: #bbb; + padding-left: 5px; +} + +.legend { + width: 100%; + max-width: 100%; + white-space: normal; + font-size: 16px !important; + font-weight: 600; + line-height: 30px !important; + border-bottom: 1px solid #e5e5e5; + margin-bottom: 8px !important; + padding: 5px 8px 5px 0 !important; + color: #671c9d !important; + text-transform: uppercase; + font-family: "Open Sans", sans-serif; +} + +.font600 { + font-weight: 600; +} + +.md-txtarea { + border: 1px solid #d5d5d5; + padding: 7px; + height: 82px; + font-size: 14px; +} + +.cancel-button { + margin-right: auto; +} + +.dialog-title { + display: flex; + align-items: center; + padding: 0; + flex: 1 1 auto; +} + +.flex-column { + display: flex; + flex-direction: column; +} diff --git a/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.spec.ts b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.spec.ts new file mode 100644 index 0000000..82aa65a --- /dev/null +++ b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UpdateModelNameDialogComponent } from './update-model-name-dialog.component'; + +describe('UpdateModelNameDialogComponent', () => { + let component: UpdateModelNameDialogComponent; + let fixture: ComponentFixture<UpdateModelNameDialogComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UpdateModelNameDialogComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(UpdateModelNameDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.ts b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.ts new file mode 100644 index 0000000..c365ec9 --- /dev/null +++ b/src/app/shared/components/update-model-name-dialog/update-model-name-dialog.component.ts @@ -0,0 +1,100 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { + FormBuilder, + FormGroup, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { + MAT_DIALOG_DATA, + MatDialogModule, + MatDialogRef, +} from '@angular/material/dialog'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { AlertService } from 'src/app/core/services/alert.service'; +import { AlertType } from '../../models'; + +@Component({ + selector: 'gp-update-model-name-dialog', + standalone: true, + imports: [ + MatDialogModule, + MatButtonModule, + MatToolbarModule, + MatIconModule, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + FormsModule, + ], + templateUrl: './update-model-name-dialog.component.html', + styleUrl: './update-model-name-dialog.component.scss', +}) +export class UpdateModelNameDialogComponent implements OnInit { + title!: string; + content!: string; + alertMessage!: string; + updateModelNameForm!: FormGroup; + message: string = ''; + + constructor( + public dialogRef: MatDialogRef<UpdateModelNameDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: any, + private alertService: AlertService, + private formBuilder: FormBuilder, + ) { + this.updateModelNameForm = this.formBuilder.group({ + modelName: ['', [Validators.required]], + modelVersion: [{ value: '', disabled: true }], + modelId: [{ value: '', disabled: true }], + }); + } + + ngOnInit(): void { + this.title = this.data.dataKey.title; + this.content = this.data.dataKey.content; + this.alertMessage = this.data.dataKey.alertMessage; + this.updateModelNameForm.patchValue({ + modelName: this.data.dataKey.modelData.name, + }); + this.updateModelNameForm.patchValue({ + modelVersion: this.data.dataKey.version, + }); + this.updateModelNameForm.patchValue({ + modelId: this.data.dataKey.modelData.solutionId, + }); + } + + update() { + if ( + this.data.dataKey.modelData.name !== + this.updateModelNameForm.controls['modelName'].value + ) + this.data.dataKey + .action(this.updateModelNameForm.controls['modelName'].value) + .subscribe({ + next: (res: any) => { + this.alertService.notify( + { message: this.alertMessage, type: AlertType.Success }, + 3000, + ); + this.dialogRef.close(true); + }, + error: (err: any) => { + this.alertService.notify( + { message: 'Operation failed', type: AlertType.Error }, + 3000, + ); + this.dialogRef.close(false); + }, + }); + else { + this.message = 'Provide new value'; + } + } +} diff --git a/src/app/shared/models/empty-models.model.ts b/src/app/shared/models/empty-models.model.ts new file mode 100644 index 0000000..5a6c293 --- /dev/null +++ b/src/app/shared/models/empty-models.model.ts @@ -0,0 +1,41 @@ +import { CatalogFilter } from './filter.model'; +import { LicenseProfileModel } from './public-solution.model'; + +/* export const emptyModelDetails = (): ModelDetails => ({ + solution: {} as PublicSolutionDetailsModel, + revision: { + version: '', + revisionId: '', + onBoarded: '', + }, + description: '', + licenseProfile: {} as LicenseProfileModel, + category: {} as CatalogFilter, + toolkitType: {} as CatalogFilter, + image: null, + tags: [], + documents: [], + authors: [], +}); + */ + +export const emptyLicenseProfileModel: LicenseProfileModel = { + $schema: '', + companyName: '', + contact: { + name: '', + URL: '', + email: '', + }, + copyright: { + year: 2021, + company: '', + suffix: '', + }, + intro: '', + keyword: '', + licenseName: '', + rtuRequired: false, + softwareType: '', + additionalInfo: '', +}; diff --git a/src/app/shared/models/index.ts b/src/app/shared/models/index.ts index 937eb62..2c969b3 100644 --- a/src/app/shared/models/index.ts +++ b/src/app/shared/models/index.ts @@ -4,3 +4,4 @@ export * from './user-details'; export * from './public-solution.model'; export * from './comment'; export * from './navigation-label.model'; +export * from './empty-models.model'; diff --git a/src/app/shared/models/public-solution.model.ts b/src/app/shared/models/public-solution.model.ts index 9ff3a6f..e11be06 100644 --- a/src/app/shared/models/public-solution.model.ts +++ b/src/app/shared/models/public-solution.model.ts @@ -38,6 +38,8 @@ export interface Solution { latestRevisionId: string; modelTypeName: string; modelType: string; + tookitType: string; + tookitTypeName: string; catalogNames: string[]; publisher: string; } @@ -155,4 +157,5 @@ export interface PublicSolution extends Solution { export interface PublicSolutionDetailsModel extends Solution { revisions: PublicSolutionDetailsRevisionModel[]; + catalogId?: string; } diff --git a/src/app/shared/models/request-payloads.ts b/src/app/shared/models/request-payloads.ts index 2dbb5df..4cfbda6 100644 --- a/src/app/shared/models/request-payloads.ts +++ b/src/app/shared/models/request-payloads.ts @@ -6,3 +6,13 @@ export interface CreateRatingRequestPayload { created?: string; modified?: string; } + +export interface UpdateSolutionRequestPayload { + active: boolean; + created: string; + modelType: string; + name: string; + ownerId: string; + solutionId: string; + tookitType: string; +} -- GitLab