feat: add ngx-charts for policy diagrams
MODIFY
package-lock.json
package-lock.json
"jsonc-parser": "3.0.0"
}
},
+ "@swimlane/ngx-charts": {
+ "version": "19.0.1",
+ "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-19.0.1.tgz",
+ "integrity": "sha512-r5g/JoW7J5S7K90hNhJTq9/4WzZuJe/PPd9W+Vj2E1kVV6sVJjQBi+iJQCDen1TFG2ka00Nlo988pAwQKfejBA==",
+ "requires": {
+ "@types/d3-shape": "^2.0.0",
+ "d3-array": "^2.9.1",
+ "d3-brush": "^2.1.0",
+ "d3-color": "^2.0.0",
+ "d3-format": "^2.0.0",
+ "d3-hierarchy": "^2.0.0",
+ "d3-interpolate": "^2.0.1",
+ "d3-scale": "^3.2.3",
+ "d3-selection": "^2.0.0",
+ "d3-shape": "^2.0.0",
+ "d3-time-format": "^3.0.0",
+ "d3-transition": "^2.0.0",
+ "tslib": "^2.0.0"
+ }
+ },
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
"integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==",
"dev": true
},
+ "@types/d3-path": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.1.tgz",
+ "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw=="
+ },
+ "@types/d3-shape": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.3.tgz",
+ "integrity": "sha512-HAhCel3wP93kh4/rq+7atLdybcESZ5bRHDEZUojClyZWsRuEMo3A52NGYJSh48SxfxEU6RZIVbZL2YFZ2OAlzQ==",
+ "requires": {
+ "@types/d3-path": "^2"
+ }
+ },
"@types/eslint": {
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
"integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
"dev": true
},
+ "d3-array": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
+ "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
+ "requires": {
+ "internmap": "^1.0.0"
+ }
+ },
+ "d3-brush": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-2.1.0.tgz",
+ "integrity": "sha512-cHLLAFatBATyIKqZOkk/mDHUbzne2B3ZwxkzMHvFTCZCmLaXDpZRihQSn8UNXTkGD/3lb/W2sQz0etAftmHMJQ==",
+ "requires": {
+ "d3-dispatch": "1 - 2",
+ "d3-drag": "2",
+ "d3-interpolate": "1 - 2",
+ "d3-selection": "2",
+ "d3-transition": "2"
+ }
+ },
+ "d3-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz",
+ "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ=="
+ },
+ "d3-dispatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz",
+ "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA=="
+ },
+ "d3-drag": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz",
+ "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==",
+ "requires": {
+ "d3-dispatch": "1 - 2",
+ "d3-selection": "2"
+ }
+ },
+ "d3-ease": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz",
+ "integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ=="
+ },
+ "d3-format": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz",
+ "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA=="
+ },
+ "d3-hierarchy": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-2.0.0.tgz",
+ "integrity": "sha512-SwIdqM3HxQX2214EG9GTjgmCc/mbSx4mQBn+DuEETubhOw6/U3fmnji4uCVrmzOydMHSO1nZle5gh6HB/wdOzw=="
+ },
+ "d3-interpolate": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz",
+ "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==",
+ "requires": {
+ "d3-color": "1 - 2"
+ }
+ },
+ "d3-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz",
+ "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA=="
+ },
+ "d3-scale": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz",
+ "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==",
+ "requires": {
+ "d3-array": "^2.3.0",
+ "d3-format": "1 - 2",
+ "d3-interpolate": "1.2.0 - 2",
+ "d3-time": "^2.1.1",
+ "d3-time-format": "2 - 3"
+ }
+ },
+ "d3-selection": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz",
+ "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA=="
+ },
+ "d3-shape": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz",
+ "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==",
+ "requires": {
+ "d3-path": "1 - 2"
+ }
+ },
+ "d3-time": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz",
+ "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==",
+ "requires": {
+ "d3-array": "2"
+ }
+ },
+ "d3-time-format": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz",
+ "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==",
+ "requires": {
+ "d3-time": "1 - 2"
+ }
+ },
+ "d3-timer": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz",
+ "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA=="
+ },
+ "d3-transition": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz",
+ "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==",
+ "requires": {
+ "d3-color": "1 - 2",
+ "d3-dispatch": "1 - 2",
+ "d3-ease": "1 - 2",
+ "d3-interpolate": "1 - 2",
+ "d3-timer": "1 - 2"
+ }
+ },
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"ipaddr.js": "^1.9.0"
}
},
+ "internmap": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
+ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
+ },
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",

