Skip to content

Commit 90d1998

Browse files
authored
feat: move port mappings to networks tab for container details (#1723)
1 parent 8201c31 commit 90d1998

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

frontend/src/routes/(app)/containers/[containerId]/+page.svelte

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import ActionButtons from '$lib/components/action-buttons.svelte';
55
import StatusBadge from '$lib/components/badges/status-badge.svelte';
66
import bytes from 'bytes';
7-
import { onDestroy, untrack } from 'svelte';
7+
import { onDestroy, tick, untrack } from 'svelte';
88
import { page } from '$app/state';
99
import type {
1010
ContainerDetailsDto,
@@ -213,11 +213,12 @@
213213
const hasEnvVars = $derived(!!(container?.config?.env && container.config.env.length > 0));
214214
const hasPorts = $derived(!!(container?.ports && container.ports.length > 0));
215215
const hasLabels = $derived(!!(container?.labels && Object.keys(container.labels).length > 0));
216-
const showConfiguration = $derived(hasEnvVars || hasPorts || hasLabels);
216+
const showConfiguration = $derived(hasEnvVars || hasLabels);
217217
218218
const hasNetworks = $derived(
219219
!!(container?.networkSettings?.networks && Object.keys(container.networkSettings.networks).length > 0)
220220
);
221+
const showNetworkTab = $derived(hasNetworks || hasPorts);
221222
const hasMounts = $derived(!!(container?.mounts && container.mounts.length > 0));
222223
const showStats = $derived(!!container?.state?.running);
223224
const showShell = $derived(!!container?.state?.running);
@@ -228,7 +229,7 @@
228229
{ value: 'logs', label: m.containers_nav_logs(), icon: FileTextIcon },
229230
...(showShell ? [{ value: 'shell', label: m.common_shell(), icon: TerminalIcon }] : []),
230231
...(showConfiguration ? [{ value: 'config', label: m.common_configuration(), icon: SettingsIcon }] : []),
231-
...(hasNetworks ? [{ value: 'network', label: m.containers_nav_networks(), icon: NetworksIcon }] : []),
232+
...(showNetworkTab ? [{ value: 'network', label: m.containers_nav_networks(), icon: NetworksIcon }] : []),
232233
...(hasMounts ? [{ value: 'storage', label: m.containers_nav_storage(), icon: VolumesIcon }] : [])
233234
]);
234235
@@ -242,6 +243,17 @@
242243
selectedTab = value;
243244
}
244245
246+
async function navigateToNetworkPortMappings() {
247+
if (!showNetworkTab) return;
248+
249+
selectedTab = 'network';
250+
await tick();
251+
252+
requestAnimationFrame(() => {
253+
document.getElementById('container-port-mappings')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
254+
});
255+
}
256+
245257
function parseDockerDate(input: string | Date | undefined | null): Date | null {
246258
if (!input) return null;
247259
if (input instanceof Date) return isNaN(input.getTime()) ? null : input;
@@ -310,7 +322,11 @@
310322

311323
{#snippet tabContent(activeTab)}
312324
<Tabs.Content value="overview" class="h-full">
313-
<ContainerOverview {container} {primaryIpAddress} />
325+
<ContainerOverview
326+
{container}
327+
{primaryIpAddress}
328+
onViewPortMappings={showNetworkTab ? navigateToNetworkPortMappings : undefined}
329+
/>
314330
</Tabs.Content>
315331

316332
{#if showStats}
@@ -357,7 +373,7 @@
357373
</Tabs.Content>
358374
{/if}
359375

360-
{#if hasNetworks}
376+
{#if showNetworkTab}
361377
<Tabs.Content value="network" class="h-full">
362378
<ContainerNetwork {container} />
363379
</Tabs.Content>

frontend/src/routes/(app)/containers/components/ContainerNetwork.svelte

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script lang="ts">
22
import * as Card from '$lib/components/ui/card';
3+
import { PortBadge } from '$lib/components/badges';
34
import { m } from '$lib/paraglide/messages';
45
import type { ContainerDetailsDto } from '$lib/types/container.type';
56
import { NetworksIcon } from '$lib/icons';
@@ -22,6 +23,27 @@
2223
</script>
2324

2425
<div class="space-y-6">
26+
<Card.Root id="container-port-mappings">
27+
<Card.Header icon={NetworksIcon}>
28+
<div class="flex flex-col space-y-1.5">
29+
<Card.Title>
30+
<h2>
31+
{m.common_port_mappings()}
32+
</h2>
33+
</Card.Title>
34+
</div>
35+
</Card.Header>
36+
<Card.Content class="p-4">
37+
{#if container.ports && container.ports.length > 0}
38+
<PortBadge ports={container.ports} />
39+
{:else}
40+
<div class="text-muted-foreground rounded-lg border border-dashed py-12 text-center">
41+
<div class="text-sm">{m.containers_no_ports()}</div>
42+
</div>
43+
{/if}
44+
</Card.Content>
45+
</Card.Root>
46+
2547
<Card.Root>
2648
<Card.Header icon={NetworksIcon}>
2749
<div class="flex flex-col space-y-1.5">
@@ -141,7 +163,7 @@
141163
{m.containers_aliases()}
142164
</div>
143165
<div class="text-foreground space-y-1 text-sm font-medium">
144-
{#each rawNetworkConfig.aliases as alias}
166+
{#each rawNetworkConfig.aliases as alias, index (index)}
145167
<div class="cursor-pointer font-mono break-all select-all" title="Click to select">
146168
{alias}
147169
</div>

frontend/src/routes/(app)/containers/components/ContainerOverview.svelte

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<script lang="ts">
22
import * as Card from '$lib/components/ui/card';
33
import StatusBadge from '$lib/components/badges/status-badge.svelte';
4-
import { PortBadge } from '$lib/components/badges';
54
import { m } from '$lib/paraglide/messages';
65
import type { ContainerDetailsDto } from '$lib/types/container.type';
76
import { format, formatDistanceToNow } from 'date-fns';
@@ -10,9 +9,10 @@
109
interface Props {
1110
container: ContainerDetailsDto;
1211
primaryIpAddress: string;
12+
onViewPortMappings?: () => void;
1313
}
1414
15-
let { container, primaryIpAddress }: Props = $props();
15+
let { container, primaryIpAddress, onViewPortMappings }: Props = $props();
1616
1717
function parseDockerDate(input: string | Date | undefined | null): Date | null {
1818
if (!input) return null;
@@ -256,6 +256,11 @@
256256
{m.containers_ports_exposed({ exposed: uniquePorts.exposed })}
257257
{/if}
258258
</div>
259+
{#if onViewPortMappings && uniquePorts.total > 0}
260+
<button type="button" class="text-primary w-fit text-xs font-medium hover:underline" onclick={onViewPortMappings}>
261+
{m.common_view_details()} → {m.containers_nav_networks()}
262+
</button>
263+
{/if}
259264
</Card.Content>
260265
</Card.Root>
261266

@@ -345,14 +350,5 @@
345350
</Card.Root>
346351
{/if}
347352
</div>
348-
349-
{#if container.ports && container.ports.length > 0}
350-
<div class="mt-6">
351-
<div class="text-muted-foreground mb-3 text-xs font-semibold tracking-wide uppercase">
352-
{m.common_port_mappings()}
353-
</div>
354-
<PortBadge ports={container.ports} />
355-
</div>
356-
{/if}
357353
</Card.Content>
358354
</Card.Root>

0 commit comments

Comments
 (0)