Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions frontend/datacat-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/datacat-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"primeng": "^18.0.2",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zod": "^3.25.28",
"zone.js": "~0.14.3"
},
"devDependencies": {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
80 changes: 43 additions & 37 deletions frontend/datacat-ui/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
import {APP_INITIALIZER, ApplicationConfig, provideZoneChangeDetection,} from '@angular/core';
import {provideRouter, withComponentInputBinding} from '@angular/router';
import {ROUTES} from '../pages/workspace.routes';
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
import {providePrimeNG} from 'primeng/config';
import {PRIMENG_CONFIG} from '../shared/primeng/primeng.config';
import {provideHttpClient, withInterceptors} from '@angular/common/http';
import {apiInterceptor} from '../shared/interceptors/api.interceptor';
import {authInterceptor} from '../shared/interceptors/auth.interceptor';
import {DialogService} from 'primeng/dynamicdialog';
import {MessageService} from 'primeng/api';
import {ApiService} from '../shared/services/datacat-generated-client';
import {ThemeSelectionService} from '../features/appearence/select-theme/select-theme.service';
import {namespaceInterceptor} from "../shared/interceptors/namespace.interceptor";
import {NamespaceService} from "../shared/services/namespace.service";
import {UserService} from "../shared/services/user.service";
import {
APP_INITIALIZER,
ApplicationConfig,
provideZoneChangeDetection,
} from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { ROUTES } from '../pages/workspace.routes';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { providePrimeNG } from 'primeng/config';
import { PRIMENG_CONFIG } from '../shared/primeng/primeng.config';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { apiInterceptor } from '../shared/interceptors/api.interceptor';
import { authInterceptor } from '../shared/interceptors/auth.interceptor';
import { DialogService } from 'primeng/dynamicdialog';
import { MessageService } from 'primeng/api';
import { ApiService } from '../shared/services/datacat-generated-client';
import { ThemeSelectionService } from '../features/appearence/select-theme/select-theme.service';
import { namespaceInterceptor } from '../shared/interceptors/namespace.interceptor';
import { NamespaceService } from '../shared/services/namespace.service';
import { UserService } from '../shared/services/user.service';

export const APP_CONFIG: ApplicationConfig = {
providers: [
provideZoneChangeDetection({eventCoalescing: true}),
provideRouter(ROUTES, withComponentInputBinding()),
provideAnimationsAsync(),
provideHttpClient(withInterceptors([apiInterceptor, namespaceInterceptor, authInterceptor])),
providePrimeNG(PRIMENG_CONFIG),
DialogService,
MessageService,
ApiService,
ThemeSelectionService,
NamespaceService,
UserService,
{
provide: APP_INITIALIZER,
useFactory: (themeService: ThemeSelectionService) => {
return () => themeService.loadSavedTheme();
},
deps: [ThemeSelectionService],
multi: true,
},
],
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(ROUTES, withComponentInputBinding()),
provideAnimationsAsync(),
provideHttpClient(
withInterceptors([apiInterceptor, namespaceInterceptor, authInterceptor]),
),
providePrimeNG(PRIMENG_CONFIG),
DialogService,
MessageService,
ApiService,
ThemeSelectionService,
NamespaceService,
UserService,
{
provide: APP_INITIALIZER,
useFactory: (themeService: ThemeSelectionService) => {
return () => themeService.loadSavedTheme();
},
deps: [ThemeSelectionService],
multi: true,
},
],
};
2 changes: 1 addition & 1 deletion frontend/datacat-ui/src/app/app.style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@

.p-panel-content-container {
flex-grow: 1;
}
}
8 changes: 6 additions & 2 deletions frontend/datacat-ui/src/entities/dashboards/data.types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export type DataPoint = {
value: number;
timestamp: string;
timestamp: Date;
};

export type DataPoints = DataPoint[];
export type TimeSeries = {
metric?: string;
labels?: { [key: string]: string };
dataPoints: DataPoint[];
};
5 changes: 5 additions & 0 deletions frontend/datacat-ui/src/entities/dashboards/etc.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export type TimeRange = {
from: Date;
to: Date;
step: string;
};
1 change: 1 addition & 0 deletions frontend/datacat-ui/src/entities/dashboards/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './dashboard.types';
export * from './panel.types';
export * from './variables.types';
export * from './mappings';
111 changes: 111 additions & 0 deletions frontend/datacat-ui/src/entities/dashboards/mappings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {
DataSourceResponse,
GetPanelResponse,
VariableResponse,
} from '../../shared/services/datacat-generated-client';
import { DataSource } from '../alerting';
import {
Layout,
Panel,
VisualizationSettings,
VisualizationType,
} from './panel.types';
import { DashboardVariable } from './variables.types';
import { z } from 'zod/v4';

const LayoutShema = z.object({
x: z.number(),
y: z.number(),
cols: z.number(),
rows: z.number(),
});

const parseLayout = (s: string): Layout => {
try {
return LayoutShema.parse(JSON.parse(s));
} catch {
return {
x: 0,
y: 0,
rows: 3,
cols: 5,
};
}
};

const parseVisualizationSettings = (s: string): VisualizationSettings => {
try {
return JSON.parse(s);
} catch {
return {};
}
};

const parseVisualizationType = (s: string): VisualizationType => {
switch (s) {
case 'LineChart':
return VisualizationType.LINE;
case 'BarChart':
return VisualizationType.BAR;
case 'PieChart':
return VisualizationType.PIE;
default:
return VisualizationType.UNKNOWN;
}
};

