Skip to content

Commit 222a847

Browse files
refactor: network settings UI (#486)
* feat(ui): update prettier configuration and quote styles - Add cx to tailwind functions - Set tailwind stylesheet path - Convert single quotes to double quotes in CSS - Add prettier ignore comments for animation utilities * refactor(ui): extract network information into separate components - Create DhcpLeaseCard component - Create Ipv6NetworkCard component * style(ui): refine component styling and layout - Add padding to AutoHeight component - Improve lifetime label display format - Enhance network information card layouts * style(ui): enhance checkbox and radio button styling - Update Checkbox component to use form-checkbox class - Refactor radio button classes for consistency across components * style(ui): Add opacity for fade-in animations * refactor(ui): enhance Modal and network settings components - Add stable scrollbar gutter to Modal component - Refactor custom domain input handling and layout adjustments
1 parent 860327b commit 222a847

19 files changed

+468
-393
lines changed

ui/.prettierrc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"arrowParens": "avoid",
77
"singleQuote": false,
88
"plugins": ["prettier-plugin-tailwindcss"],
9-
"tailwindFunctions": ["clsx"],
10-
"printWidth": 90
9+
"tailwindFunctions": ["clsx", "cx"],
10+
"printWidth": 90,
11+
"tailwindStylesheet": "./src/index.css"
1112
}

