Skip to content
Snippets Groups Projects
Commit 1c02ba44 authored by Kawtar Laariche's avatar Kawtar Laariche
Browse files

#25: :sparkles: Implement add edit catalog

parent d38a3278
No related branches found
No related tags found
1 merge request!13Features/catalogs page
Showing
with 532 additions and 57 deletions
<mat-toolbar class="dialog-header">
<div class="dialog-title">
<h2>
{{
data.dataKey.actionType === "edit" ? "Edit catalog" : "Add new catalog"
}}
</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>
<div class="star-rating-container">
<form [formGroup]="addEditDialogForm">
<div class="flex-column">
<mat-form-field>
<mat-label>Catalog name</mat-label>
<input matInput formControlName="name" />
@if (
controls["name"].touched && controls["name"].hasError("required")
) {
<mat-error class="validation-msgs"
>Catalog name required.</mat-error
>
}
</mat-form-field>
<div class="type-access-level-container">
<mat-form-field class="full-width">
<mat-label>Select access level</mat-label>
<mat-select formControlName="accessTypeCode">
@for (accessTypeCode of accessTypeCodes; track accessTypeCode) {
<mat-option [value]="accessTypeCode.value">{{
accessTypeCode.name
}}</mat-option>
}
</mat-select>
@if (
controls["accessTypeCode"].touched &&
controls["accessTypeCode"].hasError("required")
) {
<mat-error class="validation-msgs"
>Catalog access level required.</mat-error
>
}
</mat-form-field>
<mat-form-field class="full-width">
<mat-label>Select catalog type</mat-label>
<mat-select formControlName="type">
@for (type of catalogTypes; track type) {
<mat-option [value]="type.value">{{ type.name }}</mat-option>
}
</mat-select>
@if (
controls["type"].touched && controls["type"].hasError("required")
) {
<mat-error class="validation-msgs"
>Catalog type required.</mat-error
>
}
</mat-form-field>
</div>
<mat-form-field>
<mat-label>Add description</mat-label>
<textarea matInput formControlName="description"></textarea>
@if (
controls["description"].touched &&
controls["description"].hasError("required")
) {
<mat-error class="validation-msgs"
>Catalog description required.</mat-error
>
}
</mat-form-field>
<mat-checkbox formControlName="selfPublish">
Self publish?
</mat-checkbox>
</div>
</form>
</div></mat-dialog-content
>
<mat-toolbar class="form-footer">
<button mat-dialog-close mat-raised-button class="cancel-button">
Cancel
</button>
<button
[disabled]="addEditDialogForm.invalid"
color="primary"
mat-raised-button
(click)="addEditCatalog()"
>
{{
data.dataKey.actionType === "edit" ? "Update catalog" : "Create catalog"
}}
</button>
</mat-toolbar>
@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;
}
.type-access-level-container {
display: flex;
gap: 10px;
width: 100%;
}
.full-width {
width: 100%;
}
.validation-msgs {
font-size: 11px !important;
font-weight: 600 !important;
}
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AddEditCatalogDialogComponent } from './add-edit-catalog-dialog.component';
describe('AddEditCatalogDialogComponent', () => {
let component: AddEditCatalogDialogComponent;
let fixture: ComponentFixture<AddEditCatalogDialogComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AddEditCatalogDialogComponent]
})
.compileComponents();
fixture = TestBed.createComponent(AddEditCatalogDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, Inject, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup,
FormsModule,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
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 { MatSelectChange, MatSelectModule } from '@angular/material/select';
import { MatToolbarModule } from '@angular/material/toolbar';
import { AlertService } from 'src/app/core/services/alert.service';
import {
AccessTypeCode,
AlertType,
CatalogType,
SortOption,
} from 'src/app/shared/models';
@Component({
selector: 'gp-add-edit-catalog-dialog',
standalone: true,
imports: [
MatDialogModule,
MatButtonModule,
MatToolbarModule,
MatIconModule,
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
FormsModule,
MatCheckboxModule,
MatSelectModule,
],
templateUrl: './add-edit-catalog-dialog.component.html',
styleUrl: './add-edit-catalog-dialog.component.scss',
})
export class AddEditCatalogDialogComponent implements OnInit {
title!: string;
alertMessage!: string;
addEditDialogForm!: FormGroup;
message: string = '';
catalogTypes = Object.keys(CatalogType).map((key) => ({
name: key,
value: CatalogType[key as keyof typeof CatalogType],
}));
accessTypeCodes = Object.keys(AccessTypeCode).map((key) => ({
name: key,
value: AccessTypeCode[key as keyof typeof AccessTypeCode],
}));
constructor(
public dialogRef: MatDialogRef<AddEditCatalogDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: any,
private alertService: AlertService,
private formBuilder: FormBuilder,
) {
this.addEditDialogForm = this.formBuilder.group({
name: ['', [Validators.required]],
accessTypeCode: ['', [Validators.required]],
type: ['', [Validators.required]],
description: ['', [Validators.required]],
selfPublish: [false, [Validators.required]],
});
}
ngOnInit(): void {
console.log('modelData', this.data.dataKey.modelData);
this.title = this.data.dataKey.title;
this.alertMessage = this.data.dataKey.alertMessage;
if (this.data.dataKey.actionType === 'edit') {
this.addEditDialogForm.patchValue({
name: this.data.dataKey.modelData.name,
});
this.addEditDialogForm.patchValue({
accessTypeCode: this.data.dataKey.modelData.accessTypeCode,
});
this.addEditDialogForm.patchValue({
type: this.data.dataKey.modelData.type,
});
this.addEditDialogForm.patchValue({
description: this.data.dataKey.modelData.description,
});
this.addEditDialogForm.patchValue({
selfPublish: this.data.dataKey.modelData.selfPublish,
});
console.log('form after update', this.addEditDialogForm.value);
}
}
addEditCatalog() {
if (this.addEditDialogForm.valid)
this.data.dataKey.action(this.addEditDialogForm.value).subscribe({
next: (res: any) => {
this.alertService.notify(
{
message:
this.data.dataKey.actionType === 'create'
? 'Catalog created successfully'
: 'Catalog updated successfully',
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);
},
});
}
get controls() {
return this.addEditDialogForm.controls;
}
}
......@@ -12,7 +12,9 @@
</div>
</section>
<div class="add-catalog">
<button mat-raised-button color="primary">Add new catalog</button>
<button mat-raised-button color="primary" (click)="onClickAddNewCatalog()">
Add new catalog
</button>
</div>
<div class="filter">
<span>Filter</span>
......@@ -79,7 +81,18 @@
<ng-container matColumnDef="action">
<th mat-header-cell *matHeaderCellDef>Action</th>
<td mat-cell *matCellDef="let element">
{{ element.action }}
<div class="actions-container">
<button class="custom-icon" (click)="onClickEditCatalog(element)">
<mat-icon>edit</mat-icon>
</button>
<button
[disabled]="element.solutionCount > 0"
(click)="onClickDeleteCatalog(element)"
class="custom-icon"
>
<mat-icon>delete</mat-icon>
</button>
</div>
</td>
</ng-container>
......@@ -88,19 +101,6 @@
</table>
</div>
<div class="pagination-container">
<div class="total-items-container">
@if (totalItems > 0) {
<div class="total-items-text-container">
<span class="showing-text"
>Showing -
<span class="total-items-text">
{{ calculateStartIndex() }} to {{ calculateEndIndex() }} of
{{ totalItems }} Models</span
></span
>
</div>
}
</div>
<div class="pagination-text">
<mat-paginator
[length]="totalItems"
......
@use "@angular/material" as mat;
@import "../../../../styles.scss";
.content {
padding: 24px;
}
......@@ -133,3 +136,25 @@ mat-sidenav-content {
display: flex;
justify-content: flex-end;
}
.custom-icon {
background-color: transparent !important;
border: none !important;
color: mat.get-color-from-palette($graphene-ui-primary);
cursor: pointer;
mat-icon {
font-size: 16px !important;
}
}
.actions-container {
display: flex;
}
.custom-icon[disabled] {
color: #b3b3b3;
border-color: #d3d1d1;
cursor: not-allowed;
background: #efefef;
}
......@@ -16,6 +16,11 @@ import {
} from '@angular/material/paginator';
import { Catalog } from 'src/app/shared/models';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { AddEditCatalogDialogComponent } from './add-edit-catalog-dialog/add-edit-catalog-dialog.component';
import { MatIconModule } from '@angular/material/icon';
import { ConfirmActionComponent } from 'src/app/shared/components/confirm-action/confirm-action.component';
import { DeleteUserDialogConfirmationActionComponent } from 'src/app/shared/components/delete-user-dialog-confirmation-action/delete-user-dialog-confirmation-action.component';
@Component({
selector: 'gp-catalogs',
......@@ -27,6 +32,7 @@ import { MatButtonModule } from '@angular/material/button';
MatTableModule,
MatPaginatorModule,
MatButtonModule,
MatIconModule,
],
templateUrl: './catalogs.component.html',
styleUrl: './catalogs.component.scss',
......@@ -44,16 +50,19 @@ export class CatalogsComponent implements OnInit {
'action',
];
catalogs!: Catalog[];
pageSizeOptions = [10, 50, 100];
pageSizeOptions = [10, 25, 50, 100];
pageSize = 10;
pageIndex = 0;
totalItems = 0;
siteInstanceName!: string;
selectedCatalog!: Catalog;
constructor(
private privateCatalogsService: PrivateCatalogsService,
private router: Router,
private browserStorageService: BrowserStorageService,
private paginator: MatPaginatorIntl,
public dialog: MatDialog,
) {
this.userId$ = this.browserStorageService
.getUserDetails()
......@@ -62,6 +71,7 @@ export class CatalogsComponent implements OnInit {
}
ngOnInit(): void {
this.loadData();
this.getInstanceName();
}
onHomeClick() {
......@@ -80,7 +90,6 @@ export class CatalogsComponent implements OnInit {
this.totalItems = res;
this.privateCatalogsService.loadCatalogs(0, this.totalItems).subscribe({
next: (res) => {
console.log({ res });
this.catalogs = res.response_body.content;
this.totalItems = res.response_body.totalElements;
},
......@@ -90,6 +99,20 @@ export class CatalogsComponent implements OnInit {
error: (error) => {},
});
}
getInstanceName() {
this.privateCatalogsService.getSiteConfig().subscribe({
next: (res) => {
JSON.parse(res.response_body.configValue).fields.forEach(
(field: any) => {
if (field.name === 'siteInstanceName') {
this.siteInstanceName = field.data;
}
},
);
},
error: (error) => {},
});
}
onPageChange(event: PageEvent): void {
this.pageSize = event.pageSize;
......@@ -97,12 +120,80 @@ export class CatalogsComponent implements OnInit {
this.loadData();
}
calculateStartIndex(): number {
return this.pageIndex * this.pageSize + 1;
addNewCatalog(newCatalog: Catalog) {
const catalog: Catalog = {
...newCatalog,
publisher: this.siteInstanceName,
};
return this.privateCatalogsService.createCatalog(catalog);
}
onClickAddNewCatalog() {
const dialogRef: MatDialogRef<AddEditCatalogDialogComponent> =
this.dialog.open(AddEditCatalogDialogComponent, {
data: {
dataKey: {
action: (catalog: Catalog) => this.addNewCatalog(catalog),
actionType: 'create',
},
},
});
dialogRef.afterClosed().subscribe((result) => {
this.loadData();
});
}
editCatalog(editedCatalog: Catalog) {
const catalog: Catalog = {
...editedCatalog,
publisher: this.siteInstanceName,
catalogId: this.selectedCatalog.catalogId,
};
calculateEndIndex(): number {
const endIndex = (this.pageIndex + 1) * this.pageSize;
return endIndex > this.totalItems ? this.totalItems : endIndex;
return this.privateCatalogsService.updateCatalog(catalog);
}
onClickEditCatalog(catalog: Catalog) {
this.selectedCatalog = catalog;
const dialogRef: MatDialogRef<AddEditCatalogDialogComponent> =
this.dialog.open(AddEditCatalogDialogComponent, {
data: {
dataKey: {
action: (catalog: Catalog) => this.editCatalog(catalog),
actionType: 'edit',
modelData: this.selectedCatalog,
},
},
});
dialogRef.afterClosed().subscribe((result) => {
this.loadData();
});
}
deleteCatalog(catalog: Catalog) {
return this.privateCatalogsService.deleteCatalog(catalog);
}
onClickDeleteCatalog(catalog: Catalog) {
this.selectedCatalog = catalog;
const dialogRef: MatDialogRef<DeleteUserDialogConfirmationActionComponent> =
this.dialog.open(DeleteUserDialogConfirmationActionComponent, {
data: {
dataKey: {
title: `Delete Confirmation ${catalog.name}`,
content: `Are you sure that you wish to delete' ${catalog.name}`,
alertMessage: 'Catalog deleted successfully. ',
action: () => this.deleteCatalog(catalog),
},
},
autoFocus: false,
});
dialogRef.afterClosed().subscribe((result) => {
this.loadData();
});
}
}
......@@ -116,7 +116,7 @@ export class MarketplaceComponent {
.getPublicCatalogs()
.subscribe((catalogs) => {
this.catalogIds = catalogs?.map(
(catalog: Catalog) => catalog.catalogId,
(catalog: Catalog) => catalog.catalogId ?? '',
);
const requestPayload: PublicSolutionsRequestPayload = {
......
......@@ -276,7 +276,7 @@ export class ModelDetailsComponent implements OnInit {
setSelectedCatalogIdInService(): void {
const selectedCatalogId = this.selectedDefaultCatalog.catalogId;
this.sharedDataService.selectedCatalogId = selectedCatalogId;
this.sharedDataService.selectedCatalogId = selectedCatalogId ?? '';
}
setVersionIdInService(): void {
......
......@@ -390,21 +390,15 @@ export class PublishToMarketplacePageComponent implements OnInit {
return updatedTags;
}
convertToolkitTypesToCodes(
selectedToolkitTypes: string[],
): ToolkitTypeCode[] {
return selectedToolkitTypes.map(
(toolkitTypeName) =>
toolkitTypeCodeMap[toolkitTypeName] as ToolkitTypeCode,
);
}
onChangeCatalog(event: MatSelectChange) {
this.selectedCatalog = event.value;
if (this.selectedCatalog.catalogId !== '') {
this.publicSolutionsService
.getSolutionDescription(this.revisionId, this.selectedCatalog.catalogId)
.getSolutionDescription(
this.revisionId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
this.publishToMarketPlaceForm.patchValue({
......@@ -421,7 +415,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
.getSolutionDocuments(
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -441,7 +435,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
this.privateCatalogsService
.searchPublishRequestWithCatalogIds(
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -476,7 +470,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
this.selectedCatalog.accessTypeCode,
this.userId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -567,7 +561,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
return this.privateCatalogsService.updateSolutionFiles(
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
file,
);
}
......@@ -598,7 +592,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
.getSolutionDocuments(
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -647,7 +641,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
.deleteSolutionsFiles(
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
document.documentId,
)
.subscribe({
......@@ -725,7 +719,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
htmlVal,
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -1016,7 +1010,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
.getSolutionDocuments(
this.solutionId,
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......@@ -1036,7 +1030,7 @@ export class PublishToMarketplacePageComponent implements OnInit {
this.privateCatalogsService
.searchPublishRequestWithCatalogIds(
this.revisionId,
this.selectedCatalog.catalogId,
this.selectedCatalog.catalogId ?? '',
)
.subscribe({
next: (res) => {
......
......@@ -18,6 +18,7 @@ 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';
import { MatCheckboxModule } from '@angular/material/checkbox';
@Component({
selector: 'gp-update-model-name-dialog',
......
export interface Catalog {
name: string;
accessTypeCode: string;
catalogId?: string;
created?: string;
description: string;
modified?: string;
origin?: string;
publisher?: string;
selfPublish: boolean;
url: string;
solutionCount?: number;
type: string;
}
export enum AccessTypeCode {
Public = 'PB',
Restricted = 'RS',
}
export enum CatalogType {
LeaderBoard = 'LEADERBOARD',
Standard = 'STANDARD',
}
......@@ -7,3 +7,4 @@ export * from './navigation-label.model';
export * from './empty-models.model';
export * from './document.model';
export * from './publish-request.model';
export * from './catalog.model';
......@@ -57,20 +57,6 @@ export interface Tag {
tag: string;
}
export interface Catalog {
name: string;
accessTypeCode: string;
catalogId: string;
created: string;
description: string;
modified: string;
origin: string;
publisher: string;
selfPublish: boolean;
url: string;
solutionCount: number;
}
export interface Revision {
revisionId: string;
version: string;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment