From 8df6d0bf34161be7b3d412489cf2997e071b2806 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Mon, 30 Mar 2026 16:27:36 +0300 Subject: [PATCH 01/21] feat: hosted PostgreSQL card UI tweaks - Accent color for plan dialog price labels (Free / Pay as you go) - Move cdkFocusInitial to "Copy credentials" button in success dialog - Add editable connection name field in success dialog - Show "Upgrade to host more" when 3 hosted DBs reached (banner + FAB) - Merge Team/Enterprise cells in pricing table for hosted PostgreSQL - Replace password hint with warning alert style Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-plan-dialog.component.css | 7 +- ...sted-database-success-dialog.component.css | 64 +++++++++++++++- ...ted-database-success-dialog.component.html | 18 ++++- ...osted-database-success-dialog.component.ts | 31 +++++++- .../own-connections.component.html | 76 ++++++++++++------- .../own-connections.component.ts | 38 +++++++++- .../components/upgrade/upgrade.component.css | 28 +++++++ .../components/upgrade/upgrade.component.html | 27 +++---- .../components/upgrade/upgrade.component.ts | 4 +- 9 files changed, 234 insertions(+), 59 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css index d1ffbe50b..b40b2ed4b 100644 --- a/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css @@ -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 { @@ -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) { @@ -67,6 +68,6 @@ } .plan-dialog__option-price { - color: rgba(255, 255, 255, 0.48); + color: var(--color-accentedPalette-400); } } diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index 9096e2c22..584706237 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -1,3 +1,48 @@ +.hosted-dialog__name-field { + display: flex; + flex-direction: column; + gap: 4px; +} + +.hosted-dialog__name-label { + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.04em; + opacity: 0.55; +} + +.hosted-dialog__name-input { + font-size: 16px; + font-weight: 600; + padding: 8px 12px; + border: 1px solid rgba(0, 0, 0, 0.12); + border-radius: 8px; + background: transparent; + color: inherit; + outline: none; + transition: border-color 0.15s; +} + +.hosted-dialog__name-input:focus { + border-color: var(--color-accentedPalette-500); +} + +.hosted-dialog__name-input::placeholder { + color: rgba(0, 0, 0, 0.3); + font-weight: 400; +} + +@media (prefers-color-scheme: dark) { + .hosted-dialog__name-input { + border-color: rgba(255, 255, 255, 0.12); + } + + .hosted-dialog__name-input::placeholder { + color: rgba(255, 255, 255, 0.3); + } +} + .hosted-dialog__content { display: flex; flex-direction: column; @@ -49,9 +94,24 @@ 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__actions { diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 2aa57e0da..9885aa1fc 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -3,6 +3,15 @@

+
+ + +
+ @if (data.connectionId) {

Your hosted PostgreSQL database is provisioned and already connected to RocketAdmin. @@ -44,9 +53,10 @@

-

- The generated password cannot be recovered from this screen later. -

+
+ warning + The generated password cannot be recovered from this screen later. +
@@ -57,6 +67,7 @@

type="button" mat-flat-button color="accent" + cdkFocusInitial [cdkCopyToClipboard]="credentialsText" (cdkCopyToClipboardCopied)="handleCredentialsCopied()"> Copy credentials @@ -74,7 +85,6 @@

color="primary" [routerLink]="['/auto-configure', data.connectionId]" mat-dialog-close - cdkFocusInitial (click)="handlePrimaryActionClick()"> Set up dashboard diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index 3e40316b5..720ee33b8 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -1,6 +1,9 @@ import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; -import { Component, Inject } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Component, Inject, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; 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'; @@ -17,13 +20,35 @@ 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, RouterModule, CdkCopyToClipboard, FormsModule], }) export class HostedDatabaseSuccessDialogComponent { + private _http = inject(HttpClient); + public connectionTitle = ''; + private _titleSaved = false; + constructor( @Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData, private _notifications: NotificationsService, - ) {} + ) { + this.connectionTitle = data.hostedDatabase.databaseName || ''; + } + + saveTitle(): void { + const title = this.connectionTitle.trim(); + if (!title || !this.data.connectionId) return; + if (this._titleSaved && title === this._lastSavedTitle) return; + + this._http.put(`/connection/${this.data.connectionId}`, { title }).subscribe({ + next: () => { + this._titleSaved = true; + this._lastSavedTitle = title; + }, + error: () => {}, + }); + } + + private _lastSavedTitle = ''; get credentialsText(): string { const { username, password, hostname, port, databaseName } = this.data.hostedDatabase; diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.html b/frontend/src/app/components/connections-list/own-connections/own-connections.component.html index ff3d3c17e..2006b0464 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.html +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.html @@ -47,23 +47,36 @@

{{ $any(connectionItem).displayTitle @if (showHostedDatabaseEntry) {
- + @if (hostedDbLimitReached) { + +
+ +
+
+ Upgrade to host more + You've reached the limit of 3 hosted databases +
+ ✦ Upgrade +
+ } @else { + + }
} @@ -97,17 +110,24 @@

{{ $any(connectionItem).displayTitle @if (connections?.length) {
@if (showHostedDatabaseEntry) { - + @if (hostedDbLimitReached) { + + cloud_queue + Upgrade to host more + + } @else { + + } } @if (currentUser?.role === 'ADMIN' || currentUser?.role === 'DB_ADMIN') { = 3; + } + ngOnChanges(changes: SimpleChanges) { if (changes.companyId && this.companyId) { this._companyService.fetchCompanyMembers(this.companyId).subscribe((members: CompanyMember[]) => { this.hasMultipleMembers = members && members.length > 1; }); + + if (this.isSaas) { + this._hostedDatabaseService.listHostedDatabases(this.companyId).then((dbs) => { + this.hostedDbCount = dbs?.length || 0; + }); + } } if (changes.connections && this.connections && !this.connectionsListCollapsed) { @@ -115,6 +126,11 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { return; } + if (this.hostedDbLimitReached) { + this._router.navigate(['/upgrade']); + return; + } + const subscriptionLevel = this.currentUser.subscriptionLevel; const isFreePlan = !subscriptionLevel || subscriptionLevel === SubscriptionPlans.free; console.log('[HostedDB] subscriptionLevel:', subscriptionLevel, 'isFreePlan:', isFreePlan); @@ -144,11 +160,29 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { } posthog.capture('Connections: hosted PostgreSQL provisioned successfully'); - this._connectionsService.fetchConnections().subscribe(); this._notifications.showSuccessSnackbar('Hosted PostgreSQL database is ready.'); + this.hostedDbCount++; + + // Fetch connections and find the newly created one by hostname + let connectionId: string | null = null; + try { + await new Promise((resolve) => { + this._connectionsService.fetchConnections().subscribe({ + next: () => { + const match = this._connectionsService.ownConnectionsList?.find( + (c) => c.host === hostedDatabase.hostname, + ); + connectionId = match?.id || null; + resolve(); + }, + error: () => resolve(), + }); + }); + } catch {} + this._openHostedDatabaseDialog({ hostedDatabase, - connectionId: null, + connectionId, }); } catch (error) { const errorMessage = this._getErrorMessage(error); diff --git a/frontend/src/app/components/upgrade/upgrade.component.css b/frontend/src/app/components/upgrade/upgrade.component.css index af9c7bf30..6818ec139 100644 --- a/frontend/src/app/components/upgrade/upgrade.component.css +++ b/frontend/src/app/components/upgrade/upgrade.component.css @@ -336,6 +336,34 @@ margin-bottom: 20px; } +.plansTable--hosted { + width: 100%; + border-collapse: collapse; +} + +.plansTable--hosted td { + padding: 12px 16px; + font-size: 14px; + border-bottom: 1px solid rgba(0, 0, 0, 0.08); +} + +.hosted-pricing-details { + font-size: 12px; + opacity: 0.65; +} + +.hosted-pricing-note { + font-size: 12px; + opacity: 0.45; + font-style: italic; +} + +@media (prefers-color-scheme: dark) { + .plansTable--hosted td { + border-bottom-color: rgba(255, 255, 255, 0.08); + } +} + @media (prefers-color-scheme: dark) { .plansTable { --mat-table-header-headline-color: rgba(255, 255, 255, 0.64); diff --git a/frontend/src/app/components/upgrade/upgrade.component.html b/frontend/src/app/components/upgrade/upgrade.component.html index 178dda1bd..138a866d0 100644 --- a/frontend/src/app/components/upgrade/upgrade.component.html +++ b/frontend/src/app/components/upgrade/upgrade.component.html @@ -112,25 +112,22 @@

Databases

Hosted Instances

- +
- - + - - - - - + + + + + + +
- {{element[plan.key]}} - - - {{element[plan.key]}} - -
Hosted PostgreSQLTiny node (0.1 CPU, 100 MB) + Scalable node
+ $0.04/CPU/min · $0.2/GB/mo · $0.04/M IOPS +

Users

diff --git a/frontend/src/app/components/upgrade/upgrade.component.ts b/frontend/src/app/components/upgrade/upgrade.component.ts index 36ed25ee8..87e5abe21 100644 --- a/frontend/src/app/components/upgrade/upgrade.component.ts +++ b/frontend/src/app/components/upgrade/upgrade.component.ts @@ -46,8 +46,8 @@ export class UpgradeComponent implements OnInit { { title: 'Hosted PostgreSQL', free: 'Tiny node (0.1 CPU, 100 MB)', - team: 'Scalable (pay as you go)', - enterprise: 'Scalable (pay as you go)', + team: 'Scalable node — $0.04/CPU/min, $0.2/GB/mo, $0.04/M IOPS', + enterprise: 'Scalable node — $0.04/CPU/min, $0.2/GB/mo, $0.04/M IOPS', }, ]; From 03bc34dfcabaa9197e4469d38b8e0738b2e48a80 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Mon, 30 Mar 2026 16:36:13 +0300 Subject: [PATCH 02/21] fix: properly save connection title in hosted DB success dialog Fetch connection data from hostedDatabase object and send a proper PUT request with required fields to update the title without breaking the connection. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...osted-database-success-dialog.component.ts | 45 +++++++++++++------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index 720ee33b8..acffdd683 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -6,8 +6,10 @@ 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 { firstValueFrom } from 'rxjs'; import posthog from 'posthog-js'; import { CreatedHostedDatabase } from 'src/app/models/hosted-database'; +import { ConnectionsService } from 'src/app/services/connections.service'; import { NotificationsService } from 'src/app/services/notifications.service'; export interface HostedDatabaseSuccessDialogData { @@ -24,32 +26,49 @@ export interface HostedDatabaseSuccessDialogData { }) export class HostedDatabaseSuccessDialogComponent { private _http = inject(HttpClient); + private _connectionsService = inject(ConnectionsService); public connectionTitle = ''; - private _titleSaved = false; + private _saving = false; + private _lastSavedTitle = ''; constructor( @Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData, private _notifications: NotificationsService, ) { this.connectionTitle = data.hostedDatabase.databaseName || ''; + this._lastSavedTitle = this.connectionTitle; } - saveTitle(): void { + async saveTitle(): Promise { const title = this.connectionTitle.trim(); - if (!title || !this.data.connectionId) return; - if (this._titleSaved && title === this._lastSavedTitle) return; + if (!title || !this.data.connectionId || this._saving) return; + if (title === this._lastSavedTitle) return; - this._http.put(`/connection/${this.data.connectionId}`, { title }).subscribe({ - next: () => { - this._titleSaved = true; - this._lastSavedTitle = title; - }, - error: () => {}, - }); + this._saving = true; + try { + const db = this.data.hostedDatabase; + await firstValueFrom( + this._http.put(`/connection/${this.data.connectionId}`, { + title, + host: db.hostname, + port: db.port, + database: db.databaseName, + username: db.username, + type: 'postgres', + ssl: false, + ssh: false, + masterEncryption: false, + azure_encryption: false, + connectionType: 'direct', + }), + ); + this._lastSavedTitle = title; + this._connectionsService.fetchConnections().subscribe(); + } catch {} finally { + this._saving = false; + } } - private _lastSavedTitle = ''; - get credentialsText(): string { const { username, password, hostname, port, databaseName } = this.data.hostedDatabase; return `postgres://${username}:${password}@${hostname}:${port}/${databaseName}`; From b858a2e6591951638d757ed1587ddcaf7d282068 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 1 Apr 2026 17:52:10 +0300 Subject: [PATCH 03/21] fix: add Save button to connection name field and fix connectionId lookup - Add explicit Save button next to connection name input - Button disabled until title is changed, shows "Saving..." state - Fix connectionId lookup to handle nested connection object structure Co-Authored-By: Claude Opus 4.6 (1M context) --- ...sted-database-success-dialog.component.css | 12 +++++++++++ ...ted-database-success-dialog.component.html | 21 ++++++++++++++----- ...osted-database-success-dialog.component.ts | 6 +++++- .../own-connections.component.ts | 4 ++-- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index 584706237..372e74a20 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -12,7 +12,19 @@ opacity: 0.55; } +.hosted-dialog__name-row { + display: flex; + gap: 8px; + align-items: center; +} + +.hosted-dialog__name-save { + flex-shrink: 0; + height: 40px !important; +} + .hosted-dialog__name-input { + flex: 1; font-size: 16px; font-weight: 600; padding: 8px 12px; diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 9885aa1fc..8e1e3851b 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -5,11 +5,22 @@

- +
+ + +
@if (data.connectionId) { diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index acffdd683..d3012b8b6 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -28,9 +28,13 @@ export class HostedDatabaseSuccessDialogComponent { private _http = inject(HttpClient); private _connectionsService = inject(ConnectionsService); public connectionTitle = ''; - private _saving = false; + public _saving = false; private _lastSavedTitle = ''; + get titleChanged(): boolean { + return this.connectionTitle.trim() !== this._lastSavedTitle; + } + constructor( @Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData, private _notifications: NotificationsService, diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts index 8160a15f1..22290aed9 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts @@ -170,9 +170,9 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { this._connectionsService.fetchConnections().subscribe({ next: () => { const match = this._connectionsService.ownConnectionsList?.find( - (c) => c.host === hostedDatabase.hostname, + (c: any) => (c.connection?.host || c.host) === hostedDatabase.hostname, ); - connectionId = match?.id || null; + connectionId = (match as any)?.connection?.id || match?.id || null; resolve(); }, error: () => resolve(), From 06bb0cdc911b7c71782b7baf82c1bad15c732f28 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Wed, 1 Apr 2026 18:02:26 +0300 Subject: [PATCH 04/21] fix: find new connection by diffing IDs and remove extra dialog buttons - Find newly created connection by comparing IDs before/after creation instead of matching by hostname (more reliable) - Remove "Open tables" and "Set up dashboard" buttons from success dialog Co-Authored-By: Claude Opus 4.6 (1M context) --- ...osted-database-success-dialog.component.html | 17 ----------------- .../own-connections.component.ts | 17 ++++++++++++----- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 8e1e3851b..aafb4e73d 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -83,21 +83,4 @@

(cdkCopyToClipboardCopied)="handleCredentialsCopied()"> Copy credentials - @if (data.connectionId) { - - Open tables - - - Set up dashboard - - } diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts index 22290aed9..b827a0bd2 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts @@ -152,6 +152,11 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { this.creatingHostedDatabase.set(true); posthog.capture('Connections: hosted PostgreSQL creation started'); + // Remember existing connection IDs before creation + const existingIds = new Set( + (this._connectionsService.ownConnectionsList || []).map((c: any) => c.connection?.id || c.id), + ); + try { const hostedDatabase = await this._hostedDatabaseService.createHostedDatabase(companyId); @@ -163,16 +168,18 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { this._notifications.showSuccessSnackbar('Hosted PostgreSQL database is ready.'); this.hostedDbCount++; - // Fetch connections and find the newly created one by hostname + // Fetch connections and find the newly created one by diffing let connectionId: string | null = null; try { await new Promise((resolve) => { this._connectionsService.fetchConnections().subscribe({ next: () => { - const match = this._connectionsService.ownConnectionsList?.find( - (c: any) => (c.connection?.host || c.host) === hostedDatabase.hostname, - ); - connectionId = (match as any)?.connection?.id || match?.id || null; + const list = this._connectionsService.ownConnectionsList || []; + const newConn = list.find((c: any) => { + const id = c.connection?.id || c.id; + return !existingIds.has(id); + }); + connectionId = (newConn as any)?.connection?.id || newConn?.id || null; resolve(); }, error: () => resolve(), From ab8b9bddfaf89aa322e5d614681220fb09be41ef Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Thu, 2 Apr 2026 14:22:42 +0300 Subject: [PATCH 05/21] fix: improve dark theme for plan dialog hover and credentials dialog - Soft accented hover on plan cards in dark mode instead of plain white - Toned-down credentials border/code background in dark mode - Brighter field labels in credentials dialog for dark theme Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-plan-dialog.component.css | 4 ++-- .../hosted-database-success-dialog.component.css | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css index b40b2ed4b..0e46dbd4f 100644 --- a/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-plan-dialog/hosted-database-plan-dialog.component.css @@ -59,8 +59,8 @@ } .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 { diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index 372e74a20..cf478ef8b 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -53,6 +53,10 @@ .hosted-dialog__name-input::placeholder { color: rgba(255, 255, 255, 0.3); } + + .hosted-dialog__label { + color: rgba(255, 255, 255, 0.7); + } } .hosted-dialog__content { @@ -134,11 +138,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 { From b28717b7ed112fd69e8ce03344ae179bb0dc7c31 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Thu, 2 Apr 2026 18:11:13 +0300 Subject: [PATCH 06/21] fix: rename "Copy credentials" to "Copy connection string" Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index aafb4e73d..47910914b 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -81,6 +81,6 @@

cdkFocusInitial [cdkCopyToClipboard]="credentialsText" (cdkCopyToClipboardCopied)="handleCredentialsCopied()"> - Copy credentials + Copy connection string From 2f6045894aab51fd6ba12b080c698ccf2829881d Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Thu, 2 Apr 2026 18:15:37 +0300 Subject: [PATCH 07/21] fix: move password warning alert above credentials frame Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 47910914b..ad55be93f 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -41,6 +41,11 @@

} +
+ warning + The generated password cannot be recovered from this screen later. +
+
Database @@ -63,11 +68,6 @@

{{ data.hostedDatabase.password }}

- -
- warning - The generated password cannot be recovered from this screen later. -
From adcec48d77f7912b91b3da645d6f706444ade8e0 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Thu, 2 Apr 2026 18:21:44 +0300 Subject: [PATCH 08/21] feat: show checkmark and "Credentials copied" on copy button after click Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.css | 7 +++++++ .../hosted-database-success-dialog.component.html | 7 ++++++- .../hosted-database-success-dialog.component.ts | 3 +++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index cf478ef8b..1640bd01c 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -130,6 +130,13 @@ height: 20px; } +.hosted-dialog__copy-check { + font-size: 18px; + width: 18px; + height: 18px; + margin-right: 4px; +} + .hosted-dialog__actions { gap: 8px; padding-top: 8px; diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index ad55be93f..29c7485b5 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -81,6 +81,11 @@

cdkFocusInitial [cdkCopyToClipboard]="credentialsText" (cdkCopyToClipboardCopied)="handleCredentialsCopied()"> - Copy connection string + @if (copied) { + check + Credentials copied + } @else { + Copy connection string + } diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index d3012b8b6..3a7070e5d 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -78,9 +78,12 @@ export class HostedDatabaseSuccessDialogComponent { return `postgres://${username}:${password}@${hostname}:${port}/${databaseName}`; } + public copied = false; + handleCredentialsCopied(): void { posthog.capture('Connections: hosted PostgreSQL credentials copied'); this._notifications.showSuccessSnackbar('Hosted database credentials were copied to clipboard.'); + this.copied = true; } handlePrimaryActionClick(): void { From 8ff2e9b4b0b5738e5d1203a48b29578a736dde4a Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 11:00:03 +0300 Subject: [PATCH 09/21] fix: rewrite warning alert to emphasize saving credentials before closing Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 29c7485b5..4af888eb7 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -43,7 +43,7 @@

warning - The generated password cannot be recovered from this screen later. + Copy the connection string before closing — the password is shown only once and cannot be recovered later.
From 688ba004c7ab01c90434576a90b6a2bcc78782ec Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 11:02:03 +0300 Subject: [PATCH 10/21] fix: shorten warning alert text Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 4af888eb7..7c8b25633 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -43,7 +43,7 @@

warning - Copy the connection string before closing — the password is shown only once and cannot be recovered later. + Copy the connection string before closing.
From 737a8fb1a84260831970e923eab7503f42bd345f Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 12:03:23 +0300 Subject: [PATCH 11/21] fix: hide Close button until credentials are copied Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.html | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 7c8b25633..cbf8965e4 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -71,9 +71,11 @@

- + @if (copied) { + + } diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index 3a7070e5d..6bc2f8098 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -79,11 +79,14 @@ export class HostedDatabaseSuccessDialogComponent { } public copied = false; + public canClose = false; handleCredentialsCopied(): void { posthog.capture('Connections: hosted PostgreSQL credentials copied'); this._notifications.showSuccessSnackbar('Hosted database credentials were copied to clipboard.'); this.copied = true; + this.canClose = true; + setTimeout(() => { this.copied = false; }, 2000); } handlePrimaryActionClick(): void { From cfab594774d46a63b5cafd0aed7043f4717b130c Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 12:11:09 +0300 Subject: [PATCH 13/21] feat: separate rename dialog from credentials dialog - Remove connection name input from credentials success dialog - Create new HostedDatabaseRenameDialogComponent shown after credentials dialog closes - Clean up unused title-related code from success dialog Co-Authored-By: Claude Opus 4.6 (1M context) --- ...osted-database-rename-dialog.component.css | 21 ++++++ ...sted-database-rename-dialog.component.html | 16 +++++ ...hosted-database-rename-dialog.component.ts | 67 +++++++++++++++++++ ...sted-database-success-dialog.component.css | 61 ----------------- ...ted-database-success-dialog.component.html | 20 ------ ...osted-database-success-dialog.component.ts | 66 ++---------------- .../own-connections.component.ts | 23 ++++++- 7 files changed, 131 insertions(+), 143 deletions(-) create mode 100644 frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.css create mode 100644 frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.html create mode 100644 frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.css new file mode 100644 index 000000000..7a4e4cf86 --- /dev/null +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.css @@ -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; +} diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.html new file mode 100644 index 000000000..ada2b6f54 --- /dev/null +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.html @@ -0,0 +1,16 @@ +

Name your connection

+ + +

Give your hosted database a friendly name so you can find it easily.

+ + Connection name + + +
+ + + + + diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts new file mode 100644 index 000000000..5dc11007f --- /dev/null +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts @@ -0,0 +1,67 @@ +import { HttpClient } from '@angular/common/http'; +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 _http = inject(HttpClient); + private _connectionsService = inject(ConnectionsService); + + title = ''; + saving = false; + + constructor( + @Inject(MAT_DIALOG_DATA) public data: HostedDatabaseRenameDialogData, + private _dialogRef: MatDialogRef, + ) { + this.title = data.hostedDatabase.databaseName || ''; + } + + async save(): Promise { + const title = this.title.trim(); + if (!title || this.saving) return; + + this.saving = true; + try { + const db = this.data.hostedDatabase; + await firstValueFrom( + this._http.put(`/connection/${this.data.connectionId}`, { + title, + host: db.hostname, + port: db.port, + database: db.databaseName, + username: db.username, + type: 'postgres', + ssl: false, + ssh: false, + masterEncryption: false, + azure_encryption: false, + connectionType: 'direct', + }), + ); + this._connectionsService.fetchConnections().subscribe(); + this._dialogRef.close(title); + } catch { + this._dialogRef.close(); + } finally { + this.saving = false; + } + } +} diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index 1640bd01c..6b22e7251 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -1,64 +1,3 @@ -.hosted-dialog__name-field { - display: flex; - flex-direction: column; - gap: 4px; -} - -.hosted-dialog__name-label { - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.04em; - opacity: 0.55; -} - -.hosted-dialog__name-row { - display: flex; - gap: 8px; - align-items: center; -} - -.hosted-dialog__name-save { - flex-shrink: 0; - height: 40px !important; -} - -.hosted-dialog__name-input { - flex: 1; - font-size: 16px; - font-weight: 600; - padding: 8px 12px; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 8px; - background: transparent; - color: inherit; - outline: none; - transition: border-color 0.15s; -} - -.hosted-dialog__name-input:focus { - border-color: var(--color-accentedPalette-500); -} - -.hosted-dialog__name-input::placeholder { - color: rgba(0, 0, 0, 0.3); - font-weight: 400; -} - -@media (prefers-color-scheme: dark) { - .hosted-dialog__name-input { - border-color: rgba(255, 255, 255, 0.12); - } - - .hosted-dialog__name-input::placeholder { - color: rgba(255, 255, 255, 0.3); - } - - .hosted-dialog__label { - color: rgba(255, 255, 255, 0.7); - } -} - .hosted-dialog__content { display: flex; flex-direction: column; diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html index 9dcaf7c3e..45bc545a3 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.html @@ -3,26 +3,6 @@

-
- -
- - -
-
- @if (data.connectionId) {

Your hosted PostgreSQL database is provisioned and already connected to RocketAdmin. diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts index 6bc2f8098..7ff6241ea 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.ts @@ -1,15 +1,10 @@ import { CdkCopyToClipboard } from '@angular/cdk/clipboard'; -import { HttpClient } from '@angular/common/http'; -import { Component, Inject, inject } from '@angular/core'; -import { FormsModule } from '@angular/forms'; +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 { firstValueFrom } from 'rxjs'; import posthog from 'posthog-js'; import { CreatedHostedDatabase } from 'src/app/models/hosted-database'; -import { ConnectionsService } from 'src/app/services/connections.service'; import { NotificationsService } from 'src/app/services/notifications.service'; export interface HostedDatabaseSuccessDialogData { @@ -22,65 +17,22 @@ 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, MatIconModule, RouterModule, CdkCopyToClipboard, FormsModule], + imports: [MatDialogModule, MatButtonModule, MatIconModule, CdkCopyToClipboard], }) export class HostedDatabaseSuccessDialogComponent { - private _http = inject(HttpClient); - private _connectionsService = inject(ConnectionsService); - public connectionTitle = ''; - public _saving = false; - private _lastSavedTitle = ''; - - get titleChanged(): boolean { - return this.connectionTitle.trim() !== this._lastSavedTitle; - } + public copied = false; + public canClose = false; constructor( @Inject(MAT_DIALOG_DATA) public data: HostedDatabaseSuccessDialogData, private _notifications: NotificationsService, - ) { - this.connectionTitle = data.hostedDatabase.databaseName || ''; - this._lastSavedTitle = this.connectionTitle; - } - - async saveTitle(): Promise { - const title = this.connectionTitle.trim(); - if (!title || !this.data.connectionId || this._saving) return; - if (title === this._lastSavedTitle) return; - - this._saving = true; - try { - const db = this.data.hostedDatabase; - await firstValueFrom( - this._http.put(`/connection/${this.data.connectionId}`, { - title, - host: db.hostname, - port: db.port, - database: db.databaseName, - username: db.username, - type: 'postgres', - ssl: false, - ssh: false, - masterEncryption: false, - azure_encryption: false, - connectionType: 'direct', - }), - ); - this._lastSavedTitle = title; - this._connectionsService.fetchConnections().subscribe(); - } catch {} finally { - this._saving = false; - } - } + ) {} get credentialsText(): string { const { username, password, hostname, port, databaseName } = this.data.hostedDatabase; return `postgres://${username}:${password}@${hostname}:${port}/${databaseName}`; } - public copied = false; - public canClose = false; - handleCredentialsCopied(): void { posthog.capture('Connections: hosted PostgreSQL credentials copied'); this._notifications.showSuccessSnackbar('Hosted database credentials were copied to clipboard.'); @@ -88,12 +40,4 @@ export class HostedDatabaseSuccessDialogComponent { this.canClose = true; setTimeout(() => { this.copied = false; }, 2000); } - - handlePrimaryActionClick(): void { - posthog.capture('Connections: hosted PostgreSQL setup dashboard opened'); - } - - handleSecondaryActionClick(): void { - posthog.capture('Connections: hosted PostgreSQL tables opened'); - } } diff --git a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts index b827a0bd2..65cc53bf2 100644 --- a/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts +++ b/frontend/src/app/components/connections-list/own-connections/own-connections.component.ts @@ -22,6 +22,10 @@ import { HostedDatabasePlanChoice, HostedDatabasePlanDialogComponent, } from '../hosted-database-plan-dialog/hosted-database-plan-dialog.component'; +import { + HostedDatabaseRenameDialogComponent, + HostedDatabaseRenameDialogData, +} from '../hosted-database-rename-dialog/hosted-database-rename-dialog.component'; import { HostedDatabaseSuccessDialogComponent, HostedDatabaseSuccessDialogData, @@ -218,12 +222,29 @@ export class OwnConnectionsComponent implements OnInit, OnChanges { } private _openHostedDatabaseDialog(data: HostedDatabaseSuccessDialogData): void { - this._dialog.open(HostedDatabaseSuccessDialogComponent, { + const dialogRef = this._dialog.open(HostedDatabaseSuccessDialogComponent, { width: '42em', maxWidth: '95vw', data, disableClose: true, }); + + dialogRef.afterClosed().subscribe(() => { + if (data.connectionId) { + this._openRenameDialog({ + connectionId: data.connectionId, + hostedDatabase: data.hostedDatabase, + }); + } + }); + } + + private _openRenameDialog(data: HostedDatabaseRenameDialogData): void { + this._dialog.open(HostedDatabaseRenameDialogComponent, { + width: '28em', + maxWidth: '95vw', + data, + }); } private _getErrorMessage(error: unknown): string { From 1aa14761d7aa574b903af9c5ab074a822a188555 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 12:12:35 +0300 Subject: [PATCH 14/21] fix: align checkmark icon with text in copy button Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-success-dialog.component.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css index 6b22e7251..98f49958f 100644 --- a/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css +++ b/frontend/src/app/components/connections-list/hosted-database-success-dialog/hosted-database-success-dialog.component.css @@ -74,6 +74,8 @@ width: 18px; height: 18px; margin-right: 4px; + vertical-align: middle; + line-height: 1; } .hosted-dialog__actions { From cf365731403f6333638bed241f1f58244d1d0976 Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 12:22:00 +0300 Subject: [PATCH 15/21] fix: fetch full connection before renaming to preserve all fields Previously sent a partial PUT that could overwrite password, SSL, and other settings with defaults, breaking the connection. Now fetches the current connection first and only changes the title. Co-Authored-By: Claude Opus 4.6 (1M context) --- ...hosted-database-rename-dialog.component.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts index 5dc11007f..35a7bd47e 100644 --- a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts @@ -40,21 +40,15 @@ export class HostedDatabaseRenameDialogComponent { this.saving = true; try { - const db = this.data.hostedDatabase; + // Fetch the current connection to preserve all fields + const res = await firstValueFrom( + this._http.get(`/connection/one/${this.data.connectionId}`), + ); + const connection = res.connection; + connection.title = title; + await firstValueFrom( - this._http.put(`/connection/${this.data.connectionId}`, { - title, - host: db.hostname, - port: db.port, - database: db.databaseName, - username: db.username, - type: 'postgres', - ssl: false, - ssh: false, - masterEncryption: false, - azure_encryption: false, - connectionType: 'direct', - }), + this._http.put(`/connection/${this.data.connectionId}`, connection), ); this._connectionsService.fetchConnections().subscribe(); this._dialogRef.close(title); From 2da01455bf5841e2eed4d08863d5d0d14fcef17f Mon Sep 17 00:00:00 2001 From: Karina Kharchenko Date: Fri, 3 Apr 2026 12:28:18 +0300 Subject: [PATCH 16/21] fix: use safe connection properties endpoint for rename instead of PUT /connection PUT /connection/:id was overwriting credentials and breaking connections. Now uses PUT /connection/properties/:id with company_name which only updates display settings without touching connection credentials. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../hosted-database-rename-dialog.component.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts index 35a7bd47e..bda278242 100644 --- a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts @@ -40,15 +40,8 @@ export class HostedDatabaseRenameDialogComponent { this.saving = true; try { - // Fetch the current connection to preserve all fields - const res = await firstValueFrom( - this._http.get(`/connection/one/${this.data.connectionId}`), - ); - const connection = res.connection; - connection.title = title; - await firstValueFrom( - this._http.put(`/connection/${this.data.connectionId}`, connection), + this._connectionsService.updateConnectionSettings(this.data.connectionId, { company_name: title }), ); this._connectionsService.fetchConnections().subscribe(); this._dialogRef.close(title); From a41571e8422be232475b08fdc120211d56329030 Mon Sep 17 00:00:00 2001 From: Lyubov Voloshko Date: Fri, 3 Apr 2026 15:44:24 +0000 Subject: [PATCH 17/21] hosted database: add new request to rename connection --- frontend/src/app/app-routing.module.ts | 4 --- ...hosted-database-rename-dialog.component.ts | 4 +-- .../page-loader/page-loader.component.css | 0 .../page-loader/page-loader.component.html | 0 .../page-loader/page-loader.component.spec.ts | 25 ------------------- .../page-loader/page-loader.component.ts | 17 ------------- .../src/app/services/connections.service.ts | 15 +++++++++++ 7 files changed, 16 insertions(+), 49 deletions(-) delete mode 100644 frontend/src/app/components/page-loader/page-loader.component.css delete mode 100644 frontend/src/app/components/page-loader/page-loader.component.html delete mode 100644 frontend/src/app/components/page-loader/page-loader.component.spec.ts delete mode 100644 frontend/src/app/components/page-loader/page-loader.component.ts diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index c42c9d6c5..9ddafcc13 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -7,10 +7,6 @@ import { setupGuard } from './guards/setup.guard'; 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), diff --git a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts index bda278242..84e92a159 100644 --- a/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts +++ b/frontend/src/app/components/connections-list/hosted-database-rename-dialog/hosted-database-rename-dialog.component.ts @@ -1,4 +1,3 @@ -import { HttpClient } from '@angular/common/http'; import { Component, Inject, inject } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -21,7 +20,6 @@ export interface HostedDatabaseRenameDialogData { imports: [MatDialogModule, MatButtonModule, MatFormFieldModule, MatInputModule, FormsModule], }) export class HostedDatabaseRenameDialogComponent { - private _http = inject(HttpClient); private _connectionsService = inject(ConnectionsService); title = ''; @@ -41,7 +39,7 @@ export class HostedDatabaseRenameDialogComponent { this.saving = true; try { await firstValueFrom( - this._connectionsService.updateConnectionSettings(this.data.connectionId, { company_name: title }), + this._connectionsService.updateConnectionTitle(this.data.connectionId, title), ); this._connectionsService.fetchConnections().subscribe(); this._dialogRef.close(title); diff --git a/frontend/src/app/components/page-loader/page-loader.component.css b/frontend/src/app/components/page-loader/page-loader.component.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/components/page-loader/page-loader.component.html b/frontend/src/app/components/page-loader/page-loader.component.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/frontend/src/app/components/page-loader/page-loader.component.spec.ts b/frontend/src/app/components/page-loader/page-loader.component.spec.ts deleted file mode 100644 index 55cac08a1..000000000 --- a/frontend/src/app/components/page-loader/page-loader.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { provideRouter } from '@angular/router'; -import { PageLoaderComponent } from './page-loader.component'; - -describe('LoaderComponent', () => { - let component: PageLoaderComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [PageLoaderComponent], - providers: [provideRouter([])], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(PageLoaderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/frontend/src/app/components/page-loader/page-loader.component.ts b/frontend/src/app/components/page-loader/page-loader.component.ts deleted file mode 100644 index d4dd37473..000000000 --- a/frontend/src/app/components/page-loader/page-loader.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component, NgZone, OnInit } from '@angular/core'; - -import { Router } from '@angular/router'; - -@Component({ - selector: 'app-page-loader', - templateUrl: './page-loader.component.html', - styleUrls: ['./page-loader.component.css'], -}) -export class PageLoaderComponent implements OnInit { - constructor( - public router: Router, - _ngZone: NgZone, - ) {} - - ngOnInit() {} -} diff --git a/frontend/src/app/services/connections.service.ts b/frontend/src/app/services/connections.service.ts index f88872747..313ca4877 100644 --- a/frontend/src/app/services/connections.service.ts +++ b/frontend/src/app/services/connections.service.ts @@ -490,6 +490,21 @@ export class ConnectionsService { ); } + updateConnectionTitle(connectionID: string, title: string) { + return this._http.put(`/connection/title/${connectionID}`, { title }).pipe( + map((res) => { + this._notifications.showSuccessSnackbar('Connection title has been updated successfully.'); + return res; + }), + catchError((err) => { + console.log(err); + const errorMessage = err.error?.message || 'Unknown error'; + this._notifications.showErrorSnackbar(`${errorMessage}.`); + return EMPTY; + }), + ); + } + deleteConnectionSettings(connectionID: string) { return this._http.delete(`/connection/properties/${connectionID}`).pipe( map(() => { From a755621fddc0122bc71cc0e11a0a91d398217248 Mon Sep 17 00:00:00 2001 From: Lyubov Voloshko Date: Sun, 5 Apr 2026 19:24:47 +0000 Subject: [PATCH 18/21] navbar: navigate user to company/hosted databses if current connection is hosted by RA --- frontend/src/app/app.component.html | 2 +- frontend/src/app/app.component.ts | 4 +++ .../app/services/connections.service.spec.ts | 18 ++++++++++ .../src/app/services/connections.service.ts | 36 ++++++++++++++++++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index ffb82749f..a6ca79a92 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -124,7 +124,7 @@