fix: add ConfirmDialog for renewing DHCP lease and improve network settings layout

- Integrated ConfirmDialog component to confirm DHCP lease renewal.
- Enhanced the layout of network settings, including better organization of IPv4 and IPv6 information.
- Updated state management for displaying network settings and lease information.
- Improved user experience with clearer descriptions and structured UI elements.
This commit is contained in:
Adam Shiervani 2025-04-16 22:01:10 +02:00
parent fcf2dbab60
commit df74cb111d
2 changed files with 468 additions and 445 deletions

View File

@ -103,4 +103,4 @@ export function ConfirmDialog({
</div>
</Modal>
);
}
}

View File

@ -1,6 +1,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
import { ArrowPathIcon } from "@heroicons/react/24/outline";
import {
IPv4Mode,
@ -23,6 +24,7 @@ import { SelectMenuBasic } from "../components/SelectMenuBasic";
import { SettingsItem } from "./devices.$id.settings";
import Fieldset from "../components/Fieldset";
import { ConfirmDialog } from "../components/ConfirmDialog";
dayjs.extend(relativeTime);
@ -206,471 +208,492 @@ export default function SettingsNetworkRoute() {
[networkSettingsLoaded],
);
console.log("firstNetworkSettings", firstNetworkSettings.current);
console.log("networkSettings", networkSettings);
const [showRenewLeaseConfirm, setShowRenewLeaseConfirm] = useState(false);
return (
<Fieldset disabled={!networkSettingsLoaded} className="space-y-4">
<SettingsPageHeader title="Network" description="Configure your network settings" />
<div className="space-y-4">
<SettingsItem
title="MAC Address"
description="Hardware identifier for the network interface"
>
<InputField
type="text"
size="SM"
value={networkState?.mac_address}
error={""}
disabled={true}
readOnly={true}
className="dark:!text-opacity-60"
/>
</SettingsItem>
</div>
<div className="space-y-4">
<SettingsItem
title="Hostname"
description="Device identifier on the network. Blank for system default"
>
<div className="relative">
<div>
<InputField
size="SM"
type="text"
placeholder="jetkvm"
defaultValue={networkSettings.hostname}
onChange={e => {
handleHostnameChange(e.target.value);
}}
/>
</div>
</div>
</SettingsItem>
</div>
<div className="space-y-4">
<>
<Fieldset disabled={!networkSettingsLoaded} className="space-y-4">
<SettingsPageHeader
title="Network"
description="Configure your network settings"
/>
<div className="space-y-4">
<SettingsItem title="Domain" description="Network domain suffix for the device">
<div className="space-y-2">
<SelectMenuBasic
size="SM"
value={selectedDomainOption}
onChange={e => handleDomainOptionChange(e.target.value)}
options={[
{ value: "dhcp", label: "DHCP provided" },
{ value: "local", label: ".local" },
{ value: "custom", label: "Custom" },
]}
/>
<SettingsItem
title="MAC Address"
description="Hardware identifier for the network interface"
>
<InputField
type="text"
size="SM"
value={networkState?.mac_address}
error={""}
disabled={true}
readOnly={true}
className="dark:!text-opacity-60"
/>
</SettingsItem>
</div>
<div className="space-y-4">
<SettingsItem
title="Hostname"
description="Device identifier on the network. Blank for system default"
>
<div className="relative">
<div>
<InputField
size="SM"
type="text"
placeholder="jetkvm"
defaultValue={networkSettings.hostname}
onChange={e => {
handleHostnameChange(e.target.value);
}}
/>
</div>
</div>
</SettingsItem>
{selectedDomainOption === "custom" && (
<div className="flex items-center justify-between gap-x-2">
<InputField
</div>
<div className="space-y-4">
<div className="space-y-4">
<SettingsItem
title="Domain"
description="Network domain suffix for the device"
>
<div className="space-y-2">
<SelectMenuBasic
size="SM"
value={selectedDomainOption}
onChange={e => handleDomainOptionChange(e.target.value)}
options={[
{ value: "dhcp", label: "DHCP provided" },
{ value: "local", label: ".local" },
{ value: "custom", label: "Custom" },
]}
/>
</div>
</SettingsItem>
{selectedDomainOption === "custom" && (
<div className="flex items-center justify-between gap-x-2">
<InputField
size="SM"
type="text"
placeholder="home"
value={customDomain}
onChange={e => setCustomDomain(e.target.value)}
/>
<Button
size="SM"
theme="primary"
text="Save Domain"
onClick={() => handleCustomDomainChange(customDomain)}
/>
</div>
)}
</div>
<div className="space-y-4">
<SettingsItem
title="mDNS"
description="Control mDNS (multicast DNS) operational mode"
>
<SelectMenuBasic
size="SM"
type="text"
placeholder="home"
value={customDomain}
onChange={e => setCustomDomain(e.target.value)}
value={networkSettings.mdns_mode}
onChange={e => handleMdnsModeChange(e.target.value)}
options={filterUnknown([
{ value: "disabled", label: "Disabled" },
{ value: "auto", label: "Auto" },
{ value: "ipv4_only", label: "IPv4 only" },
{ value: "ipv6_only", label: "IPv6 only" },
])}
/>
<Button
</SettingsItem>
</div>
<div className="space-y-4">
<SettingsItem
title="Time synchronization"
description="Configure time synchronization settings"
>
<SelectMenuBasic
size="SM"
theme="primary"
text="Save Domain"
onClick={() => handleCustomDomainChange(customDomain)}
value={networkSettings.time_sync_mode}
onChange={e => {
handleTimeSyncModeChange(e.target.value);
}}
options={filterUnknown([
{ value: "unknown", label: "..." },
// { value: "auto", label: "Auto" },
{ value: "ntp_only", label: "NTP only" },
{ value: "ntp_and_http", label: "NTP and HTTP" },
{ value: "http_only", label: "HTTP only" },
// { value: "custom", label: "Custom" },
])}
/>
</div>
</SettingsItem>
</div>
<Button
size="SM"
theme="primary"
disabled={firstNetworkSettings.current === networkSettings}
text="Save Settings"
onClick={() => setNetworkSettingsRemote(networkSettings)}
/>
</div>
<div className="h-[1px] w-full bg-slate-800/10 dark:bg-slate-300/20" />
<div className="space-y-4">
<SettingsItem title="IPv4 Mode" description="Configure the IPv4 mode">
<SelectMenuBasic
size="SM"
value={networkSettings.ipv4_mode}
onChange={e => handleIpv4ModeChange(e.target.value)}
options={filterUnknown([
{ value: "dhcp", label: "DHCP" },
// { value: "static", label: "Static" },
])}
/>
</SettingsItem>
{networkState?.dhcp_lease && (
<GridCard>
<div className="p-4">
<div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white">
DHCP Lease
</h3>
<div className="flex gap-x-6 gap-y-2">
<div className="flex-1 space-y-2">
{networkState?.dhcp_lease?.ip && (
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
IP Address
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.ip}
</span>
</div>
)}
{networkState?.dhcp_lease?.netmask && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Subnet Mask
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.netmask}
</span>
</div>
)}
{networkState?.dhcp_lease?.dns && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
DNS Servers
</span>
<span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.dns.map(dns => (
<div key={dns}>{dns}</div>
))}
</span>
</div>
)}
{networkState?.dhcp_lease?.broadcast && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Broadcast
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.broadcast}
</span>
</div>
)}
{networkState?.dhcp_lease?.domain && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Domain
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.domain}
</span>
</div>
)}
{networkState?.dhcp_lease?.ntp_servers &&
networkState?.dhcp_lease?.ntp_servers.length > 0 && (
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
NTP Servers
</div>
<div className="shrink text-right text-sm font-medium">
{networkState?.dhcp_lease?.ntp_servers.map(server => (
<div key={server}>{server}</div>
))}
</div>
</div>
)}
{networkState?.dhcp_lease?.hostname && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Hostname
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.hostname}
</span>
</div>
)}
</div>
<div className="flex-1 space-y-2">
{networkState?.dhcp_lease?.routers &&
networkState?.dhcp_lease?.routers.length > 0 && (
<div className="flex justify-between pt-2">
<span className="text-sm text-slate-600 dark:text-slate-400">
Gateway
</span>
<span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.routers.map(router => (
<div key={router}>{router}</div>
))}
</span>
</div>
)}
{networkState?.dhcp_lease?.server_id && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
DHCP Server
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.server_id}
</span>
</div>
)}
{networkState?.dhcp_lease?.lease_expiry && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Lease Expires
</span>
<span className="text-sm font-medium">
<LifeTimeLabel
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
/>
</span>
</div>
)}
{networkState?.dhcp_lease?.mtu && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
MTU
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.mtu}
</span>
</div>
)}
{networkState?.dhcp_lease?.ttl && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
TTL
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.ttl}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_next_server && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot Next Server
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_next_server}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_server_name && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot Server Name
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_server_name}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_file && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot File
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_file}
</span>
</div>
)}
</div>
</div>
<div>
<Button
size="SM"
theme="light"
className="text-red-500"
text="Renew DHCP Lease"
LeadingIcon={ArrowPathIcon}
onClick={() => setShowRenewLeaseConfirm(true)}
/>
</div>
</div>
</div>
</GridCard>
)}
</div>
<div className="space-y-4">
<SettingsItem title="IPv6 Mode" description="Configure the IPv6 mode">
<SelectMenuBasic
size="SM"
value={networkSettings.ipv6_mode}
onChange={e => handleIpv6ModeChange(e.target.value)}
options={filterUnknown([
// { value: "disabled", label: "Disabled" },
{ value: "slaac", label: "SLAAC" },
// { value: "dhcpv6", label: "DHCPv6" },
// { value: "slaac_and_dhcpv6", label: "SLAAC and DHCPv6" },
// { value: "static", label: "Static" },
// { value: "link_local", label: "Link-local only" },
])}
/>
</SettingsItem>
{networkState?.ipv6_addresses && (
<GridCard>
<div className="p-4">
<div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white">
IPv6 Information
</h3>
<div className="grid grid-cols-2 gap-x-6 gap-y-2">
{networkState?.dhcp_lease?.ip && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Link-local
</span>
<span className="text-sm font-medium">
{networkState?.ipv6_link_local}
</span>
</div>
)}
</div>
<div className="space-y-3 pt-2">
{networkState?.ipv6_addresses &&
networkState?.ipv6_addresses.length > 0 && (
<div className="space-y-3">
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
{networkState.ipv6_addresses.map(addr => (
<div
key={addr.address}
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-slate-100/40 p-4 pl-4 dark:border-blue-500 dark:bg-slate-900"
>
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
<div className="col-span-2 flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Address
</span>
<span className="text-sm font-medium">
{addr.address}
</span>
</div>
{addr.valid_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Valid Lifetime
</span>
<span className="text-sm font-medium">
{addr.valid_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel
lifetime={`${addr.valid_lifetime}`}
/>
)}
</span>
</div>
)}
{addr.preferred_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Preferred Lifetime
</span>
<span className="text-sm font-medium">
{addr.preferred_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel
lifetime={`${addr.preferred_lifetime}`}
/>
)}
</span>
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</GridCard>
)}
</div>
<div className="hidden space-y-4">
<SettingsItem
title="mDNS"
description="Control mDNS (multicast DNS) operational mode"
title="LLDP"
description="Control which TLVs will be sent over Link Layer Discovery Protocol"
>
<SelectMenuBasic
size="SM"
value={networkSettings.mdns_mode}
onChange={e => handleMdnsModeChange(e.target.value)}
value={networkSettings.lldp_mode}
onChange={e => handleLldpModeChange(e.target.value)}
options={filterUnknown([
{ value: "disabled", label: "Disabled" },
{ value: "auto", label: "Auto" },
{ value: "ipv4_only", label: "IPv4 only" },
{ value: "ipv6_only", label: "IPv6 only" },
{ value: "basic", label: "Basic" },
{ value: "all", label: "All" },
])}
/>
</SettingsItem>
</div>
<div className="space-y-4">
<SettingsItem
title="Time synchronization"
description="Configure time synchronization settings"
>
<SelectMenuBasic
size="SM"
value={networkSettings.time_sync_mode}
onChange={e => {
handleTimeSyncModeChange(e.target.value);
}}
options={filterUnknown([
{ value: "unknown", label: "..." },
// { value: "auto", label: "Auto" },
{ value: "ntp_only", label: "NTP only" },
{ value: "ntp_and_http", label: "NTP and HTTP" },
{ value: "http_only", label: "HTTP only" },
// { value: "custom", label: "Custom" },
])}
/>
</SettingsItem>
</div>
<Button
size="SM"
theme="primary"
disabled={firstNetworkSettings.current === networkSettings}
text="Save Settings"
onClick={() => setNetworkSettingsRemote(networkSettings)}
/>
</div>
<div className="h-[1px] w-full bg-slate-800/10 dark:bg-slate-300/20" />
<div className="space-y-4">
<SettingsItem title="IPv4 Mode" description="Configure the IPv4 mode">
<SelectMenuBasic
size="SM"
value={networkSettings.ipv4_mode}
onChange={e => handleIpv4ModeChange(e.target.value)}
options={filterUnknown([
{ value: "dhcp", label: "DHCP" },
// { value: "static", label: "Static" },
])}
/>
</SettingsItem>
{networkState?.dhcp_lease && (
<GridCard>
<div className="p-4">
<div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white">
Current DHCP Lease
</h3>
<div className="flex gap-x-6 gap-y-2">
<div className="flex-1 space-y-2">
{networkState?.dhcp_lease?.ip && (
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
IP Address
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.ip}
</span>
</div>
)}
{networkState?.dhcp_lease?.netmask && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Subnet Mask
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.netmask}
</span>
</div>
)}
{networkState?.dhcp_lease?.dns && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
DNS Servers
</span>
<span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.dns.map(dns => (
<div key={dns}>{dns}</div>
))}
</span>
</div>
)}
{networkState?.dhcp_lease?.broadcast && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Broadcast
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.broadcast}
</span>
</div>
)}
{networkState?.dhcp_lease?.domain && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Domain
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.domain}
</span>
</div>
)}
{networkState?.dhcp_lease?.ntp_servers &&
networkState?.dhcp_lease?.ntp_servers.length > 0 && (
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
NTP Servers
</div>
<div className="shrink text-right text-sm font-medium">
{networkState?.dhcp_lease?.ntp_servers.map(server => (
<div key={server}>{server}</div>
))}
</div>
</div>
)}
{networkState?.dhcp_lease?.hostname && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Hostname
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.hostname}
</span>
</div>
)}
</div>
<div className="flex-1 space-y-2">
{networkState?.dhcp_lease?.routers &&
networkState?.dhcp_lease?.routers.length > 0 && (
<div className="flex justify-between pt-2">
<span className="text-sm text-slate-600 dark:text-slate-400">
Gateway
</span>
<span className="text-right text-sm font-medium">
{networkState?.dhcp_lease?.routers.map(router => (
<div key={router}>{router}</div>
))}
</span>
</div>
)}
{networkState?.dhcp_lease?.server_id && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
DHCP Server
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.server_id}
</span>
</div>
)}
{networkState?.dhcp_lease?.lease_expiry && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Lease Expires
</span>
<span className="text-sm font-medium">
<LifeTimeLabel
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
/>
</span>
</div>
)}
{networkState?.dhcp_lease?.mtu && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
MTU
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.mtu}
</span>
</div>
)}
{networkState?.dhcp_lease?.ttl && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
TTL
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.ttl}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_next_server && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot Next Server
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_next_server}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_server_name && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot Server Name
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_server_name}
</span>
</div>
)}
{networkState?.dhcp_lease?.bootp_file && (
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
<span className="text-sm text-slate-600 dark:text-slate-400">
Boot File
</span>
<span className="text-sm font-medium">
{networkState?.dhcp_lease?.bootp_file}
</span>
</div>
)}
</div>
</div>
<div>
<Button
size="SM"
theme="primary"
text="Renew lease"
onClick={handleRenewLease}
/>
</div>
</div>
</div>
</GridCard>
)}
</div>
<div className="space-y-4">
<SettingsItem title="IPv6 Mode" description="Configure the IPv6 mode">
<SelectMenuBasic
size="SM"
value={networkSettings.ipv6_mode}
onChange={e => handleIpv6ModeChange(e.target.value)}
options={filterUnknown([
// { value: "disabled", label: "Disabled" },
{ value: "slaac", label: "SLAAC" },
// { value: "dhcpv6", label: "DHCPv6" },
// { value: "slaac_and_dhcpv6", label: "SLAAC and DHCPv6" },
// { value: "static", label: "Static" },
// { value: "link_local", label: "Link-local only" },
])}
/>
</SettingsItem>
{networkState?.ipv6_addresses && (
<GridCard>
<div className="p-4">
<div className="space-y-4">
<h3 className="text-base font-bold text-slate-900 dark:text-white">
IPv6 Information
</h3>
<div className="grid grid-cols-2 gap-x-6 gap-y-2">
{networkState?.dhcp_lease?.ip && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Link-local
</span>
<span className="text-sm font-medium">
{networkState?.ipv6_link_local}
</span>
</div>
)}
</div>
<div className="space-y-3 pt-2">
{networkState?.ipv6_addresses &&
networkState?.ipv6_addresses.length > 0 && (
<div className="space-y-3">
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
{networkState.ipv6_addresses.map(addr => (
<div
key={addr.address}
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-slate-100/40 p-4 pl-4 dark:border-blue-500 dark:bg-slate-900"
>
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
<div className="col-span-2 flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Address
</span>
<span className="text-sm font-medium">
{addr.address}
</span>
</div>
{addr.valid_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Valid Lifetime
</span>
<span className="text-sm font-medium">
{addr.valid_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel
lifetime={`${addr.valid_lifetime}`}
/>
)}
</span>
</div>
)}
{addr.preferred_lifetime && (
<div className="flex flex-col justify-between">
<span className="text-sm text-slate-600 dark:text-slate-400">
Preferred Lifetime
</span>
<span className="text-sm font-medium">
{addr.preferred_lifetime === "" ? (
<span className="text-slate-400 dark:text-slate-600">
N/A
</span>
) : (
<LifeTimeLabel
lifetime={`${addr.preferred_lifetime}`}
/>
)}
</span>
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</GridCard>
)}
</div>
<div className="hidden space-y-4">
<SettingsItem
title="LLDP"
description="Control which TLVs will be sent over Link Layer Discovery Protocol"
>
<SelectMenuBasic
size="SM"
value={networkSettings.lldp_mode}
onChange={e => handleLldpModeChange(e.target.value)}
options={filterUnknown([
{ value: "disabled", label: "Disabled" },
{ value: "basic", label: "Basic" },
{ value: "all", label: "All" },
])}
/>
</SettingsItem>
</div>
</Fieldset>
</Fieldset>
<ConfirmDialog
open={showRenewLeaseConfirm}
onClose={() => setShowRenewLeaseConfirm(false)}
title="Renew DHCP Lease"
description="This will request a new IP address from your DHCP server. Your device may temporarily lose network connectivity during this process."
variant="danger"
confirmText="Renew Lease"
onConfirm={() => {
handleRenewLease();
setShowRenewLeaseConfirm(false);
}}
/>
</>
);
}