Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"amplitude-js": "^8.21.9",
"angular-password-strength-meter": "npm:@eresearchqut/angular-password-strength-meter@^13.0.7",
"angulartics2": "^14.1.0",
"chart.js": "^4.5.1",
"color-string": "^2.0.1",
"convert": "^5.12.0",
"date-fns": "^4.1.0",
Expand All @@ -50,6 +51,7 @@
"mermaid": "^11.12.1",
"monaco-editor": "0.55.1",
"ng-dynamic-component": "^10.7.0",
"ng2-charts": "^8.0.0",
"ngx-cookie-service": "^19.0.0",
"ngx-markdown": "^19.1.1",
"ngx-stripe": "^19.0.0",
Expand Down
249 changes: 211 additions & 38 deletions frontend/src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,219 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { AuthGuard } from './auth.guard';
import { NgModule } from '@angular/core';

const routes: Routes = [
{path: '', redirectTo: '/connections-list', pathMatch: 'full'},
{path: 'loader', loadComponent: () => import('./components/page-loader/page-loader.component').then(m => m.PageLoaderComponent)},
{path: 'registration', loadChildren: () => import('./routes/registration.routes').then(m => m.REGISTRATION_ROUTES)},
{path: 'login', loadComponent: () => import('./components/login/login.component').then(m => m.LoginComponent), title: 'Login | Rocketadmin'},
{path: 'forget-password', loadComponent: () => import('./components/password-request/password-request.component').then(m => m.PasswordRequestComponent), title: 'Request password | Rocketadmin'},
{path: 'external/user/password/reset/verify/:verification-token', loadChildren: () => import('./routes/password-reset.routes').then(m => m.PASSWORD_RESET_ROUTES)},
{path: 'external/user/email/verify/:verification-token', loadComponent: () => import('./components/email-verification/email-verification.component').then(m => m.EmailVerificationComponent), title: 'Email verification | Rocketadmin'},
{path: 'external/user/email/change/verify/:change-token', loadComponent: () => import('./components/email-change/email-change.component').then(m => m.EmailChangeComponent), title: 'Email updating | Rocketadmin'},
{path: 'deleted', loadComponent: () => import('./components/user-deleted-success/user-deleted-success.component').then(m => m.UserDeletedSuccessComponent), title: 'User deleted | Rocketadmin'},
{path: 'connect-db', loadComponent: () => import('./components/connect-db/connect-db.component').then(m => m.ConnectDBComponent), canActivate: [AuthGuard]},
{path: 'connections-list', loadComponent: () => import('./components/connections-list/connections-list.component').then(m => m.ConnectionsListComponent), canActivate: [AuthGuard]},
{path: 'user-settings', loadComponent: () => import('./components/user-settings/user-settings.component').then(m => m.UserSettingsComponent), canActivate: [AuthGuard]},
// company routes have to be in this specific order
{path: 'company/:company-id/verify/:verification-token', pathMatch: 'full', loadChildren: () => import('./routes/company-invitation.routes').then(m => m.COMPANY_INVITATION_ROUTES)},
{path: 'company', pathMatch: 'full', loadComponent: () => import('./components/company/company.component').then(m => m.CompanyComponent), canActivate: [AuthGuard]},
{path: 'secrets', pathMatch: 'full', loadComponent: () => import('./components/secrets/secrets.component').then(m => m.SecretsComponent), canActivate: [AuthGuard], title: 'Secrets | Rocketadmin'},
{path: 'sso/:company-id', pathMatch: 'full', loadComponent: () => import('./components/sso/sso.component').then(m => m.SsoComponent), canActivate: [AuthGuard]},
{path: 'change-password', loadChildren: () => import('./routes/password-change.routes').then(m => m.PASSWORD_CHANGE_ROUTES)},
{path: 'upgrade', loadComponent: () => import('./components/upgrade/upgrade.component').then(m => m.UpgradeComponent), canActivate: [AuthGuard], title: 'Upgrade | Rocketadmin'},
{path: 'upgrade/payment', loadComponent: () => import('./components/payment-form/payment-form.component').then(m => m.PaymentFormComponent), canActivate: [AuthGuard], title: 'Payment | Rocketadmin'},
{path: 'subscription/success', loadComponent: () => import('./components/upgrade-success/upgrade-success.component').then(m => m.UpgradeSuccessComponent), canActivate: [AuthGuard], title: 'Upgraded successfully | Rocketadmin'},
{path: 'edit-db/:connection-id', loadComponent: () => import('./components/connect-db/connect-db.component').then(m => m.ConnectDBComponent), canActivate: [AuthGuard]},
{path: 'connection-settings/:connection-id', loadComponent: () => import('./components/connection-settings/connection-settings.component').then(m => m.ConnectionSettingsComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id', loadComponent: () => import('./components/dashboard/dashboard.component').then(m => m.DashboardComponent), canActivate: [AuthGuard]},
{path: 'audit/:connection-id', loadComponent: () => import('./components/audit/audit.component').then(m => m.AuditComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id/:table-name', pathMatch: 'full', loadComponent: () => import('./components/dashboard/dashboard.component').then(m => m.DashboardComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id/:table-name/entry', pathMatch: 'full', loadComponent: () => import('./components/db-table-row-edit/db-table-row-edit.component').then(m => m.DbTableRowEditComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id/:table-name/widgets', pathMatch: 'full', loadComponent: () => import('./components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component').then(m => m.DbTableWidgetsComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id/:table-name/settings', pathMatch: 'full', loadComponent: () => import('./components/dashboard/db-table-view/db-table-settings/db-table-settings.component').then(m => m.DbTableSettingsComponent), canActivate: [AuthGuard]},
{path: 'dashboard/:connection-id/:table-name/actions', pathMatch: 'full', loadComponent: () => import('./components/dashboard/db-table-view/db-table-actions/db-table-actions.component').then(m => m.DbTableActionsComponent), canActivate: [AuthGuard]},
{path: 'permissions/:connection-id', loadComponent: () => import('./components/users/users.component').then(m => m.UsersComponent), canActivate: [AuthGuard]},
{path: 'zapier', loadComponent: () => import('./components/zapier/zapier.component').then(m => m.ZapierComponent), canActivate: [AuthGuard]},
{path: '**', loadComponent: () => import('./components/page-not-found/page-not-found.component').then(m => m.PageNotFoundComponent)},
{ path: '', redirectTo: '/connections-list', pathMatch: 'full' },
{
path: 'loader',
loadComponent: () => import('./components/page-loader/page-loader.component').then((m) => m.PageLoaderComponent),
},
{
path: 'registration',
loadChildren: () => import('./routes/registration.routes').then((m) => m.REGISTRATION_ROUTES),
},
{
path: 'login',
loadComponent: () => import('./components/login/login.component').then((m) => m.LoginComponent),
title: 'Login | Rocketadmin',
},
{
path: 'forget-password',
loadComponent: () =>
import('./components/password-request/password-request.component').then((m) => m.PasswordRequestComponent),
title: 'Request password | Rocketadmin',
},
{
path: 'external/user/password/reset/verify/:verification-token',
loadChildren: () => import('./routes/password-reset.routes').then((m) => m.PASSWORD_RESET_ROUTES),
},
{
path: 'external/user/email/verify/:verification-token',
loadComponent: () =>
import('./components/email-verification/email-verification.component').then((m) => m.EmailVerificationComponent),
title: 'Email verification | Rocketadmin',
},
{
path: 'external/user/email/change/verify/:change-token',
loadComponent: () => import('./components/email-change/email-change.component').then((m) => m.EmailChangeComponent),
title: 'Email updating | Rocketadmin',
},
{
path: 'deleted',
loadComponent: () =>
import('./components/user-deleted-success/user-deleted-success.component').then(
(m) => m.UserDeletedSuccessComponent,
),
title: 'User deleted | Rocketadmin',
},
{
path: 'connect-db',
loadComponent: () => import('./components/connect-db/connect-db.component').then((m) => m.ConnectDBComponent),
canActivate: [AuthGuard],
},
{
path: 'connections-list',
loadComponent: () =>
import('./components/connections-list/connections-list.component').then((m) => m.ConnectionsListComponent),
canActivate: [AuthGuard],
},
{
path: 'user-settings',
loadComponent: () =>
import('./components/user-settings/user-settings.component').then((m) => m.UserSettingsComponent),
canActivate: [AuthGuard],
},
// company routes have to be in this specific order
{
path: 'company/:company-id/verify/:verification-token',
pathMatch: 'full',
loadChildren: () => import('./routes/company-invitation.routes').then((m) => m.COMPANY_INVITATION_ROUTES),
},
{
path: 'company',
pathMatch: 'full',
loadComponent: () => import('./components/company/company.component').then((m) => m.CompanyComponent),
canActivate: [AuthGuard],
},
{
path: 'secrets',
pathMatch: 'full',
loadComponent: () => import('./components/secrets/secrets.component').then((m) => m.SecretsComponent),
canActivate: [AuthGuard],
title: 'Secrets | Rocketadmin',
},
{
path: 'sso/:company-id',
pathMatch: 'full',
loadComponent: () => import('./components/sso/sso.component').then((m) => m.SsoComponent),
canActivate: [AuthGuard],
},
{
path: 'change-password',
loadChildren: () => import('./routes/password-change.routes').then((m) => m.PASSWORD_CHANGE_ROUTES),
},
{
path: 'upgrade',
loadComponent: () => import('./components/upgrade/upgrade.component').then((m) => m.UpgradeComponent),
canActivate: [AuthGuard],
title: 'Upgrade | Rocketadmin',
},
{
path: 'upgrade/payment',
loadComponent: () => import('./components/payment-form/payment-form.component').then((m) => m.PaymentFormComponent),
canActivate: [AuthGuard],
title: 'Payment | Rocketadmin',
},
{
path: 'subscription/success',
loadComponent: () =>
import('./components/upgrade-success/upgrade-success.component').then((m) => m.UpgradeSuccessComponent),
canActivate: [AuthGuard],
title: 'Upgraded successfully | Rocketadmin',
},
{
path: 'edit-db/:connection-id',
loadComponent: () => import('./components/connect-db/connect-db.component').then((m) => m.ConnectDBComponent),
canActivate: [AuthGuard],
},
{
path: 'connection-settings/:connection-id',
loadComponent: () =>
import('./components/connection-settings/connection-settings.component').then(
(m) => m.ConnectionSettingsComponent,
),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id',
loadComponent: () => import('./components/dashboard/dashboard.component').then((m) => m.DashboardComponent),
canActivate: [AuthGuard],
},
{
path: 'audit/:connection-id',
loadComponent: () => import('./components/audit/audit.component').then((m) => m.AuditComponent),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id/:table-name',
pathMatch: 'full',
loadComponent: () => import('./components/dashboard/dashboard.component').then((m) => m.DashboardComponent),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id/:table-name/entry',
pathMatch: 'full',
loadComponent: () =>
import('./components/db-table-row-edit/db-table-row-edit.component').then((m) => m.DbTableRowEditComponent),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id/:table-name/widgets',
pathMatch: 'full',
loadComponent: () =>
import('./components/dashboard/db-table-view/db-table-widgets/db-table-widgets.component').then(
(m) => m.DbTableWidgetsComponent,
),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id/:table-name/settings',
pathMatch: 'full',
loadComponent: () =>
import('./components/dashboard/db-table-view/db-table-settings/db-table-settings.component').then(
(m) => m.DbTableSettingsComponent,
),
canActivate: [AuthGuard],
},
{
path: 'dashboard/:connection-id/:table-name/actions',
pathMatch: 'full',
loadComponent: () =>
import('./components/dashboard/db-table-view/db-table-actions/db-table-actions.component').then(
(m) => m.DbTableActionsComponent,
),
canActivate: [AuthGuard],
},
{
path: 'permissions/:connection-id',
loadComponent: () => import('./components/users/users.component').then((m) => m.UsersComponent),
canActivate: [AuthGuard],
},
{
path: 'zapier',
loadComponent: () => import('./components/zapier/zapier.component').then((m) => m.ZapierComponent),
canActivate: [AuthGuard],
},
{
path: 'charts/:connection-id',
loadComponent: () =>
import('./components/charts/charts-list/charts-list.component').then((m) => m.ChartsListComponent),
canActivate: [AuthGuard],
title: 'Saved Queries | Rocketadmin',
},
{
path: 'charts/:connection-id/new',
loadComponent: () =>
import('./components/charts/chart-edit/chart-edit.component').then((m) => m.ChartEditComponent),
canActivate: [AuthGuard],
title: 'Create Query | Rocketadmin',
},
{
path: 'charts/:connection-id/:query-id',
loadComponent: () =>
import('./components/charts/chart-edit/chart-edit.component').then((m) => m.ChartEditComponent),
canActivate: [AuthGuard],
title: 'Edit Query | Rocketadmin',
},
{
path: '**',
loadComponent: () =>
import('./components/page-not-found/page-not-found.component').then((m) => m.PageNotFoundComponent),
},
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule { }
export class AppRoutingModule {}
3 changes: 3 additions & 0 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,9 @@ export class AppComponent {
permissions: {
caption: 'Permissions',
},
charts: {
caption: 'Charts',
},
'connection-settings': {
caption: 'Connection settings',
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
.warning-container {
display: flex;
gap: 16px;
padding: 16px;
background-color: #fff3e0;
border-radius: 8px;
}

@media (prefers-color-scheme: dark) {
.warning-container {
background-color: rgba(255, 152, 0, 0.15);
}
}

.warning-icon {
font-size: 32px;
width: 32px;
height: 32px;
color: #ef6c00;
flex-shrink: 0;
}

@media (prefers-color-scheme: dark) {
.warning-icon {
color: #ffb74d;
}
}

.warning-content p {
margin: 0;
}

.warning-content p:first-child {
margin-bottom: 8px;
}

.warning-details {
font-size: 14px;
color: rgba(0, 0, 0, 0.54);
}

@media (prefers-color-scheme: dark) {
.warning-details {
color: rgba(255, 255, 255, 0.54);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<h2 mat-dialog-title>Delete Saved Query</h2>

<mat-dialog-content>
<div class="warning-container">
<mat-icon class="warning-icon">warning</mat-icon>
<div class="warning-content">
<p>Are you sure you want to delete the query <strong>{{data.query.name}}</strong>?</p>
<p class="warning-details">
This action cannot be undone. The query and its configuration will be permanently removed.
</p>
</div>
</div>
</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-button mat-dialog-close [disabled]="submitting">Cancel</button>
<button mat-flat-button color="warn"
(click)="onDelete()"
[disabled]="submitting"
data-testid="delete-chart-confirm-button">
{{submitting ? 'Deleting...' : 'Delete Query'}}
</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { CommonModule } from '@angular/common';
import { Component, Inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { Angulartics2 } from 'angulartics2';
import { SavedQuery } from 'src/app/models/saved-query';
import { SavedQueriesService } from 'src/app/services/saved-queries.service';

@Component({
selector: 'app-chart-delete-dialog',
templateUrl: './chart-delete-dialog.component.html',
styleUrls: ['./chart-delete-dialog.component.css'],
imports: [CommonModule, MatDialogModule, MatButtonModule, MatIconModule],
})
export class ChartDeleteDialogComponent {
public submitting = false;

constructor(
@Inject(MAT_DIALOG_DATA) public data: { query: SavedQuery; connectionId: string },
private dialogRef: MatDialogRef<ChartDeleteDialogComponent>,
private _savedQueries: SavedQueriesService,
private angulartics2: Angulartics2,
) {}

onDelete(): void {
this.submitting = true;
this._savedQueries.deleteSavedQuery(this.data.connectionId, this.data.query.id).subscribe({
next: () => {
this.angulartics2.eventTrack.next({
action: 'Charts: saved query deleted successfully',
});
this.submitting = false;
this.dialogRef.close(true);
},
error: () => {
this.submitting = false;
},
});
}
}
Loading
Loading