export const mapDataSourceResponseToDataSource = (
r: DataSourceResponse,
): DataSource => {
return {
id: r.id!,
name: r.name!,
driver: r.type! as any,
connectionUrl: r.connectionString!,
};
};

export const mapGetPanelResponeToPanel = (r: GetPanelResponse): Panel => {
return {
id: r.id!,
title: r.title!,
query: r.query!.query!,
dataSource: mapDataSourceResponseToDataSource(r.query!.dataSource!),
layout: parseLayout(r.layout!),
visualizationType: parseVisualizationType(r.typeName!),
visualizationSettings: parseVisualizationSettings(r.styleConfiguration!),
};
};

export const mapVariableResponseToDashboardVariable = (
r: VariableResponse,
): DashboardVariable => {
return {
id: r.id!,
placeholder: r.placeholder!,
value: r.value!,
};
};

export const serializeVisualizationSettings = (
vs: VisualizationSettings,
): string => {
return JSON.stringify(vs);
};

export const serializeLayout = (layout: Layout): string => {
return JSON.stringify(layout);
};

export const serializeVisualizationType = (type: VisualizationType): number => {
switch (type) {
case VisualizationType.LINE:
return 1;
case VisualizationType.BAR:
return 3;
case VisualizationType.PIE:
return 2;
default:
return 4;
}
};
21 changes: 10 additions & 11 deletions frontend/datacat-ui/src/entities/dashboards/panel.types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { DataSource } from '../alerting';

export type PanelType = {
id: number;
type: VisualizationType;
};

export enum VisualizationType {
LINE = 'line',
BAR = 'bar',
PIE = 'pie',
GAUGE = 'gauge',
TABLE = 'table',
UNKNOWN = 'unknown',
}

Expand Down Expand Up @@ -51,10 +54,6 @@ export type Panel = {
visualizationSettings?: VisualizationSettings;
};

export type LineStyle = {
lineWidth: number;
};

export const decodeLayout = (encoded: string | undefined): Layout => {
if (encoded) {
try {
Expand Down Expand Up @@ -101,9 +100,9 @@ export const encodeVisualizationType = (
case VisualizationType.LINE:
return 1;
case VisualizationType.BAR:
return 2;
case VisualizationType.PIE:
return 3;
case VisualizationType.PIE:
return 2;
default:
return 4;
}
Expand All @@ -113,11 +112,11 @@ export const decodeVisualizationType = (
type: string | undefined,
): VisualizationType => {
switch (type) {
case 'Graph':
case 'LineChart':
return VisualizationType.LINE;
case 'Table':
case 'BarChart':
return VisualizationType.BAR;
case 'Pie Chart':
case 'PieChart':
return VisualizationType.PIE;
default:
return VisualizationType.UNKNOWN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AlertStatus } from '../../../entities';
import { TooltipModule } from 'primeng/tooltip';
import { from } from 'rxjs';
import { FAKE_ALERTS_COUNTS_BY_STATUS } from '../../../shared/mock/fakes';
import { ApiService } from '../../../shared/services/datacat-generated-client';

@Component({
standalone: true,
Expand All @@ -16,18 +17,19 @@ import { FAKE_ALERTS_COUNTS_BY_STATUS } from '../../../shared/mock/fakes';
export class AlertsCountsByStatusComponent implements OnInit {
protected alertsCountsByStatus?: AlertsCountsByStatus;

constructor(private apiService: ApiService) {}

ngOnInit() {
this.loadAlertsCountsByStatus();
}

protected loadAlertsCountsByStatus() {
// TODO: add API call
from([FAKE_ALERTS_COUNTS_BY_STATUS]).subscribe({
next: (alertsCountsByStatus) => {
this.alertsCountsByStatus = alertsCountsByStatus;
},
error: () => {
// TODO
this.apiService.getApiV1AlertGetCounters().subscribe({
next: (data) => {
this.alertsCountsByStatus = new Map<AlertStatus, number>();
data.forEach((d) => {
this.alertsCountsByStatus?.set(d.status as AlertStatus, d.count || 0);
});
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
<ng-template pTemplate="emptymessage">
<tr>
<td colspan="6" [style]="{ textAlign: 'center' }">
<p-tag value="No alerts found" severity="info" />
<p-tag value="No dashboards found" severity="info" />
</td>
</tr>
</ng-template>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<p-button
icon="pi pi-trash"
[text]="true"
severity="secondary"
(onClick)="showDeletionDialog()"
/>
<p-dialog
header="Delete dashboard?"
[appendTo]="'body'"
[(visible)]="isDeletionDialogVisible"
[closable]="false"
[modal]="true"
>
<p>Are you sure you want to delete this dashboard?</p>
@if (isDeletionError) {
<p class="text-secondary error">Unable to delete dashboard</p>
}
<div class="actions-container">
<p-button
label="Cancel"
severity="secondary"
(onClick)="hideDeletionDialog()"
/>
<p-button
[loading]="isDeletionInitiated"
label="Delete"
severity="danger"
(onClick)="deleteDashboard()"
/>
</div>
</p-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.actions-container {
display: flex;
justify-content: space-between;
margin-top: var(--p-padding-md);
}

.error {
margin-top: var(--p-padding-sm);
}
Loading