mirror of https://github.com/jetkvm/kvm.git
Improve network setting UI
This commit is contained in:
parent
f24ca5136e
commit
80ae3f7fb3
|
|
@ -186,31 +186,71 @@ export default function SettingsNetworkRoute() {
|
||||||
const settings = prepareSettings(data);
|
const settings = prepareSettings(data);
|
||||||
const dirty = formState.dirtyFields;
|
const dirty = formState.dirtyFields;
|
||||||
|
|
||||||
// These fields will prompt a confirm dialog, all else save immediately
|
// Build list of critical changes for display
|
||||||
const criticalFields = [
|
const changes: { label: string; from: string; to: string }[] = [];
|
||||||
// Label is for the UI, key is the internal key of the field
|
|
||||||
{ label: "IPv4 mode", key: "ipv4_mode" },
|
|
||||||
{ label: "IPv6 mode", key: "ipv6_mode" },
|
|
||||||
{ label: "DHCP client", key: "dhcp_client" },
|
|
||||||
] as { label: string; key: keyof NetworkSettings }[];
|
|
||||||
|
|
||||||
const criticalChanged = criticalFields.some(field => dirty[field.key]);
|
if (dirty.dhcp_client) {
|
||||||
|
changes.push({
|
||||||
|
label: "DHCP client",
|
||||||
|
from: initialSettingsRef.current?.dhcp_client as string,
|
||||||
|
to: data.dhcp_client as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv4_mode) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv4 mode",
|
||||||
|
from: initialSettingsRef.current?.ipv4_mode as string,
|
||||||
|
to: data.ipv4_mode as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv4_static?.address) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv4 address",
|
||||||
|
from: initialSettingsRef.current?.ipv4_static?.address as string,
|
||||||
|
to: data.ipv4_static?.address as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv4_static?.netmask) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv4 netmask",
|
||||||
|
from: initialSettingsRef.current?.ipv4_static?.netmask as string,
|
||||||
|
to: data.ipv4_static?.netmask as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv4_static?.gateway) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv4 gateway",
|
||||||
|
from: initialSettingsRef.current?.ipv4_static?.gateway as string,
|
||||||
|
to: data.ipv4_static?.gateway as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv4_static?.dns) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv4 DNS",
|
||||||
|
from: initialSettingsRef.current?.ipv4_static?.dns.join(", ").toString() ?? "",
|
||||||
|
to: data.ipv4_static?.dns.join(",").toString() ?? "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty.ipv6_mode) {
|
||||||
|
changes.push({
|
||||||
|
label: "IPv6 mode",
|
||||||
|
from: initialSettingsRef.current?.ipv6_mode as string,
|
||||||
|
to: data.ipv6_mode as string,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// If no critical fields are changed, save immediately
|
// If no critical fields are changed, save immediately
|
||||||
if (!criticalChanged) return onSubmit(settings);
|
if (changes.length === 0) return onSubmit(settings);
|
||||||
|
|
||||||
const changes = new Set<{ label: string; from: string; to: string }>();
|
|
||||||
criticalFields.forEach(field => {
|
|
||||||
const { key, label } = field;
|
|
||||||
if (dirty[key]) {
|
|
||||||
const from = initialSettingsRef?.current?.[key] as string;
|
|
||||||
const to = data[key] as string;
|
|
||||||
changes.add({ label, from, to });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// Show confirmation dialog for critical changes
|
||||||
setStagedSettings(settings);
|
setStagedSettings(settings);
|
||||||
setCriticalChanges(Array.from(changes));
|
setCriticalChanges(changes);
|
||||||
setShowCriticalSettingsConfirm(true);
|
setShowCriticalSettingsConfirm(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -263,7 +303,7 @@ export default function SettingsNetworkRoute() {
|
||||||
{networkState?.mac_address} {" "}
|
{networkState?.mac_address} {" "}
|
||||||
</div>
|
</div>
|
||||||
</GridCard>
|
</GridCard>
|
||||||
<Button className="rounded-l-none border-l-blue-900 dark:border-l-blue-600" size="SM" type="button" theme="primary" LeadingIcon={LuCopy} onClick={async () => {
|
<Button className="rounded-l-none border-l-slate-800/30 dark:border-slate-300/20" size="SM" type="button" theme="light" LeadingIcon={LuCopy} onClick={async () => {
|
||||||
if (await copy(networkState?.mac_address || "")) {
|
if (await copy(networkState?.mac_address || "")) {
|
||||||
notifications.success("MAC address copied to clipboard");
|
notifications.success("MAC address copied to clipboard");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -356,7 +396,7 @@ export default function SettingsNetworkRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
|
|
||||||
<SettingsItem title="DHCP client" description="Configure which DHCP client to use (reboot required)">
|
<SettingsItem title="DHCP client" description="Configure which DHCP client to use">
|
||||||
<SelectMenuBasic
|
<SelectMenuBasic
|
||||||
size="SM"
|
size="SM"
|
||||||
options={[
|
options={[
|
||||||
|
|
@ -484,52 +524,43 @@ export default function SettingsNetworkRoute() {
|
||||||
}, 500);
|
}, 500);
|
||||||
}}
|
}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
// close();
|
|
||||||
setShowCriticalSettingsConfirm(false);
|
setShowCriticalSettingsConfirm(false);
|
||||||
}}
|
}}
|
||||||
isConfirming={formState.isSubmitting}
|
isConfirming={formState.isSubmitting}
|
||||||
description={
|
description={
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<p>
|
<div>
|
||||||
This will update the device's network configuration and may briefly
|
<p className="text-sm leading-relaxed text-slate-700 dark:text-slate-300">
|
||||||
disconnect your session.
|
The following network settings will be applied. These changes may require a reboot and cause brief disconnection.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="rounded-md border border-slate-200 bg-slate-50 p-3 dark:border-slate-700 dark:bg-slate-900/40">
|
<div className="space-y-2.5">
|
||||||
<div className="mb-2 text-xs font-semibold tracking-wide text-slate-500 uppercase dark:text-slate-400">
|
<div className="flex items-center justify-between text-[13px] font-medium text-slate-900 dark:text-white">
|
||||||
Pending changes
|
Configuration changes
|
||||||
</div>
|
</div>
|
||||||
<dl className="grid grid-cols-1 gap-y-2">
|
<div className="space-y-2.5">
|
||||||
{criticalChanges.map((c, idx) => (
|
{criticalChanges.map((c, idx) => (
|
||||||
<div key={idx} className="w-full not-last:pb-2">
|
<div key={idx + c.label} className="flex gap-x-2 flex-wrap space-y-1.5 bg-slate-100/50 dark:bg-slate-800/50 border border-slate-800/10 dark:border-slate-300/20 rounded-md py-2 px-3">
|
||||||
<div className="flex items-center gap-2 gap-x-8">
|
<div className="text-xs text-slate-600 dark:text-slate-400">
|
||||||
<dt className="text-sm text-slate-500 dark:text-slate-400">
|
<span>{c.label}</span>
|
||||||
{c.label}
|
</div>
|
||||||
</dt>
|
<div className="flex items-center gap-2.5">
|
||||||
<div className="flex items-center gap-2">
|
<code className="rounded border border-slate-800/20 bg-slate-50 px-1.5 py-1 text-xs text-black font-mono dark:border-slate-300/20 dark:bg-slate-800 dark:text-slate-100">
|
||||||
<span className="rounded-sm bg-slate-200 px-1.5 py-0.5 text-sm font-medium text-slate-900 dark:bg-slate-700 dark:text-slate-100">
|
{c.from || "—"}
|
||||||
{c.from || "—"}
|
</code>
|
||||||
</span>
|
<svg className="size-3.5 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" />
|
||||||
<span className="text-sm text-slate-500 dark:text-slate-400">
|
</svg>
|
||||||
→
|
<code className="rounded border border-slate-800/20 bg-slate-50 px-1.5 py-1 text-xs text-black font-mono dark:border-slate-300/20 dark:bg-slate-800 dark:text-slate-100">
|
||||||
</span>
|
{c.to}
|
||||||
|
</code>
|
||||||
<span className="rounded-sm bg-slate-200 px-1.5 py-0.5 text-sm font-medium text-slate-900 dark:bg-slate-700 dark:text-slate-100">
|
|
||||||
{c.to}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</dl>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="text-sm">
|
|
||||||
If the network settings are invalid,{" "}
|
|
||||||
<strong>the device may become unreachable</strong> and require a factory
|
|
||||||
reset to restore connectivity.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue