Skip to content

Commit 520c80f

Browse files
Merge pull request #1696 from karinakharchenko/hosted-card-tweaks
Hosted PostgreSQL card UI tweaks and fixes
2 parents 9a89927 + 2e09517 commit 520c80f

27 files changed

Lines changed: 414 additions & 142 deletions

frontend/src/app/app.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@
124124

125125
<div *ngIf="connectionID" class="menu">
126126
<a mat-button *ngFor="let tab of visibleTabs" attr.data-testid="{{tab}}-header-link"
127-
routerLink="{{tab}}/{{connectionID}}"
127+
routerLink="{{tab === 'edit-db' && isHostedConnection ? '/hosted-databases' : tab + '/' + connectionID}}"
128128
routerLinkActive="nav-bar__button_active"
129129
class="nav-bar__button">
130130
<span *ngIf="tab !== 'edit-db' ">{{navigationTabs[tab].caption}}</span>

frontend/src/app/app.component.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ export class AppComponent {
272272
return this._connections.currentConnection?.isTestConnection || false;
273273
}
274274

275+
get isHostedConnection() {
276+
return this._connections.isHostedConnection;
277+
}
278+
275279
get visibleTabs() {
276280
return this._connections.visibleTabs;
277281
}

frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
text-align: left;
1919
transition:
2020
border-color 0.15s,
21-
background 0.15s;
21+
background 0.15s,
22+
box-shadow 0.15s;
2223
}
2324

2425
.plan-dialog__option:hover {
@@ -49,7 +50,7 @@
4950
font-weight: 700;
5051
letter-spacing: 0.04em;
5152
text-transform: uppercase;
52-
color: rgba(0, 0, 0, 0.48);
53+
color: var(--color-accentedPalette-500);
5354
}
5455

5556
@media (prefers-color-scheme: dark) {
@@ -58,15 +59,15 @@
5859
}
5960

6061
.plan-dialog__option:hover {
61-
border-color: var(--color-accentedPalette-500);
62-
background: var(--color-accentedPalette-700);
62+
border-color: color-mix(in srgb, var(--color-accentedPalette-500), transparent 65%);
63+
background: color-mix(in srgb, var(--color-accentedPalette-500), transparent 92%);
6364
}
6465

6566
.plan-dialog__option-description {
6667
color: rgba(255, 255, 255, 0.64);
6768
}
6869

