From a0d766de8afef6be287e51aedc37065fef7d6f2b Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 16 May 2026 04:12:15 -0300 Subject: [PATCH] =?UTF-8?q?refactor(dashboard):=20layout=20accordion=20par?= =?UTF-8?q?a=20endpoint=20=E2=80=94=20barra=20de=20URLs=20ativas=20+=20lin?= =?UTF-8?q?has=20colaps=C3=A1veis=20por=20t=C3=BAnel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dashboard/endpoint/EndpointPageClient.tsx | 856 ++++++++++-------- 1 file changed, 496 insertions(+), 360 deletions(-) diff --git a/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx b/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx index 7e047d1f52..a29b2c8a2c 100644 --- a/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx +++ b/src/app/(dashboard)/dashboard/endpoint/EndpointPageClient.tsx @@ -126,7 +126,7 @@ const DEFAULT_TUNNEL_VISIBILITY: EndpointTunnelVisibility = { function runEndpointBackgroundTask(taskName: string, task: () => Promise) { void task().catch((error) => { - console.log(`Error running endpoint background task (${taskName}):`, error); + console.log("Error running endpoint background task:", taskName, error); }); } @@ -173,6 +173,7 @@ export default function APIPageClient({ machineId }: Readonly(null); const [ngrokToken, setNgrokToken] = useState(""); const [showNgrokTunnel, setShowNgrokTunnel] = useState(true); + const [expandedTunnel, setExpandedTunnel] = useState(null); const { copied, copy } = useCopyToClipboard(); @@ -1029,6 +1030,31 @@ export default function APIPageClient({ machineId }: Readonly {/* Endpoint Card */} - -
-
-

{t("title")}

-
- -
- {resolvedMachineId && ( -

- {t("machineId", { id: resolvedMachineId.slice(0, 8) })} -

- )} -
-
- {cloudEnabled ? ( - - ) : cloudConfigured ? ( - - ) : ( - - Cloud not configured - - )} -
-
+ +

{t("title")}

{/* Cloud Status Toast */} {cloudStatus && ( @@ -1230,143 +1205,282 @@ export default function APIPageClient({ machineId }: Readonly )} - {/* Endpoint URL */} -
- - -
- - {showCloudflaredTunnel && ( -
-
-
-
-
-

- {translateOrFallback("cloudflaredTitle", "Cloudflare Quick Tunnel")} -

- - {cloudflaredPhaseMeta[cloudflaredPhase].label} + {/* Active URLs — shown only when more than just local is active */} + {activeUrls.length > 1 && ( +
+

+ Active Endpoints +

+
+ {activeUrls.map(({ label, url, key }) => ( +
+ + {label} + + {url} + +
+
+ ))} +
+
+ )} + {/* Connection rows */} +
+ {/* Local Server */} +
+ + computer + +
+ Local Server + {resolvedMachineId && ( + + · {resolvedMachineId.slice(0, 8)} + + )} +
+ + + Running + + + {baseUrl} + + +
+ + {/* Cloud OmniRoute */} +
+ + cloud + +
+ Cloud OmniRoute + {cloudEnabled && cloudEndpointNew && ( + + {cloudEndpointNew} + + )} +
+ + + {cloudEnabled ? "Active" : "Disabled"} + + {cloudEnabled ? ( + + ) : cloudConfigured ? ( + + ) : ( + + Not configured + + )} +
+ + {/* Tunnels section header */} + {(showCloudflaredTunnel || showTailscaleFunnel || showNgrokTunnel) && ( +
+ + network_node + + + Tunnels + +
+ + {activeTunnelCount} / {visibleTunnelCount} active + +
+ )} + + {/* Cloudflare Quick Tunnel */} + {showCloudflaredTunnel && ( +
+ )} -
- - {cloudflaredNotice && ( -
- - {cloudflaredNotice.type === "success" - ? "check_circle" - : cloudflaredNotice.type === "info" - ? "info" - : "error"} - - {cloudflaredNotice.message} - + expand_more + + + {expandedTunnel === "cf" && ( +
+ {cloudflaredNotice && ( +
+ + {cloudflaredNotice.type === "success" + ? "check_circle" + : cloudflaredNotice.type === "info" + ? "info" + : "error"} + + {cloudflaredNotice.message} + +
+ )} +

{cloudflaredUrlNotice}

+
+ + +
+ {cloudflaredStatus?.lastError && ( +

+ {translateOrFallback("cloudflaredLastError", "Last error: {error}", { + error: cloudflaredStatus.lastError, + })} +

+ )}
)} - -

{cloudflaredUrlNotice}

-
- - -
- {cloudflaredStatus?.lastError && ( -

- {translateOrFallback("cloudflaredLastError", "Last error: {error}", { - error: cloudflaredStatus.lastError, - })} -

- )}
-
- )} + )} - {showTailscaleFunnel && ( -
-
-
-
-
-

- {translateOrFallback("tailscaleTitle", "Tailscale Funnel")} -

- - {tailscalePhaseMeta[tailscalePhase].label} - -
+ {/* Tailscale Funnel */} + {showTailscaleFunnel && ( +
+ )} -
- - {tailscaleNotice && ( -
- - {tailscaleNotice.type === "success" - ? "check_circle" - : tailscaleNotice.type === "info" - ? "info" - : "error"} - - {tailscaleNotice.message} - -
- )} - -

{tailscaleUrlNotice}

- {tailscaleStatus?.phase === "needs_login" && ( -

- {translateOrFallback( - "tailscaleNeedsLoginHint", - "Authenticate this machine with Tailscale, then enable Funnel." + expand_more + + + {expandedTunnel === "ts" && ( +

+ {tailscaleNotice && ( +
+ + {tailscaleNotice.type === "success" + ? "check_circle" + : tailscaleNotice.type === "info" + ? "info" + : "error"} + + {tailscaleNotice.message} + +
)} -

- )} - {/* Sudo password input — shown when Tailscale is installed but not running (needs sudo to start daemon) */} - {tailscaleStatus?.installed && - !tailscaleStatus?.running && - tailscaleStatus?.platform !== "win32" && ( -
-
)}
-
- )} + )} - {showNgrokTunnel && ( -
-
-
-
-
-

- {translateOrFallback("ngrokTitle", "ngrok Tunnel")} -

- - {ngrokPhaseMeta[ngrokPhase].label} - -
+ {/* ngrok Tunnel */} + {showNgrokTunnel && ( +
+ )} -
- - {ngrokNotice && ( -
- - {ngrokNotice.type === "success" - ? "check_circle" - : ngrokNotice.type === "info" - ? "info" - : "error"} - - {ngrokNotice.message} - + expand_more + + + {expandedTunnel === "ngrok" && ( +
+ {ngrokNotice && ( +
+ + {ngrokNotice.type === "success" + ? "check_circle" + : ngrokNotice.type === "info" + ? "info" + : "error"} + + {ngrokNotice.message} + +
+ )} +

{ngrokUrlNotice}

+ {ngrokStatus?.phase === "needs_auth" && ( +
+ + setNgrokToken(event.target.value)} + placeholder={translateOrFallback( + "ngrokAuthTokenPlaceholder", + "Enter your ngrok authtoken" + )} + disabled={ngrokBusy} + className="font-mono text-sm" + /> +
+ )} +
+ + +
+ {ngrokStatus?.lastError && ( +

+ {translateOrFallback("ngrokLastError", "Last error: {error}", { + error: ngrokStatus.lastError, + })} +

+ )}
)} - -

{ngrokUrlNotice}

- {ngrokStatus?.phase === "needs_auth" && ( -
- - setNgrokToken(event.target.value)} - placeholder={translateOrFallback( - "ngrokAuthTokenPlaceholder", - "Enter your ngrok authtoken" - )} - disabled={ngrokBusy} - className="font-mono text-sm" - /> -
- )} -
- - -
- {ngrokStatus?.lastError && ( -

- {translateOrFallback("ngrokLastError", "Last error: {error}", { - error: ngrokStatus.lastError, - })} -

- )}
-
- )} + )} +