Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8df6d0b
feat: hosted PostgreSQL card UI tweaks
karinakharchenko Mar 30, 2026
03bc34d
fix: properly save connection title in hosted DB success dialog
karinakharchenko Mar 30, 2026
b858a2e
fix: add Save button to connection name field and fix connectionId lo…
karinakharchenko Apr 1, 2026
06bb0cd
fix: find new connection by diffing IDs and remove extra dialog buttons
karinakharchenko Apr 1, 2026
ab8b9bd
fix: improve dark theme for plan dialog hover and credentials dialog
karinakharchenko Apr 2, 2026
b28717b
fix: rename "Copy credentials" to "Copy connection string"
karinakharchenko Apr 2, 2026
2f60458
fix: move password warning alert above credentials frame
karinakharchenko Apr 2, 2026
adcec48
feat: show checkmark and "Credentials copied" on copy button after click
karinakharchenko Apr 2, 2026
8ff2e9b
fix: rewrite warning alert to emphasize saving credentials before clo…
karinakharchenko Apr 3, 2026
688ba00
fix: shorten warning alert text
karinakharchenko Apr 3, 2026
737a8fb
fix: hide Close button until credentials are copied
karinakharchenko Apr 3, 2026
160ca11
fix: reset copy button after 2s, keep Close button visible
karinakharchenko Apr 3, 2026
cfab594
feat: separate rename dialog from credentials dialog
karinakharchenko Apr 3, 2026
1aa1476
fix: align checkmark icon with text in copy button
karinakharchenko Apr 3, 2026
cf36573
fix: fetch full connection before renaming to preserve all fields
karinakharchenko Apr 3, 2026
2da0145
fix: use safe connection properties endpoint for rename instead of PU…
karinakharchenko Apr 3, 2026
a41571e
hosted database: add new request to rename connection
lyubov-voloshko Apr 3, 2026
a755621
navbar: navigate user to company/hosted databses if current connectio…
lyubov-voloshko Apr 5, 2026
34092ff
hosted databases: add title and move data base name below
lyubov-voloshko Apr 5, 2026
369e550
hosted databases: consistent behaviour with creds dialog
lyubov-voloshko Apr 5, 2026
57f5efd
connection list: update list on RA hosted db delete
lyubov-voloshko Apr 5, 2026
2e09517
Merge branch 'main' into hosted-card-tweaks
lyubov-voloshko Apr 5, 2026
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: 1 addition & 1 deletion frontend/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

<div *ngIf="connectionID" class="menu">
<a mat-button *ngFor="let tab of visibleTabs" attr.data-testid="{{tab}}-header-link"
routerLink="{{tab}}/{{connectionID}}"
routerLink="{{tab === 'edit-db' && isHostedConnection ? '/hosted-databases' : tab + '/' + connectionID}}"
routerLinkActive="nav-bar__button_active"
class="nav-bar__button">
<span *ngIf="tab !== 'edit-db' ">{{navigationTabs[tab].caption}}</span>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ export class AppComponent {
return this._connections.currentConnection?.isTestConnection || false;
}

get isHostedConnection() {
return this._connections.isHostedConnection;
}

get visibleTabs() {
return this._connections.visibleTabs;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
text-align: left;
transition:
border-color 0.15s,
background 0.15s;
background 0.15s,
box-shadow 0.15s;
}

.plan-dialog__option:hover {
Expand Down Expand Up @@ -49,7 +50,7 @@
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: rgba(0, 0, 0, 0.48);
color: var(--color-accentedPalette-500);
}

@media (prefers-color-scheme: dark) {
Expand All @@ -58,15 +59,15 @@
}

.plan-dialog__option:hover {
border-color: var(--color-accentedPalette-500);
background: var(--color-accentedPalette-700);
border-color: color-mix(in srgb, var(--color-accentedPalette-500), transparent 65%);
background: color-mix(in srgb, var(--color-accentedPalette-500), transparent 92%);
}

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