MODIFY
package.json
package.json
"@angular/router": "~12.2.7",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
+ "@swimlane/ngx-charts": "^19.0.1",
"keycloak-angular": "^8.4.0",
"keycloak-js": "^15.0.2",
"ngx-markdown": "^12.0.1",

MODIFY
src/app/app.module.ts
src/app/app.module.ts
@@ -36,6 +36,8 @@ import { MyAppsUseApiComponent } from './components/my-apps-use-api/my-apps-use-
import { MyAppsPoliciesComponent } from './components/my-apps-policies/my-apps-policies.component';
import { MyAppsManageApiComponent } from './components/my-apps-manage-api/my-apps-manage-api.component';
import { PolicyCardComponent } from './components/policy-card/policy-card.component';
+import { NgxChartsModule } from '@swimlane/ngx-charts';
+import { GaugeChartComponent } from './components/charts/gauge-chart/gauge-chart.component';
export function initializeApp(
configService: ConfigService,
@@ -101,6 +103,7 @@ function initializeKeycloak(keycloakHelper: KeycloakHelperService) {
MyAppsPoliciesComponent,
MyAppsManageApiComponent,
PolicyCardComponent,
+ GaugeChartComponent,
],
imports: [
KeycloakAngularModule,
@@ -122,6 +125,7 @@ function initializeKeycloak(keycloakHelper: KeycloakHelperService) {
},
}),
ReactiveFormsModule,
+ NgxChartsModule,
],
providers: [
{

ADD
src/app/components/charts/gauge-chart/gauge-chart.component.html
+<div class="chart-container">
+ <h2>{{ name }}</h2>
+ <div class="chart">
+ <ngx-charts-gauge
+ class="second"
+ [results]="data"
+ [legend]="legend"
+ [max]="limit"
+ [legendPosition]="legendPosition"
+ [scheme]="colorScheme"
+ >
+ </ngx-charts-gauge>
+ </div>
+ <div class="infos">
+ <h4>Usage ({{ period }})</h4>
+ <h2>
+ {{ currentValue }} <span class="primary">/ {{ limit }}</span>
+ </h2>
+ <h3>{{ percentage }}% used</h3>
+ </div>
+</div>

ADD
src/app/components/charts/gauge-chart/gauge-chart.component.scss
+h2, h3, h4 {
+ text-align: center;
+}
+
+.infos {
+ position: relative;
+ z-index: 50;
+ top: -5rem
+}

ADD
src/app/components/charts/gauge-chart/gauge-chart.component.spec.ts
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GaugeChartComponent } from './gauge-chart.component';
+
+describe('GaugeChartComponent', () => {
+ let component: GaugeChartComponent;
+ let fixture: ComponentFixture<GaugeChartComponent>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ GaugeChartComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(GaugeChartComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});

ADD
src/app/components/charts/gauge-chart/gauge-chart.component.ts
+import { Component, Input, OnInit } from '@angular/core';
+import { IGaugeChartData } from '../../../interfaces/IGaugeChartData';
+import { LegendPosition } from '@swimlane/ngx-charts';
+
+@Component({
+ selector: 'app-gauge-chart',
+ templateUrl: './gauge-chart.component.html',
+ styleUrls: ['./gauge-chart.component.scss'],
+})
+export class GaugeChartComponent implements OnInit {
+ @Input() gaugeData?: IGaugeChartData;
+
+ data: { name: string; value: number }[] = [{ name: '', value: 0 }];
+ legend = false;
+ limit = 0;
+ currentValue = 0;
+ legendPosition = LegendPosition.Below;
+ period = '';
+ percentage = '';
+ name: string | undefined;
+ colorScheme: any | undefined;
+
+ ngOnInit(): void {
+ this.getPrimaryColor();
+ this.setupChart();
+ }
+
+ private setupChart() {
+ if (this.gaugeData) {
+ this.name = this.gaugeData.name;
+ this.data = [
+ {
+ name: 'Currently Used',
+ value: this.gaugeData.currentValue,
+ },
+ ];
+ this.currentValue = this.gaugeData.currentValue;
+ this.limit = this.gaugeData.limit;
+ this.period = this.gaugeData.period;
+ this.percentage = ((this.currentValue * 100) / this.limit).toFixed(1);
+ }
+ }
+
+ /**
+ * This method gets and sets the primary color for the charts by adding a element to the DOM
+ */
+ getPrimaryColor(): void {
+ const p = document.createElement('p');
+ p.classList.add('primary');
+ document.body.appendChild(p);
+ const color = getComputedStyle(p).color;
+ document.body.removeChild(p);
+ this.colorScheme = { domain: [color] };
+ }
+}

MODIFY
src/app/components/my-apps-policies/my-apps-policies.component.html
src/app/components/my-apps-policies/my-apps-policies.component.html
<div class="summary mb-40" *ngFor="let policy of policies">
- <div class="policies divider-right" *ngIf="policies">
+ <div class="policies mr-40" *ngIf="policies">
<app-policy-card [policy]="policy"></app-policy-card>
</div>
- <div>
- <p>Hello</p>
+ <div class="divider-left">
+ <mat-grid-list cols="2" rowHeight="1:1" class="grid">
+ <mat-grid-tile colspan="1">
+ <app-gauge-chart [gaugeData]="gaugeData"></app-gauge-chart>
+ </mat-grid-tile>
+ <mat-grid-tile colspan="1">
+ <app-gauge-chart [gaugeData]="gaugeData"></app-gauge-chart>
+ </mat-grid-tile>
+ </mat-grid-list>
</div>
</div>

MODIFY
src/app/components/my-apps-policies/my-apps-policies.component.scss
src/app/components/my-apps-policies/my-apps-policies.component.scss
display: grid;
justify-content: space-between;
align-items: center;
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 1fr 2fr;
}
.policies {
justify-content: space-around;
gap: 20px;
}
+
+h2, h3 {
+ text-align: center;
+}
+
+.grid .divider-left {
+ margin-bottom: -4rem;
+}

MODIFY
src/app/components/my-apps-policies/my-apps-policies.component.ts
src/app/components/my-apps-policies/my-apps-policies.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { IPolicy } from '../../interfaces/ICommunication';
import { IContractExt } from '../../interfaces/IContractExt';
+import { IGaugeChartData } from '../../interfaces/IGaugeChartData';
@Component({
selector: 'app-my-apps-policies',
@@ -10,6 +11,7 @@ import { IContractExt } from '../../interfaces/IContractExt';
export class MyAppsPoliciesComponent implements OnInit {
@Input() contract?: IContractExt;
policies: IPolicy[] | undefined;
+ gaugeData: IGaugeChartData | undefined;
ngOnInit(): void {
this.getAllPolicies();
@@ -55,5 +57,11 @@ export class MyAppsPoliciesComponent implements OnInit {
this.policies = this.contract.policies;
}
this.policies = [policy, policy];
+ this.gaugeData = {
+ name: 'Rate Limit Usage',
+ limit: 500,
+ period: 'Seconds',
+ currentValue: 250,
+ };
}
}

ADD
src/app/interfaces/IGaugeChartData.ts
+export interface IGaugeChartData {
+ name: string;
+ limit: number;
+ period: string;
+ currentValue: number;
+}

MODIFY
src/assets/i18n/en.json
src/assets/i18n/en.json
"SUMMARY": "Summary",
"USE_API": "Use API",
"POLICIES": "Policies",
- "MANAGE_API": "API managen",
+ "MANAGE_API": "Manage API",
"API_DOCS_TRY_NOW": "API Docs & Try it now",
"VIEW_AND_TRY_NOW": "View API Docs & Try now",
"API_KEY": "API Key",

feat: add ngx-charts for policy diagrams