6970
.plan-dialog__option-price {
70-
color: rgba(255, 255, 255, 0.48);
71+
color: var(--color-accentedPalette-400);
7172
}
7273
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.rename-dialog__content {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 16px;
5+
min-width: min(100%, 28rem);
6+
}
7+
8+
.rename-dialog__description {
9+
margin: 0;
10+
opacity: 0.65;
11+
font-size: 14px;
12+
}
13+
14+
.rename-dialog__field {
15+
width: 100%;
16+
}
17+
18+
.rename-dialog__actions {
19+
gap: 8px;
20+
padding-top: 8px;
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<h1 mat-dialog-title>Name your connection</h1>
2+
3+
<mat-dialog-content class="rename-dialog__content">
4+
<p class="rename-dialog__description">Give your hosted database a friendly name so you can find it easily.</p>
5+
<mat-form-field appearance="outline" class="rename-dialog__field">
6+
<mat-label>Connection name</mat-label>
7+
<input matInput [(ngModel)]="title" placeholder="e.g. Production DB, Staging, My App" (keyup.enter)="save()" cdkFocusInitial>
8+
</mat-form-field>
9+
</mat-dialog-content>
10+
11+
<mat-dialog-actions align="end" class="rename-dialog__actions">
12+
<button type="button" mat-button mat-dialog-close>Skip</button>
13+
<button type="button" mat-flat-button color="accent" [disabled]="!title.trim() || saving" (click)="save()">
14+
{{ saving ? 'Saving...' : 'Save' }}
15+
</button>
16+
</mat-dialog-actions>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { Component, Inject, inject } from '@angular/core';
2+
import { FormsModule } from '@angular/forms';
3+
import { MatButtonModule } from '@angular/material/button';
4+
import { MatFormFieldModule } from '@angular/material/form-field';
5+
import { MatInputModule } from '@angular/material/input';
6+
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
7+
import { firstValueFrom } from 'rxjs';
8+
import { CreatedHostedDatabase } from 'src/app/models/hosted-database';
9+
import { ConnectionsService } from 'src/app/services/connections.service';
10+
11+
export interface HostedDatabaseRenameDialogData {
12+
connectionId: string;
13+
hostedDatabase: CreatedHostedDatabase;
14+
}
15+
16+
@Component({
17+
selector: 'app-hosted-database-rename-dialog',
18+
templateUrl: './hosted-database-rename-dialog.component.html',
19+
styleUrls: ['./hosted-database-rename-dialog.component.css'],
20+
imports: [MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, FormsModule],
21+
})
22+
export class HostedDatabaseRenameDialogComponent {
23+
private _connectionsService = inject(ConnectionsService);
24+
25+
title = '';
26+
saving = false;
27+
28+
constructor(
29+
@Inject(MAT_DIALOG_DATA) public data: HostedDatabaseRenameDialogData,
30+
private _dialogRef: MatDialogRef<HostedDatabaseRenameDialogComponent>,
31+
) {
32+
this.title = data.hostedDatabase.databaseName || '';
33+
}
34+
35+
async save(): Promise<void> {
36+
const title = this.title.trim();
37+
if (!title || this.saving) return;
38+
39+
this.saving = true;
40+
try {
41+
await firstValueFrom(
42+
this._connectionsService.updateConnectionTitle(this.data.connectionId, title),
43+
);
44+
this._connectionsService.fetchConnections().subscribe();
45+
this._dialogRef.close(title);
46+
} catch {
47+
this._dialogRef.close();
48+
} finally {
49+
this.saving = false;
50+
}
51+
}
52+
}

frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,33 @@
4949
overflow-wrap: anywhere;
5050
}
5151