ui/src/components/AutoHeight.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const AutoHeight = ({ children, ...props }: { children: React.ReactNode }) => {
2222
{...props}
2323
height={height}
2424
duration={300}
25-
contentClassName="h-fit"
25+
contentClassName="h-fit p-px"
2626
contentRef={contentDiv}
2727
disableDisplayNone
2828
>

ui/src/components/Checkbox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const sizes = {
1212

1313
const checkboxVariants = cva({
1414
base: cx(
15-
"block rounded",
15+
"form-checkbox block rounded",
1616

1717
// Colors
1818
"border-slate-300 dark:border-slate-600 bg-slate-50 dark:bg-slate-800 checked:accent-blue-700 checked:dark:accent-blue-500 transition-colors",
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { LuRefreshCcw } from "react-icons/lu";
2+
3+
import { Button } from "@/components/Button";
4+
import { GridCard } from "@/components/Card";
5+
import { LifeTimeLabel } from "@/routes/devices.$id.settings.network";
6+
import { NetworkState } from "@/hooks/stores";
7+
8+
export default function DhcpLeaseCard({
9+
networkState,
10+
setShowRenewLeaseConfirm,
11+
}: {
12+
networkState: NetworkState;
13+
setShowRenewLeaseConfirm: (show: boolean) => void;
14+
}) {
15+
return (
16+
<GridCard>
17+
<div className="animate-fadeIn p-4 opacity-0 animation-duration-500 text-black dark:text-white">
18+
<div className="space-y-3">
19+
<h3 className="text-base font-bold text-slate-900 dark:text-white">
20+
DHCP Lease Information
21+
</h3>
22+
23+
<div className="flex gap-x-6 gap-y-2">
24+
<div className="flex-1 space-y-2">
25+
{networkState?.dhcp_lease?.ip && (
26+
<div className="flex justify-between border-slate-800/10 pt-2 dark:border-slate-300/20">
27+
<span className="text-sm text-slate-600 dark:text-slate-400">
28+
IP Address
29+
</span>
30+
<span className="text-sm font-medium">
31+
{networkState?.dhcp_lease?.ip}
32+
</span>
33+
</div>
34+
)}
35+
36+
{networkState?.dhcp_lease?.netmask && (
37+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
38+
<span className="text-sm text-slate-600 dark:text-slate-400">
39+
Subnet Mask
40+
</span>
41+
<span className="text-sm font-medium">
42+
{networkState?.dhcp_lease?.netmask}
43+
</span>
44+
</div>
45+
)}
46+
47+
{networkState?.dhcp_lease?.dns && (
48+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
49+
<span className="text-sm text-slate-600 dark:text-slate-400">
50+
DNS Servers
51+
</span>
52+
<span className="text-right text-sm font-medium">
53+
{networkState?.dhcp_lease?.dns.map(dns => <div key={dns}>{dns}</div>)}
54+
</span>
55+
</div>
56+
)}
57+
58+
{networkState?.dhcp_lease?.broadcast && (
59+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
60+
<span className="text-sm text-slate-600 dark:text-slate-400">
61+
Broadcast
62+
</span>
63+
<span className="text-sm font-medium">
64+
{networkState?.dhcp_lease?.broadcast}
65+
</span>
66+
</div>
67+
)}
68+
69+
{networkState?.dhcp_lease?.domain && (
70+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
71+
<span className="text-sm text-slate-600 dark:text-slate-400">
72+
Domain
73+
</span>
74+
<span className="text-sm font-medium">
75+
{networkState?.dhcp_lease?.domain}
76+
</span>
77+
</div>
78+
)}
79+
80+
{networkState?.dhcp_lease?.ntp_servers &&
81+
networkState?.dhcp_lease?.ntp_servers.length > 0 && (
82+
<div className="flex justify-between gap-x-8 border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
83+
<div className="w-full grow text-sm text-slate-600 dark:text-slate-400">
84+
NTP Servers
85+
</div>
86+
<div className="shrink text-right text-sm font-medium">
87+
{networkState?.dhcp_lease?.ntp_servers.map(server => (
88+
<div key={server}>{server}</div>
89+
))}
90+
</div>
91+
</div>
92+
)}
93+
94+
{networkState?.dhcp_lease?.hostname && (
95+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
96+
<span className="text-sm text-slate-600 dark:text-slate-400">
97+
Hostname
98+
</span>
99+
<span className="text-sm font-medium">
100+
{networkState?.dhcp_lease?.hostname}
101+
</span>
102+
</div>
103+
)}
104+
</div>
105+
106+
<div className="flex-1 space-y-2">
107+
{networkState?.dhcp_lease?.routers &&
108+
networkState?.dhcp_lease?.routers.length > 0 && (
109+
<div className="flex justify-between pt-2">
110+
<span className="text-sm text-slate-600 dark:text-slate-400">
111+
Gateway
112+
</span>
113+
<span className="text-right text-sm font-medium">
114+
{networkState?.dhcp_lease?.routers.map(router => (
115+
<div key={router}>{router}</div>
116+
))}
117+
</span>
118+
</div>
119+
)}
120+
121+
{networkState?.dhcp_lease?.server_id && (
122+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
123+
<span className="text-sm text-slate-600 dark:text-slate-400">
124+
DHCP Server
125+
</span>
126+
<span className="text-sm font-medium">
127+
{networkState?.dhcp_lease?.server_id}
128+
</span>
129+
</div>
130+
)}
131+
132+
{networkState?.dhcp_lease?.lease_expiry && (
133+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
134+
<span className="text-sm text-slate-600 dark:text-slate-400">
135+
Lease Expires
136+
</span>
137+
<span className="text-sm font-medium">
138+
<LifeTimeLabel
139+
lifetime={`${networkState?.dhcp_lease?.lease_expiry}`}
140+
/>
141+
</span>
142+
</div>
143+
)}
144+
145+
{networkState?.dhcp_lease?.mtu && (
146+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
147+
<span className="text-sm text-slate-600 dark:text-slate-400">MTU</span>
148+
<span className="text-sm font-medium">
149+
{networkState?.dhcp_lease?.mtu}
150+
</span>
151+
</div>
152+
)}
153+
154+
{networkState?.dhcp_lease?.ttl && (
155+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
156+
<span className="text-sm text-slate-600 dark:text-slate-400">TTL</span>
157+
<span className="text-sm font-medium">
158+
{networkState?.dhcp_lease?.ttl}
159+
</span>
160+
</div>
161+
)}
162+
163+
{networkState?.dhcp_lease?.bootp_next_server && (
164+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
165+
<span className="text-sm text-slate-600 dark:text-slate-400">
166+
Boot Next Server
167+
</span>
168+
<span className="text-sm font-medium">
169+
{networkState?.dhcp_lease?.bootp_next_server}
170+
</span>
171+
</div>
172+
)}
173+
174+
{networkState?.dhcp_lease?.bootp_server_name && (
175+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
176+
<span className="text-sm text-slate-600 dark:text-slate-400">
177+
Boot Server Name
178+
</span>
179+
<span className="text-sm font-medium">
180+
{networkState?.dhcp_lease?.bootp_server_name}
181+
</span>
182+
</div>
183+
)}
184+
185+
{networkState?.dhcp_lease?.bootp_file && (
186+
<div className="flex justify-between border-t border-slate-800/10 pt-2 dark:border-slate-300/20">
187+
<span className="text-sm text-slate-600 dark:text-slate-400">
188+
Boot File
189+
</span>
190+
<span className="text-sm font-medium">
191+
{networkState?.dhcp_lease?.bootp_file}
192+
</span>
193+
</div>
194+
)}
195+
</div>
196+
</div>
197+
198+
<div>
199+
<Button
200+
size="SM"
201+
theme="light"
202+
className="text-red-500"
203+
text="Renew DHCP Lease"
204+
LeadingIcon={LuRefreshCcw}
205+
onClick={() => setShowRenewLeaseConfirm(true)}
206+
/>
207+
</div>
208+
</div>
209+
</div>
210+
</GridCard>
211+
);
212+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { NetworkState } from "../hooks/stores";
2+
import { LifeTimeLabel } from "../routes/devices.$id.settings.network";
3+
4+
import { GridCard } from "./Card";
5+
6+
export default function Ipv6NetworkCard({
7+
networkState,
8+
}: {
9+
networkState: NetworkState;
10+
}) {
11+
return (
12+
<GridCard>
13+
<div className="animate-fadeIn p-4 text-black opacity-0 animation-duration-500 dark:text-white">
14+
<div className="space-y-4">
15+
<h3 className="text-base font-bold text-slate-900 dark:text-white">
16+
IPv6 Information
17+
</h3>
18+
19+
<div className="grid grid-cols-2 gap-x-6 gap-y-2">
20+
{networkState?.dhcp_lease?.ip && (
21+
<div className="flex flex-col justify-between">
22+
<span className="text-sm text-slate-600 dark:text-slate-400">
23+
Link-local
24+
</span>
25+
<span className="text-sm font-medium">
26+
{networkState?.ipv6_link_local}
27+
</span>
28+
</div>
29+
)}
30+
</div>
31+
32+
<div className="space-y-3 pt-2">
33+
{networkState?.ipv6_addresses && networkState?.ipv6_addresses.length > 0 && (
34+
<div className="space-y-3">
35+
<h4 className="text-sm font-semibold">IPv6 Addresses</h4>
36+
{networkState.ipv6_addresses.map(
37+
addr => (
38+
<div
39+
key={addr.address}
40+
className="rounded-md rounded-l-none border border-slate-500/10 border-l-blue-700/50 bg-white p-4 pl-4 backdrop-blur-sm dark:bg-transparent"
41+
>
42+
<div className="grid grid-cols-2 gap-x-8 gap-y-4">
43+
<div className="col-span-2 flex flex-col justify-between">
44+
<span className="text-sm text-slate-600 dark:text-slate-400">
45+
Address
46+
</span>
47+
<span className="text-sm font-medium">{addr.address}</span>
48+
</div>
49+
50+
{addr.valid_lifetime && (
51+
<div className="flex flex-col justify-between">
52+
<span className="text-sm text-slate-600 dark:text-slate-400">
53+
Valid Lifetime
54+
</span>
55+
<span className="text-sm font-medium">
56+
{addr.valid_lifetime === "" ? (
57+
<span className="text-slate-400 dark:text-slate-600">
58+
N/A
59+
</span>
60+
) : (
61+
<LifeTimeLabel lifetime={`${addr.valid_lifetime}`} />
62+
)}
63+
</span>
64+
</div>
65+
)}
66+
{addr.preferred_lifetime && (
67+
<div className="flex flex-col justify-between">
68+
<span className="text-sm text-slate-600 dark:text-slate-400">
69+
Preferred Lifetime
70+
</span>
71+
<span className="text-sm font-medium">
72+
{addr.preferred_lifetime === "" ? (
73+
<span className="text-slate-400 dark:text-slate-600">
74+
N/A
75+
</span>
76+
) : (
77+
<LifeTimeLabel lifetime={`${addr.preferred_lifetime}`} />
78+
)}
79+
</span>
80+
</div>
81+
)}
82+
</div>
83+
</div>
84+
),
85+
)}
86+
</div>
87+
)}
88+
</div>
89+
</div>
90+
</div>
91+
</GridCard>
92+
);
93+
}

ui/src/components/Modal.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ const Modal = React.memo(function Modal({
2020
transition
2121
className="fixed inset-0 bg-gray-500/75 transition-opacity data-closed:opacity-0 data-enter:duration-500 data-leave:duration-200 data-enter:ease-out data-leave:ease-in dark:bg-slate-900/90"
2222
/>
23-
<div className="fixed inset-0 z-20 w-screen overflow-y-auto">
23+
<div className="fixed inset-0 z-20 w-screen overflow-y-auto" style={{
24+
scrollbarGutter: 'stable'
25+
}}>
2426
{/* TODO: This doesn't work well with other-sessions */}
2527
<div className="flex min-h-full items-end justify-center p-4 text-center md:items-baseline md:p-4">
2628
<DialogPanel

ui/src/components/extensions/ATXPowerControl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export function ATXPowerControl() {
107107
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
108108
</Card>
109109
) : (
110-
<Card className="h-[120px] animate-fadeIn">
110+
<Card className="h-[120px] animate-fadeIn opacity-0">
111111
<div className="space-y-4 p-3">
112112
{/* Control Buttons */}
113113
<div className="flex items-center space-x-2">

ui/src/components/extensions/DCPowerControl.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function DCPowerControl() {
6363
<LoadingSpinner className="h-6 w-6 text-blue-500 dark:text-blue-400" />
6464
</Card>
6565
) : (
66-
<Card className="h-[160px] animate-fadeIn">
66+
<Card className="h-[160px] animate-fadeIn opacity-0">
6767
<div className="space-y-4 p-3">
6868
{/* Power Controls */}
6969
<div className="flex items-center space-x-2">

ui/src/components/extensions/SerialConsole.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export function SerialConsole() {
5858
description="Configure your serial console settings"
5959
/>
6060

61-
<Card className="animate-fadeIn">
61+
<Card className="animate-fadeIn opacity-0">
6262
<div className="space-y-4 p-3">
6363
{/* Open Console Button */}
6464
<div className="flex items-center">

ui/src/components/popovers/ExtensionPopover.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export default function ExtensionPopover() {
9292
{renderActiveExtension()}
9393

9494
<div
95-
className="flex animate-fadeIn items-center justify-end space-x-2"
95+
className="flex animate-fadeIn opacity-0 items-center justify-end space-x-2"
9696
style={{
9797
animationDuration: "0.7s",
9898
animationDelay: "0.2s",
@@ -113,7 +113,7 @@ export default function ExtensionPopover() {
113113
title="Extensions"
114114
description="Load and manage your extensions"
115115
/>
116-
<Card className="animate-fadeIn">
116+
<Card className="animate-fadeIn opacity-0" >
117117
<div className="w-full divide-y divide-slate-700/30 dark:divide-slate-600/30">
118118
{AVAILABLE_EXTENSIONS.map(extension => (
119119
<div

0 commit comments

Comments
 (0)