diff --git a/src/app/api-keys/page.tsx b/src/app/api-keys/page.tsx index 2593f3f..6f6221f 100644 --- a/src/app/api-keys/page.tsx +++ b/src/app/api-keys/page.tsx @@ -22,6 +22,7 @@ import { RefreshCw, Shield, Trash2, + X, } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; @@ -855,6 +856,19 @@ export default function ApiKeysPage() { {showCreate && (
+
event.stopPropagation()} + > +
event.stopPropagation()} >
@@ -895,18 +908,6 @@ export default function ApiKeysPage() { : "Create a new API key for scripts, bots, and CI/CD jobs."}

-
{generatedKey ? ( @@ -1195,6 +1196,7 @@ export default function ApiKeysPage() {
)} +
)} diff --git a/src/app/apps/installed/page.tsx b/src/app/apps/installed/page.tsx index b2c6380..0bb5145 100644 --- a/src/app/apps/installed/page.tsx +++ b/src/app/apps/installed/page.tsx @@ -483,7 +483,16 @@ function InstallOperationsModal({ padding: 16, }} > -
+ +
@@ -527,19 +536,6 @@ function InstallOperationsModal({ {install.containerName ? ` • ${install.containerName}` : ""}

-
- -
)}
+ ); diff --git a/src/app/apps/page.tsx b/src/app/apps/page.tsx index 758b69b..2a4a9e2 100644 --- a/src/app/apps/page.tsx +++ b/src/app/apps/page.tsx @@ -1003,59 +1003,59 @@ function InstallModal({ return createPortal(
+
-
- -
-

- {mode === "custom" - ? "Custom App Install" - : `Install ${template?.name ?? "App"}`} -

-

- {mode === "custom" - ? "Define your own Docker image, ports, env vars, volumes, and runtime options." - : "Start from the template, then override ports, env vars, paths, or command as needed."} -

-
-
- -
+
+ +
+

+ {mode === "custom" + ? "Custom App Install" + : `Install ${template?.name ?? "App"}`} +

+

+ {mode === "custom" + ? "Define your own Docker image, ports, env vars, volumes, and runtime options." + : "Start from the template, then override ports, env vars, paths, or command as needed."} +

+
+
+
+
, document.body, diff --git a/src/app/backups/page.tsx b/src/app/backups/page.tsx index 7a67cbe..489e9ce 100644 --- a/src/app/backups/page.tsx +++ b/src/app/backups/page.tsx @@ -259,6 +259,15 @@ function CreateBackupModal({ return (
+
+
@@ -287,17 +296,6 @@ function CreateBackupModal({ Backup a server's data to local or S3

-
{error && ( @@ -626,6 +624,7 @@ function CreateBackupModal({
+
); @@ -919,6 +918,15 @@ export default function BackupsPage() { /> {downloadDialog && (
+
+
@@ -952,17 +960,6 @@ export default function BackupsPage() { Pilih format download untuk {downloadDialog.name}

-
+
)} diff --git a/src/app/containers/components/modals/ContainerDetailsModal.tsx b/src/app/containers/components/modals/ContainerDetailsModal.tsx index fe9b31c..e4925ea 100644 --- a/src/app/containers/components/modals/ContainerDetailsModal.tsx +++ b/src/app/containers/components/modals/ContainerDetailsModal.tsx @@ -58,6 +58,15 @@ export default function ContainerDetailsModal({ return (
+
+
@@ -103,18 +112,6 @@ export default function ContainerDetailsModal({ : ""}

-
) : null}
+
); diff --git a/src/app/containers/components/modals/ContainerFileManagerModal.tsx b/src/app/containers/components/modals/ContainerFileManagerModal.tsx index 9f35ebf..5db8a09 100644 --- a/src/app/containers/components/modals/ContainerFileManagerModal.tsx +++ b/src/app/containers/components/modals/ContainerFileManagerModal.tsx @@ -491,6 +491,18 @@ export default function ContainerFileManagerModal({ current?.onConfirm(); }} /> +
+
-
+ ); diff --git a/src/app/containers/components/modals/ContainerLogsModal.tsx b/src/app/containers/components/modals/ContainerLogsModal.tsx index 8ebd096..757df3d 100644 --- a/src/app/containers/components/modals/ContainerLogsModal.tsx +++ b/src/app/containers/components/modals/ContainerLogsModal.tsx @@ -48,6 +48,15 @@ export default function ContainerLogsModal({ return (
+
+
@@ -80,18 +89,6 @@ export default function ContainerLogsModal({ Logs - {container.name}
-
+
); diff --git a/src/app/containers/components/modals/DeployContainerModal.tsx b/src/app/containers/components/modals/DeployContainerModal.tsx index 0719e11..bc35e78 100644 --- a/src/app/containers/components/modals/DeployContainerModal.tsx +++ b/src/app/containers/components/modals/DeployContainerModal.tsx @@ -989,9 +989,7 @@ export default function DeployContainerModal({ }, }); - void containersApi - .cancelJob(jobId) - .catch(() => undefined); + void containersApi.cancelJob(jobId).catch(() => undefined); }, }, }); @@ -1079,6 +1077,15 @@ export default function DeployContainerModal({ return (
+
+
@@ -1140,27 +1147,6 @@ export default function DeployContainerModal({ : "Select a source to deploy from. You can choose from pre-built app templates, connect your Git provider, or manually enter deployment details."}

-
{error ? (
)} +
); diff --git a/src/app/databases/components/AddDatabaseModal.tsx b/src/app/databases/components/AddDatabaseModal.tsx index fa16cd1..930dd19 100644 --- a/src/app/databases/components/AddDatabaseModal.tsx +++ b/src/app/databases/components/AddDatabaseModal.tsx @@ -467,16 +467,25 @@ export default function AddDatabaseModal({ return (
-
+
+ +
@@ -511,20 +520,9 @@ export default function AddDatabaseModal({ > {choice === null ? "Select a source to deploy from" - : "Deploy a new database container on your server"} + : "Deploy a new database container on your server"}

-
{error && ( @@ -1052,6 +1050,7 @@ export default function AddDatabaseModal({
)} +
); diff --git a/src/app/domains/components/AddDomainModal.tsx b/src/app/domains/components/AddDomainModal.tsx index e11de23..0430502 100644 --- a/src/app/domains/components/AddDomainModal.tsx +++ b/src/app/domains/components/AddDomainModal.tsx @@ -478,6 +478,15 @@ export default function AddDomainModal({ return (
+
+
@@ -512,17 +521,6 @@ export default function AddDomainModal({ : "Configure domain with optional auto-SSL"}

-
{error && ( @@ -744,7 +742,7 @@ export default function AddDomainModal({ }} > Isolated config means the Nginx config for this domain will be - created separately and won't be shared with any other domain. + created separately and won't be shared with any other domain. This allows you to reuse target containers and ports that are already in use by other domains, as long as those domains are not using Shared mode on the same server. @@ -803,6 +801,7 @@ export default function AddDomainModal({
+
); diff --git a/src/app/firewall/page.tsx b/src/app/firewall/page.tsx index c916e14..2b7a521 100644 --- a/src/app/firewall/page.tsx +++ b/src/app/firewall/page.tsx @@ -16,6 +16,7 @@ import { ToggleRight, Loader2, Server as ServerIcon, + X, } from "lucide-react"; import { security as securityApi, @@ -1043,16 +1044,28 @@ export default function FirewallPage() { {showAddRule && (
setShowAddRule(false)}>
event.stopPropagation()} + > + +
@@ -1069,12 +1082,6 @@ export default function FirewallPage() { Configure a new UFW rule for this server.

-
+
)} diff --git a/src/app/git/components/GitProvidersPanel.tsx b/src/app/git/components/GitProvidersPanel.tsx index 56fd007..5ce52da 100644 --- a/src/app/git/components/GitProvidersPanel.tsx +++ b/src/app/git/components/GitProvidersPanel.tsx @@ -492,8 +492,21 @@ export default function GitProvidersPanel({ const renderProviderSelectorModal = () => (
event.stopPropagation()} + > + +
@@ -531,30 +544,10 @@ export default function GitProvidersPanel({ and repository access.

-
{renderProviderSelectionRow()} +
); @@ -1221,8 +1214,27 @@ export default function GitProvidersPanel({ {draft ? (
event.stopPropagation()} + > + +
@@ -1265,24 +1277,6 @@ export default function GitProvidersPanel({ flow.

-
@@ -1341,6 +1335,7 @@ export default function GitProvidersPanel({
)} +
) : null} diff --git a/src/app/globals.css b/src/app/globals.css index 302d6ff..3c5153a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -822,6 +822,7 @@ body { contain: layout paint style; } .modal { + position: relative; background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; @@ -838,6 +839,73 @@ body { contain: content; } +.modal-shell { + position: relative; + width: 100%; + max-width: 480px; + --modal-close-size: 28px; +} + +.modal-close { + position: absolute; + top: 5px; + right: 5px; + z-index: 5; + display: inline-flex; + width: var(--modal-close-size); + height: var(--modal-close-size); + min-width: var(--modal-close-size); + flex: 0 0 var(--modal-close-size); + align-items: center; + justify-content: center; + padding: 0; + margin: 0; + border: 2px solid rgba(255, 255, 255, 0.82) !important; + border-radius: 50%; + background: #2c2c2c !important; + color: #ffffff !important; + /* box-shadow: + 0 14px 30px rgba(220, 38, 38, 0.34), + 0 4px 14px rgba(2, 6, 23, 0.26); */ + cursor: pointer; + line-height: 1; + transition: + background 0.18s ease, + box-shadow 0.18s ease, + transform 0.18s ease; + transform: translate(50%, -50%); + transition: all 0.4s ease; +} + +.modal-close:hover { + background: #b91c1c !important; + /* box-shadow: + 0 16px 36px rgba(220, 38, 38, 0.42), + 0 6px 18px rgba(2, 6, 23, 0.28); */ + transform: translate(50%, calc(-50% - 1px)) rotate(180deg); +} + +.modal-close:disabled { + cursor: not-allowed; + opacity: 0.55; + transform: translate(50%, -50%); +} + +.modal-close:focus-visible { + outline: 3px solid rgba(239, 68, 68, 0.3); + outline-offset: 3px; +} + +.modal-close svg { + stroke-width: 2.25; +} + +@media (max-width: 640px) { + .modal-close { + --modal-close-size: 28px; + } +} + .modal::-webkit-scrollbar { width: 12px; } diff --git a/src/app/networks/page.tsx b/src/app/networks/page.tsx index 7b57165..955562c 100644 --- a/src/app/networks/page.tsx +++ b/src/app/networks/page.tsx @@ -111,6 +111,15 @@ function AddNetworkModal({ return (
+
+
@@ -140,17 +149,6 @@ function AddNetworkModal({ database.

-
{error && ( @@ -356,6 +354,7 @@ function AddNetworkModal({
+
); @@ -439,7 +438,16 @@ function NetworkDetailsModal({ padding: 16, }} > -
+ +
@@ -495,17 +503,6 @@ function NetworkDetailsModal({ : "Unknown server"}

-
) : null}
+ ); diff --git a/src/app/notifications/components/NotificationsSettingsPanel.tsx b/src/app/notifications/components/NotificationsSettingsPanel.tsx index 1fe30e1..8851835 100644 --- a/src/app/notifications/components/NotificationsSettingsPanel.tsx +++ b/src/app/notifications/components/NotificationsSettingsPanel.tsx @@ -596,6 +596,19 @@ export default function NotificationsSettingsPanel({ {draft ? (
+
+
@@ -637,24 +650,6 @@ export default function NotificationsSettingsPanel({ : "Set up a new notification provider and configure which events will trigger notifications."}

-
@@ -1424,6 +1419,7 @@ export default function NotificationsSettingsPanel({
+
) : null} diff --git a/src/app/projects/[projectId]/environments/[environmentId]/components/import/ImportFromSyncModal.tsx b/src/app/projects/[projectId]/environments/[environmentId]/components/import/ImportFromSyncModal.tsx index ddb599c..e2c7ff9 100644 --- a/src/app/projects/[projectId]/environments/[environmentId]/components/import/ImportFromSyncModal.tsx +++ b/src/app/projects/[projectId]/environments/[environmentId]/components/import/ImportFromSyncModal.tsx @@ -126,7 +126,11 @@ export default function ImportFromSyncModal({ ); setServerContainers((current) => current.map((item) => - item.id === container.id ? { ...item, environmentId } : item, + item.id === container.id + ? { ...item, environmentId } + : item.environmentId === environmentId + ? item + : item, ), ); } catch (assignError) { @@ -143,17 +147,29 @@ export default function ImportFromSyncModal({ return (
event.stopPropagation()} + > + +
@@ -178,18 +194,6 @@ export default function ImportFromSyncModal({ {environmentName}.

-
- Services / Containers + + Services / Containers +
) : null} +
); diff --git a/src/app/projects/components/EnvironmentFormModal.tsx b/src/app/projects/components/EnvironmentFormModal.tsx index 4c6f0dd..740ac74 100644 --- a/src/app/projects/components/EnvironmentFormModal.tsx +++ b/src/app/projects/components/EnvironmentFormModal.tsx @@ -61,9 +61,22 @@ export default function EnvironmentFormModal({ return (
+
event.stopPropagation()} + > +
event.stopPropagation()} onSubmit={handleSubmit} style={{ width: "100%", @@ -75,9 +88,9 @@ export default function EnvironmentFormModal({ style={{ display: "flex", alignItems: "flex-start", - justifyContent: "space-between", gap: 12, marginBottom: 20, + paddingRight: 36, }} >
@@ -96,19 +109,6 @@ export default function EnvironmentFormModal({ Tambahkan environment baru ke project {projectName}.

-
@@ -250,6 +250,7 @@ export default function EnvironmentFormModal({
+
); } diff --git a/src/app/projects/components/EnvironmentOpsModal.tsx b/src/app/projects/components/EnvironmentOpsModal.tsx index b9e3805..b04e273 100644 --- a/src/app/projects/components/EnvironmentOpsModal.tsx +++ b/src/app/projects/components/EnvironmentOpsModal.tsx @@ -79,9 +79,22 @@ export default function EnvironmentOpsModal({ return (
+
event.stopPropagation()} + > +
event.stopPropagation()} onSubmit={handleSubmit} style={{ width: "100%", maxWidth: 760, padding: 28 }} > @@ -89,9 +102,9 @@ export default function EnvironmentOpsModal({ style={{ display: "flex", alignItems: "flex-start", - justifyContent: "space-between", gap: 12, marginBottom: 20, + paddingRight: 36, }} >
@@ -110,19 +123,6 @@ export default function EnvironmentOpsModal({ Informasi dan edit metadata environment.

-
+ ); } diff --git a/src/app/projects/components/ProjectFormModal.tsx b/src/app/projects/components/ProjectFormModal.tsx index f25709d..6e1a38a 100644 --- a/src/app/projects/components/ProjectFormModal.tsx +++ b/src/app/projects/components/ProjectFormModal.tsx @@ -44,23 +44,36 @@ export default function ProjectFormModal({ return (
-
event.stopPropagation()} - onSubmit={handleSubmit} - style={{ - width: "100%", - maxWidth: 560, - padding: 28, - }} > + +
@@ -82,21 +95,8 @@ export default function ProjectFormModal({ {isEditing ? "Update project information used across environments and deployments." : "Create a new project to manage your environments and deployments."} -

+

-
@@ -195,6 +195,7 @@ export default function ProjectFormModal({
+
); } diff --git a/src/app/s3-storage/components/S3StorageSettingsPanel.tsx b/src/app/s3-storage/components/S3StorageSettingsPanel.tsx index 84d5aac..2483624 100644 --- a/src/app/s3-storage/components/S3StorageSettingsPanel.tsx +++ b/src/app/s3-storage/components/S3StorageSettingsPanel.tsx @@ -599,6 +599,19 @@ export default function S3StorageSettingsPanel({ {draft ? (
+
+
@@ -639,24 +652,6 @@ export default function S3StorageSettingsPanel({ use it.

-
@@ -1148,6 +1143,7 @@ export default function S3StorageSettingsPanel({
+ ) : null} diff --git a/src/app/security/components/AddFirewallRuleModal.tsx b/src/app/security/components/AddFirewallRuleModal.tsx index 3c85cd7..fd0a175 100644 --- a/src/app/security/components/AddFirewallRuleModal.tsx +++ b/src/app/security/components/AddFirewallRuleModal.tsx @@ -1,4 +1,4 @@ -import { Loader2, Plus } from "lucide-react"; +import { Loader2, Plus, X } from "lucide-react"; import type { RuleFormState } from "./security-types"; interface AddFirewallRuleModalProps { @@ -19,16 +19,28 @@ export default function AddFirewallRuleModal({ return (
event.stopPropagation()} + > + +

Add Firewall Rule

-
@@ -174,6 +173,7 @@ export default function AddFirewallRuleModal({
+
); diff --git a/src/app/servers/components/ServerConfigModal.tsx b/src/app/servers/components/ServerConfigModal.tsx index ea25403..9505f4c 100644 --- a/src/app/servers/components/ServerConfigModal.tsx +++ b/src/app/servers/components/ServerConfigModal.tsx @@ -628,6 +628,15 @@ export default function ServerConfigModal({ return (
+
+
-
+
-
@@ -1156,6 +1154,7 @@ export default function ServerConfigModal({ ) : ( renderActiveTab() )} +
); diff --git a/src/app/servers/components/ServerFormModal.tsx b/src/app/servers/components/ServerFormModal.tsx index b36ba49..04f80d5 100644 --- a/src/app/servers/components/ServerFormModal.tsx +++ b/src/app/servers/components/ServerFormModal.tsx @@ -92,20 +92,29 @@ export default function ServerFormModal({ return (
-
+
+ +

{isEdit ? "Edit Server" : "Add Server"}

-
{error && (
+
); diff --git a/src/app/servers/page.tsx b/src/app/servers/page.tsx index 6b602f8..0f68177 100644 --- a/src/app/servers/page.tsx +++ b/src/app/servers/page.tsx @@ -458,22 +458,39 @@ export default function ServersPage() { onClick={closeDeleteModal} >
event.stopPropagation()} > + {!confirmDeleteStep ? ( + + ) : null} +
{!confirmDeleteStep ? (
@@ -515,20 +532,6 @@ export default function ServersPage() { server access assignments.

-
)} +
) : null} diff --git a/src/app/ssl/components/IssueCertificateModal.tsx b/src/app/ssl/components/IssueCertificateModal.tsx index 0a24b9b..d30588f 100644 --- a/src/app/ssl/components/IssueCertificateModal.tsx +++ b/src/app/ssl/components/IssueCertificateModal.tsx @@ -40,6 +40,15 @@ export default function IssueCertificateModal({ return (
+
+
@@ -68,18 +77,6 @@ export default function IssueCertificateModal({ Create a new SSL record from an existing domain.

-
{error ? ( @@ -188,6 +185,7 @@ export default function IssueCertificateModal({
+
); diff --git a/src/app/terminal/page.tsx b/src/app/terminal/page.tsx index 18a52e3..6f9453f 100644 --- a/src/app/terminal/page.tsx +++ b/src/app/terminal/page.tsx @@ -771,17 +771,29 @@ function TerminalPageContent() { onClick={() => setShowPicker(false)} style={{ zIndex: 200 }} > +
event.stopPropagation()} + > +
event.stopPropagation()} >
@@ -804,17 +816,6 @@ function TerminalPageContent() { Choose a server to open or restore an SSH session.

-
{pickerError && ( @@ -927,6 +928,7 @@ function TerminalPageContent() { )}
+ )} diff --git a/src/app/users/components/EditRoleModal.tsx b/src/app/users/components/EditRoleModal.tsx index 1afa16d..3314728 100644 --- a/src/app/users/components/EditRoleModal.tsx +++ b/src/app/users/components/EditRoleModal.tsx @@ -1,6 +1,6 @@ "use client"; -import { AlertCircle, Edit3, Loader2 } from "lucide-react"; +import { AlertCircle, Edit3, Loader2, X } from "lucide-react"; import { useState } from "react"; import type { UserRecord, UserRole } from "@/lib/api"; import { manageableRoles } from "@/app/users/components/user-role-config"; @@ -27,16 +27,28 @@ export default function EditRoleModal({ return (
event.stopPropagation()} + > + +
@@ -53,19 +65,6 @@ export default function EditRoleModal({ {user.email}

-
{error ? ( @@ -152,7 +151,8 @@ export default function EditRoleModal({ Save Role
+
); -} \ No newline at end of file +} diff --git a/src/app/users/components/InviteUserModal.tsx b/src/app/users/components/InviteUserModal.tsx index 9f2b6eb..2585cb9 100644 --- a/src/app/users/components/InviteUserModal.tsx +++ b/src/app/users/components/InviteUserModal.tsx @@ -1,6 +1,6 @@ "use client"; -import { AlertCircle, CheckCircle, Copy, Key, Loader2 } from "lucide-react"; +import { AlertCircle, CheckCircle, Copy, Key, Loader2, X } from "lucide-react"; import { useState } from "react"; import type { CreateUserInvitationBody, @@ -58,16 +58,28 @@ export default function InviteUserModal({ return (
event.stopPropagation()} + > + +
@@ -86,19 +98,6 @@ export default function InviteUserModal({ Generate an onboarding link so the user sets their own password.

-
{result ? ( @@ -379,6 +378,7 @@ export default function InviteUserModal({
)} +
); diff --git a/src/app/users/components/ServerAccessModal.tsx b/src/app/users/components/ServerAccessModal.tsx index 1f290f3..f2ba815 100644 --- a/src/app/users/components/ServerAccessModal.tsx +++ b/src/app/users/components/ServerAccessModal.tsx @@ -1,6 +1,6 @@ "use client"; -import { AlertCircle, Loader2, Shield } from "lucide-react"; +import { AlertCircle, Loader2, Shield, X } from "lucide-react"; import { useState } from "react"; import type { Server as ServerRecord, UserRecord } from "@/lib/api"; import ServerAccessSelector from "@/app/users/components/ServerAccessSelector"; @@ -35,16 +35,28 @@ export default function ServerAccessModal({ return (
event.stopPropagation()} + > + +
@@ -61,19 +73,6 @@ export default function ServerAccessModal({ {user.email}

-
{error ? ( @@ -140,7 +139,8 @@ export default function ServerAccessModal({ Save Access
+
); -} \ No newline at end of file +} diff --git a/src/components/ConfirmActionDialog.tsx b/src/components/ConfirmActionDialog.tsx index 73c4440..214f8fe 100644 --- a/src/components/ConfirmActionDialog.tsx +++ b/src/components/ConfirmActionDialog.tsx @@ -73,21 +73,35 @@ export default function ConfirmActionDialog({ onClick={onClose} >
event.stopPropagation()} > + +
@@ -121,19 +135,6 @@ export default function ConfirmActionDialog({ {description}

-
{note ? ( @@ -176,6 +177,7 @@ export default function ConfirmActionDialog({
+
);