52-
.hosted-dialog__hint {
53-
color: rgba(0, 0, 0, 0.64);
52+
.hosted-dialog__warning {
53+
display: flex;
54+
align-items: center;
55+
gap: 10px;
56+
padding: 10px 14px;
57+
border-radius: 8px;
58+
border-left: 3px solid var(--warning-color);
59+
background: var(--warning-background-color);
5460
font-size: 13px;
61+
color: inherit;
62+
}
63+
64+
.hosted-dialog__warning-icon {
65+
flex-shrink: 0;
66+
color: var(--warning-color);
67+
font-size: 20px;
68+
width: 20px;
69+
height: 20px;
70+
}
71+
72+
.hosted-dialog__copy-check {
73+
font-size: 18px;
74+
width: 18px;
75+
height: 18px;
76+
margin-right: 4px;
77+
vertical-align: middle;
78+
line-height: 1;
5579
}
5680

5781
.hosted-dialog__actions {
@@ -62,11 +86,11 @@
6286
@media (prefers-color-scheme: dark) {
6387
.hosted-dialog__credentials {
6488
background: transparent;
65-
border-color: var(--color-accentedPalette-400);
89+
border-color: rgba(255, 255, 255, 0.12);
6690
}
6791

6892
.hosted-dialog__credentials code {
69-
background: var(--color-accentedPalette-600);
93+
background: rgba(255, 255, 255, 0.06);
7094
}
7195

7296
.hosted-dialog__hint {

frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ <h1 mat-dialog-title>
2121
</p>
2222
}
2323

24+
<div class="hosted-dialog__warning">
25+
<mat-icon class="hosted-dialog__warning-icon">warning</mat-icon>
26+
<span>Copy the connection string before closing.</span>
27+
</div>
28+
2429
<div class="hosted-dialog__credentials ph-no-capture">
2530
<div class="hosted-dialog__row">
2631
<span class="hosted-dialog__label">Database</span>
@@ -43,40 +48,26 @@ <h1 mat-dialog-title>
4348
<code>{{ data.hostedDatabase.password }}</code>
4449
</div>
4550
</div>
46-
47-
<p class="hosted-dialog__hint">
48-
The generated password cannot be recovered from this screen later.
49-
</p>
5051
</mat-dialog-content>
5152

5253
<mat-dialog-actions align="end" class="hosted-dialog__actions">
53-
<button type="button" mat-stroked-button mat-dialog-close>
54-
Close
55-
</button>
54+
@if (canClose) {
55+
<button type="button" mat-stroked-button mat-dialog-close>
56+
Close
57+
</button>
58+
}
5659
<button
5760
type="button"
5861
mat-flat-button
5962
color="accent"
63+
cdkFocusInitial
6064
[cdkCopyToClipboard]="credentialsText"
6165
(cdkCopyToClipboardCopied)="handleCredentialsCopied()">
62-
Copy credentials
66+
@if (copied) {
67+
<mat-icon class="hosted-dialog__copy-check">check</mat-icon>
68+
Credentials copied
69+
} @else {
70+
Copy connection string
71+
}
6372
</button>
64-
@if (data.connectionId) {
65-
<a
66-
mat-button
67-
[routerLink]="['/dashboard', data.connectionId]"
68-
mat-dialog-close
69-
(click)="handleSecondaryActionClick()">
70-
Open tables
71-
</a>
72-
<a
73-
mat-flat-button
74-
color="primary"
75-
[routerLink]="['/auto-configure', data.connectionId]"
76-
mat-dialog-close
77-
cdkFocusInitial
78-
(click)="handlePrimaryActionClick()">
79-
Set up dashboard
80-
</a>
81-
}
8273
</mat-dialog-actions>

frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
22
import { Component, Inject } from '@angular/core';
33
import { MatButtonModule } from '@angular/material/button';
4+
import { MatIconModule } from '@angular/material/icon';
45
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
5-
import { RouterModule } from '@angular/router';
66
import posthog from 'posthog-js';
77
import { CreatedHostedDatabase } from 'src/app/models/hosted-database';
88
import { NotificationsService } from 'src/app/services/notifications.service';
@@ -17,9 +17,12 @@ export interface HostedDatabaseSuccessDialogData {
1717
selector: 'app-hosted-database-success-dialog',
1818
templateUrl: './hosted-database-success-dialog.component.html',
1919
styleUrl: './hosted-database-success-dialog.component.css',
20-
imports: [MatDialogModule, MatButtonModule, RouterModule, CdkCopyToClipboard],
20+
imports: [MatDialogModule, MatButtonModule, MatIconModule, CdkCopyToClipboard],
2121
})
2222
export class HostedDatabaseSuccessDialogComponent {
23+
public copied = false;
24+
public canClose = false;
25+
2326
constructor(
2427
@Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData,
2528
private _notifications: NotificationsService,
@@ -33,13 +36,8 @@ export class HostedDatabaseSuccessDialogComponent {
3336
handleCredentialsCopied(): void {
3437
posthog.capture('Connections: hosted PostgreSQL credentials copied');
3538
this._notifications.showSuccessSnackbar('Hosted database credentials were copied to clipboard.');
36-
}
37-
38-
handlePrimaryActionClick(): void {
39-
posthog.capture('Connections: hosted PostgreSQL setup dashboard opened');
40-
}
41-
42-
handleSecondaryActionClick(): void {
43-
posthog.capture('Connections: hosted PostgreSQL tables opened');
39+
this.copied = true;
40+
this.canClose = true;
41+
setTimeout(() => { this.copied = false; }, 2000);
4442
}
4543
}

frontend/src/app/components/connections-list/own-connections/own-connections.component.html

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,36 @@ <h2 class="connectionInfo__connectionTitle">{{ $any(connectionItem).displayTitle
4747
@if (showHostedDatabaseEntry) {
4848
<div class="section">
4949
<span class="section__label">Hosted by Rocketadmin</span>
50-
<button
51-
type="button"
52-
class="hosted-banner"
53-
data-testid="create-hosted-database-button"
54-
[disabled]="creatingHostedDatabase()"
55-
(click)="createHostedDatabase()">
56-
<div class="hosted-banner__iconBox">
57-
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
58-
</div>
59-
<div class="hosted-banner__text">
60-
<span class="hosted-banner__title">
61-
{{ creatingHostedDatabase() ? 'Creating hosted PostgreSQL...' : 'Hosted PostgreSQL' }}
62-
</span>
63-
<span class="hosted-banner__subtitle">Provision a managed database in one click</span>
64-
</div>
65-
<span class="hosted-banner__badge">&#10022; Quickstart</span>
66-
</button>
50+
@if (hostedDbLimitReached) {
51+
<a routerLink="/upgrade" class="hosted-banner hosted-banner--upgrade">
52+
<div class="hosted-banner__iconBox">
53+
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
54+
</div>
55+
<div class="hosted-banner__text">
56+
<span class="hosted-banner__title">Upgrade to host more</span>
57+
<span class="hosted-banner__subtitle">You've reached the limit of 3 hosted databases</span>
58+
</div>
59+
<span class="hosted-banner__badge">&#10022; Upgrade</span>
60+
</a>
61+
} @else {
62+
<button
63+
type="button"
64+
class="hosted-banner"
65+
data-testid="create-hosted-database-button"
66+
[disabled]="creatingHostedDatabase()"
67+
(click)="createHostedDatabase()">
68+
<div class="hosted-banner__iconBox">
69+
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
70+
</div>
71+
<div class="hosted-banner__text">
72+
<span class="hosted-banner__title">
73+
{{ creatingHostedDatabase() ? 'Creating hosted PostgreSQL...' : 'Hosted PostgreSQL' }}
74+
</span>
75+
<span class="hosted-banner__subtitle">Provision a managed database in one click</span>
76+
</div>
77+
<span class="hosted-banner__badge">&#10022; Quickstart</span>
78+
</button>
79+
}
6780
</div>
6881
}
6982

@@ -97,17 +110,24 @@ <h2 class="connectionInfo__connectionTitle">{{ $any(connectionItem).displayTitle
97110
@if (connections?.length) {
98111
<div class="fabActions">
99112
@if (showHostedDatabaseEntry) {
100-
<button
101-
mat-stroked-button
102-
type="button"
103-
color="primary"
104-
class="fabHostedButton"
105-
data-testid="create-hosted-database-fab-button"
106-
[disabled]="creatingHostedDatabase()"
107-
(click)="createHostedDatabase()">
108-
<mat-icon>cloud_queue</mat-icon>
109-
{{ creatingHostedDatabase() ? 'Creating...' : 'Create hosted PostgreSQL' }}
110-
</button>
113+
@if (hostedDbLimitReached) {
114+
<a mat-stroked-button color="primary" class="fabHostedButton" routerLink="/upgrade">
115+
<mat-icon>cloud_queue</mat-icon>
116+
Upgrade to host more
117+
</a>
118+
} @else {
119+
<button
120+
mat-stroked-button
121+
type="button"
122+
color="primary"
123+
class="fabHostedButton"
124+
data-testid="create-hosted-database-fab-button"
125+
[disabled]="creatingHostedDatabase()"
126+
(click)="createHostedDatabase()">
127+
<mat-icon>cloud_queue</mat-icon>
128+
{{ creatingHostedDatabase() ? 'Creating...' : 'Create hosted PostgreSQL' }}
129+
</button>
130+
}
111131
}
112132
@if (currentUser?.role === 'ADMIN' || currentUser?.role === 'DB_ADMIN') {
113133
<a mat-flat-button

0 commit comments

Comments
 (0)