fix: resolve cancel and close buttons not working on server modals

This commit is contained in:
xtcnet 2026-03-08 10:29:37 +07:00
parent 36acc3ea65
commit 5f4acb755e
2 changed files with 131 additions and 151 deletions

View file

@ -2,17 +2,19 @@ import {
IconDeviceFloppy, IconDeviceFloppy,
IconX, IconX,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import EasyModal, { useModal } from "ez-modal-react";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import Modal from "react-bootstrap/Modal";
import type { WgInterface } from "src/api/backend/wireguard"; import type { WgInterface } from "src/api/backend/wireguard";
import { Button } from "src/components";
interface WireGuardLinkedServersModalProps { interface WireGuardLinkedServersModalProps {
wgInterface: WgInterface; wgInterface: WgInterface;
allInterfaces: WgInterface[]; allInterfaces: WgInterface[];
onHide?: () => void;
resolve?: (data: { linked_servers: number[] }) => void;
} }
function WireGuardLinkedServersModal({ wgInterface, allInterfaces, onHide, resolve }: WireGuardLinkedServersModalProps) { const WireGuardLinkedServersModal = EasyModal.create(({ wgInterface, allInterfaces }: WireGuardLinkedServersModalProps) => {
const modal = useModal<any>();
// A map or set to manage checked status easily // A map or set to manage checked status easily
const [selected, setSelected] = useState<Set<number>>(new Set()); const [selected, setSelected] = useState<Set<number>>(new Set());
@ -33,77 +35,64 @@ function WireGuardLinkedServersModal({ wgInterface, allInterfaces, onHide, resol
}; };
const onSave = () => { const onSave = () => {
if (resolve) { modal.resolve({ linked_servers: Array.from(selected) });
resolve({ linked_servers: Array.from(selected) }); modal.hide();
} };
if (onHide) {
onHide(); const handleClose = () => {
} modal.resolve(null);
modal.hide();
}; };
const availableServers = allInterfaces.filter(i => i.id !== wgInterface.id); const availableServers = allInterfaces.filter(i => i.id !== wgInterface.id);
return ( return (
<div className="modal modal-blur fade show d-block" tabIndex={-1}> <Modal show={modal.visible} onHide={handleClose} backdrop="static">
<div className="modal-dialog modal-dialog-centered" role="document"> <Modal.Header closeButton>
<div className="modal-content"> <Modal.Title>
<div className="modal-header"> Linked Servers for {wgInterface.name}
<h5 className="modal-title"> </Modal.Title>
Linked Servers for {wgInterface.name} </Modal.Header>
</h5> <Modal.Body>
<button <p className="text-muted mb-3">
type="button" Select the WireGuard servers that clients from <strong>{wgInterface.name}</strong> will be allowed to communicate with.
className="btn-close" </p>
onClick={onHide}
aria-label="Close"
></button>
</div>
<div className="modal-body">
<p className="text-muted mb-3">
Select the WireGuard servers that clients from <strong>{wgInterface.name}</strong> will be allowed to communicate with.
</p>
{availableServers.length === 0 ? ( {availableServers.length === 0 ? (
<div className="alert alert-info border-0 mb-0"> <div className="alert alert-info border-0 mb-0">
There are no other servers available to link to. There are no other servers available to link to.
</div>
) : (
<div className="list-group list-group-flush mb-3">
{availableServers.map(server => (
<label key={server.id} className="list-group-item d-flex align-items-center cursor-pointer px-0">
<input
className="form-check-input mt-0 me-3"
type="checkbox"
checked={selected.has(server.id)}
onChange={() => toggleServer(server.id)}
/>
<div className="flex-grow-1">
<div><strong>{server.name}</strong></div>
<div className="text-muted small border border-light border-1 bg-light rounded mt-1 p-1 d-inline-block">
Subnet: {server.ipv4Cidr}
</div>
</div>
</label>
))}
</div>
)}
</div> </div>
<div className="modal-footer"> ) : (
<button <div className="list-group list-group-flush mb-3">
type="button" {availableServers.map(server => (
className="btn btn-link link-secondary me-auto" <label key={server.id} className="list-group-item d-flex align-items-center cursor-pointer px-0">
onClick={onHide} <input
> className="form-check-input mt-0 me-3"
<IconX size={16} className="me-1" /> Cancel type="checkbox"
</button> checked={selected.has(server.id)}
<button type="button" className="btn btn-primary" onClick={onSave} disabled={availableServers.length === 0 && selected.size === 0}> onChange={() => toggleServer(server.id)}
<IconDeviceFloppy size={16} className="me-1" /> Save Links />
</button> <div className="flex-grow-1">
<div><strong>{server.name}</strong></div>
<div className="text-muted small border border-light border-1 bg-light rounded mt-1 p-1 d-inline-block">
Subnet: {server.ipv4Cidr}
</div>
</div>
</label>
))}
</div> </div>
</div> )}
</div> </Modal.Body>
</div> <Modal.Footer>
<Button type="button" data-bs-dismiss="modal" onClick={handleClose}>
<IconX size={16} className="me-1" /> Cancel
</Button>
<Button type="button" className="ms-auto btn-primary" onClick={onSave} disabled={availableServers.length === 0 && selected.size === 0}>
<IconDeviceFloppy size={16} className="me-1" /> Save Links
</Button>
</Modal.Footer>
</Modal>
); );
} });
export default WireGuardLinkedServersModal; export default WireGuardLinkedServersModal;

View file

@ -2,19 +2,21 @@ import {
IconDeviceFloppy, IconDeviceFloppy,
IconX, IconX,
} from "@tabler/icons-react"; } from "@tabler/icons-react";
import EasyModal, { useModal } from "ez-modal-react";
import { useEffect } from "react"; import { useEffect } from "react";
import Modal from "react-bootstrap/Modal";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import { Button } from "src/components";
interface WireGuardServerModalProps { interface WireGuardServerModalProps {
wgInterface?: any; wgInterface?: any;
onHide?: () => void;
resolve?: (data: any) => void;
} }
const WG_DEFAULT_MTU = 1420; const WG_DEFAULT_MTU = 1420;
const WG_DEFAULT_DNS = "1.1.1.1, 8.8.8.8"; const WG_DEFAULT_DNS = "1.1.1.1, 8.8.8.8";
function WireGuardServerModal({ wgInterface, onHide, resolve }: WireGuardServerModalProps) { const WireGuardServerModal = EasyModal.create(({ wgInterface }: WireGuardServerModalProps) => {
const modal = useModal<any>();
const { const {
register, register,
handleSubmit, handleSubmit,
@ -44,95 +46,84 @@ function WireGuardServerModal({ wgInterface, onHide, resolve }: WireGuardServerM
...data, ...data,
mtu: Number(data.mtu) || WG_DEFAULT_MTU, mtu: Number(data.mtu) || WG_DEFAULT_MTU,
}; };
if (resolve) { modal.resolve(submitData);
resolve(submitData); modal.hide();
} };
if (onHide) {
onHide(); const handleClose = () => {
} modal.resolve(null);
modal.hide();
}; };
return ( return (
<div className="modal modal-blur fade show d-block" tabIndex={-1}> <Modal show={modal.visible} onHide={handleClose} backdrop="static">
<div className="modal-dialog modal-dialog-centered" role="document"> <form onSubmit={handleSubmit(onSubmit)}>
<form className="modal-content" onSubmit={handleSubmit(onSubmit)}> <Modal.Header closeButton>
<div className="modal-header"> <Modal.Title>
<h5 className="modal-title"> {wgInterface ? `Edit Server: ${wgInterface.name}` : "New WireGuard Server"}
{wgInterface ? `Edit Server: ${wgInterface.name}` : "New WireGuard Server"} </Modal.Title>
</h5> </Modal.Header>
<button <Modal.Body>
type="button" <div className="mb-3">
className="btn-close" <label className="form-label required">Host / Endpoint IP</label>
onClick={onHide} <input
aria-label="Close" type="text"
></button> className={`form-control ${errors.host ? "is-invalid" : ""}`}
placeholder="e.g., 203.0.113.1"
{...register("host", { required: "Host IP is required" })}
/>
{errors.host && (
<div className="invalid-feedback">{errors.host.message as string}</div>
)}
<small className="form-hint">
The public IP or hostname domain that clients will use to connect.
</small>
</div> </div>
<div className="modal-body">
<div className="mb-3">
<label className="form-label required">Host / Endpoint IP</label>
<input
type="text"
className={`form-control ${errors.host ? "is-invalid" : ""}`}
placeholder="e.g., 203.0.113.1"
{...register("host", { required: "Host IP is required" })}
/>
{errors.host && (
<div className="invalid-feedback">{errors.host.message as string}</div>
)}
<small className="form-hint">
The public IP or hostname domain that clients will use to connect.
</small>
</div>
<div className="mb-3"> <div className="mb-3">
<label className="form-label">Client DNS Servers</label> <label className="form-label">Client DNS Servers</label>
<input <input
type="text" type="text"
className="form-control" className="form-control"
placeholder={WG_DEFAULT_DNS} placeholder={WG_DEFAULT_DNS}
{...register("dns")} {...register("dns")}
/> />
<small className="form-hint">Comma separated list. Assigned to clients.</small> <small className="form-hint">Comma separated list. Assigned to clients.</small>
</div>
<div className="mb-3">
<label className="form-label">MTU</label>
<input
type="number"
className="form-control"
placeholder={WG_DEFAULT_MTU.toString()}
{...register("mtu")}
/>
</div>
<div className="mb-3">
<label className="form-label">Client Isolation</label>
<label className="form-check form-switch">
<input
className="form-check-input"
type="checkbox"
{...register("isolate_clients")}
/>
<span className="form-check-label">Prevent clients on this server from communicating with each other</span>
</label>
</div>
</div> </div>
<div className="modal-footer">
<button <div className="mb-3">
type="button" <label className="form-label">MTU</label>
className="btn btn-link link-secondary me-auto" <input
onClick={onHide} type="number"
> className="form-control"
<IconX size={16} className="me-1" /> Cancel placeholder={WG_DEFAULT_MTU.toString()}
</button> {...register("mtu")}
<button type="submit" className="btn btn-primary"> />
<IconDeviceFloppy size={16} className="me-1" /> Save
</button>
</div> </div>
</form>
</div> <div className="mb-3">
</div> <label className="form-label">Client Isolation</label>
<label className="form-check form-switch">
<input
className="form-check-input"
type="checkbox"
{...register("isolate_clients")}
/>
<span className="form-check-label">Prevent clients on this server from communicating with each other</span>
</label>
</div>
</Modal.Body>
<Modal.Footer>
<Button type="button" data-bs-dismiss="modal" onClick={handleClose}>
<IconX size={16} className="me-1" /> Cancel
</Button>
<Button type="submit" className="ms-auto btn-primary">
<IconDeviceFloppy size={16} className="me-1" /> Save
</Button>
</Modal.Footer>
</form>
</Modal>
); );
} });
export default WireGuardServerModal; export default WireGuardServerModal;