.plan-dialog__option-price {
color: rgba(255, 255, 255, 0.48);
color: var(--color-accentedPalette-400);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.rename-dialog__content {
display: flex;
flex-direction: column;
gap: 16px;
min-width: min(100%, 28rem);
}

.rename-dialog__description {
margin: 0;
opacity: 0.65;
font-size: 14px;
}

.rename-dialog__field {
width: 100%;
}

.rename-dialog__actions {
gap: 8px;
padding-top: 8px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<h1 mat-dialog-title>Name your connection</h1>

<mat-dialog-content class="rename-dialog__content">
<p class="rename-dialog__description">Give your hosted database a friendly name so you can find it easily.</p>
<mat-form-field appearance="outline" class="rename-dialog__field">
<mat-label>Connection name</mat-label>
<input matInput [(ngModel)]="title" placeholder="e.g. Production DB, Staging, My App" (keyup.enter)="save()" cdkFocusInitial>
</mat-form-field>
</mat-dialog-content>

<mat-dialog-actions align="end" class="rename-dialog__actions">
<button type="button" mat-button mat-dialog-close>Skip</button>
<button type="button" mat-flat-button color="accent" [disabled]="!title.trim() || saving" (click)="save()">
{{ saving ? 'Saving...' : 'Save' }}
</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component, Inject, inject } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { firstValueFrom } from 'rxjs';
import { CreatedHostedDatabase } from 'src/app/models/hosted-database';
import { ConnectionsService } from 'src/app/services/connections.service';

export interface HostedDatabaseRenameDialogData {
connectionId: string;
hostedDatabase: CreatedHostedDatabase;
}

@Component({
selector: 'app-hosted-database-rename-dialog',
templateUrl: './hosted-database-rename-dialog.component.html',
styleUrls: ['./hosted-database-rename-dialog.component.css'],
imports: [MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, FormsModule],
})
export class HostedDatabaseRenameDialogComponent {
private _connectionsService = inject(ConnectionsService);

title = '';
saving = false;

constructor(
@Inject(MAT_DIALOG_DATA) public data: HostedDatabaseRenameDialogData,
private _dialogRef: MatDialogRef<HostedDatabaseRenameDialogComponent>,
) {
this.title = data.hostedDatabase.databaseName || '';
}

async save(): Promise<void> {
const title = this.title.trim();
if (!title || this.saving) return;

this.saving = true;
try {
await firstValueFrom(
this._connectionsService.updateConnectionTitle(this.data.connectionId, title),
);
this._connectionsService.fetchConnections().subscribe();
this._dialogRef.close(title);
} catch {
this._dialogRef.close();
} finally {
this.saving = false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,33 @@
overflow-wrap: anywhere;
}

.hosted-dialog__hint {
color: rgba(0, 0, 0, 0.64);
.hosted-dialog__warning {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
border-radius: 8px;
border-left: 3px solid var(--warning-color);
background: var(--warning-background-color);
font-size: 13px;
color: inherit;
}

.hosted-dialog__warning-icon {
flex-shrink: 0;
color: var(--warning-color);
font-size: 20px;
width: 20px;
height: 20px;
}

.hosted-dialog__copy-check {
font-size: 18px;
width: 18px;
height: 18px;
margin-right: 4px;
vertical-align: middle;
line-height: 1;
}

.hosted-dialog__actions {
Expand All @@ -62,11 +86,11 @@
@media (prefers-color-scheme: dark) {
.hosted-dialog__credentials {
background: transparent;
border-color: var(--color-accentedPalette-400);
border-color: rgba(255, 255, 255, 0.12);
}

.hosted-dialog__credentials code {
background: var(--color-accentedPalette-600);
background: rgba(255, 255, 255, 0.06);
}

.hosted-dialog__hint {

Copilot AI Apr 2, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The template no longer renders .hosted-dialog__hint, but the dark-mode stylesheet still targets .hosted-dialog__hint. Consider removing/updating this selector (and any other hint-related rules) now that the hint was replaced by the warning element, to avoid dead CSS.

Copilot uses AI. Check for mistakes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ <h1 mat-dialog-title>
</p>
}

<div class="hosted-dialog__warning">
<mat-icon class="hosted-dialog__warning-icon">warning</mat-icon>
<span>Copy the connection string before closing.</span>
</div>

<div class="hosted-dialog__credentials ph-no-capture">
<div class="hosted-dialog__row">
<span class="hosted-dialog__label">Database</span>
Expand All @@ -43,40 +48,26 @@ <h1 mat-dialog-title>
<code>{{ data.hostedDatabase.password }}</code>
</div>
</div>

<p class="hosted-dialog__hint">
The generated password cannot be recovered from this screen later.
</p>
</mat-dialog-content>

<mat-dialog-actions align="end" class="hosted-dialog__actions">
<button type="button" mat-stroked-button mat-dialog-close>
Close
</button>
@if (canClose) {
<button type="button" mat-stroked-button mat-dialog-close>
Close
</button>
}
<button
type="button"
mat-flat-button
color="accent"
cdkFocusInitial
[cdkCopyToClipboard]="credentialsText"
(cdkCopyToClipboardCopied)="handleCredentialsCopied()">
Copy credentials
@if (copied) {
<mat-icon class="hosted-dialog__copy-check">check</mat-icon>
Credentials copied
} @else {
Copy connection string
}
</button>
@if (data.connectionId) {
<a
mat-button
[routerLink]="['/dashboard', data.connectionId]"
mat-dialog-close
(click)="handleSecondaryActionClick()">
Open tables
</a>
<a
mat-flat-button
color="primary"
[routerLink]="['/auto-configure', data.connectionId]"
mat-dialog-close
cdkFocusInitial
(click)="handlePrimaryActionClick()">
Set up dashboard
</a>
}
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { CdkCopyToClipboard } from '@angular/cdk/clipboard';
import { Component, Inject } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
import { RouterModule } from '@angular/router';
import posthog from 'posthog-js';
import { CreatedHostedDatabase } from 'src/app/models/hosted-database';
import { NotificationsService } from 'src/app/services/notifications.service';
Expand All @@ -17,9 +17,12 @@ export interface HostedDatabaseSuccessDialogData {
selector: 'app-hosted-database-success-dialog',
templateUrl: './hosted-database-success-dialog.component.html',
styleUrl: './hosted-database-success-dialog.component.css',
imports: [MatDialogModule, MatButtonModule, RouterModule, CdkCopyToClipboard],
imports: [MatDialogModule, MatButtonModule, MatIconModule, CdkCopyToClipboard],
})
export class HostedDatabaseSuccessDialogComponent {
public copied = false;
public canClose = false;

constructor(
@Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData,
private _notifications: NotificationsService,
Expand All @@ -33,13 +36,8 @@ export class HostedDatabaseSuccessDialogComponent {
handleCredentialsCopied(): void {
posthog.capture('Connections: hosted PostgreSQL credentials copied');
this._notifications.showSuccessSnackbar('Hosted database credentials were copied to clipboard.');
}

handlePrimaryActionClick(): void {
posthog.capture('Connections: hosted PostgreSQL setup dashboard opened');
}

handleSecondaryActionClick(): void {
posthog.capture('Connections: hosted PostgreSQL tables opened');
this.copied = true;
this.canClose = true;
setTimeout(() => { this.copied = false; }, 2000);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,36 @@ <h2 class="connectionInfo__connectionTitle">{{ $any(connectionItem).displayTitle
@if (showHostedDatabaseEntry) {
<div class="section">
<span class="section__label">Hosted by Rocketadmin</span>
<button
type="button"
class="hosted-banner"
data-testid="create-hosted-database-button"
[disabled]="creatingHostedDatabase()"
(click)="createHostedDatabase()">
<div class="hosted-banner__iconBox">
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
</div>
<div class="hosted-banner__text">
<span class="hosted-banner__title">
{{ creatingHostedDatabase() ? 'Creating hosted PostgreSQL...' : 'Hosted PostgreSQL' }}
</span>
<span class="hosted-banner__subtitle">Provision a managed database in one click</span>
</div>
<span class="hosted-banner__badge">&#10022; Quickstart</span>
</button>
@if (hostedDbLimitReached) {
<a routerLink="/upgrade" class="hosted-banner hosted-banner--upgrade">
<div class="hosted-banner__iconBox">
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
</div>
<div class="hosted-banner__text">
<span class="hosted-banner__title">Upgrade to host more</span>
<span class="hosted-banner__subtitle">You've reached the limit of 3 hosted databases</span>
</div>
<span class="hosted-banner__badge">&#10022; Upgrade</span>
</a>
} @else {
<button
type="button"
class="hosted-banner"
data-testid="create-hosted-database-button"
[disabled]="creatingHostedDatabase()"
(click)="createHostedDatabase()">
<div class="hosted-banner__iconBox">
<mat-icon svgIcon="ai_rocket" class="hosted-banner__icon"></mat-icon>
</div>
<div class="hosted-banner__text">
<span class="hosted-banner__title">
{{ creatingHostedDatabase() ? 'Creating hosted PostgreSQL...' : 'Hosted PostgreSQL' }}
</span>
<span class="hosted-banner__subtitle">Provision a managed database in one click</span>
</div>
<span class="hosted-banner__badge">&#10022; Quickstart</span>
</button>
}
</div>
}

Expand Down Expand Up @@ -97,17 +110,24 @@ <h2 class="connectionInfo__connectionTitle">{{ $any(connectionItem).displayTitle
@if (connections?.length) {
<div class="fabActions">
@if (showHostedDatabaseEntry) {
<button
mat-stroked-button
type="button"
color="primary"
class="fabHostedButton"
data-testid="create-hosted-database-fab-button"
[disabled]="creatingHostedDatabase()"
(click)="createHostedDatabase()">
<mat-icon>cloud_queue</mat-icon>
{{ creatingHostedDatabase() ? 'Creating...' : 'Create hosted PostgreSQL' }}
</button>
@if (hostedDbLimitReached) {
<a mat-stroked-button color="primary" class="fabHostedButton" routerLink="/upgrade">
<mat-icon>cloud_queue</mat-icon>
Upgrade to host more
</a>
} @else {
<button
mat-stroked-button
type="button"
color="primary"
class="fabHostedButton"
data-testid="create-hosted-database-fab-button"
[disabled]="creatingHostedDatabase()"
(click)="createHostedDatabase()">
<mat-icon>cloud_queue</mat-icon>
{{ creatingHostedDatabase() ? 'Creating...' : 'Create hosted PostgreSQL' }}
</button>
}
}
@if (currentUser?.role === 'ADMIN' || currentUser?.role === 'DB_ADMIN') {
<a mat-flat-button
Expand Down
Loading
Loading