ATC_SIMPLE/FRONTEND/src/components/FormAddEdit.tsx

669 lines
20 KiB
TypeScript

import {
ActionIcon,
Box,
Button,
Flex,
Group,
Modal,
NumberInput,
PasswordInput,
Select,
Table,
TextInput,
} from "@mantine/core";
import { useForm } from "@mantine/form";
import { useEffect, useState } from "react";
import type { TLine, TStation } from "../untils/types";
import DialogConfirm from "./DialogConfirm";
import axios from "axios";
import { notifications } from "@mantine/notifications";
import { IconInputX } from "@tabler/icons-react";
const apiUrl = import.meta.env.VITE_BACKEND_URL;
const lineInit = {
port: 0,
lineNumber: 0,
lineClear: 0,
station_id: 0,
apc_name: "",
};
const StationSetting = ({
isOpen,
isEdit,
onClose,
dataStation,
setStations,
setActiveTab,
stations,
}: {
isOpen: boolean;
isEdit: boolean;
onClose: () => void;
dataStation?: TStation;
setStations: (value: React.SetStateAction<TStation[]>) => void;
setActiveTab: (value: string) => void;
stations: TStation[];
}) => {
const [lines, setLines] = useState<TLine[]>([lineInit]);
const [openConfirm, setOpenConfirm] = useState<boolean>(false);
const ipRegex =
/(\b25[0-5]|\b2[0-4][0-9]|\b1[0-9]{2}|\b[1-9]?[0-9])\.(\b25[0-5]|\b2[0-4][0-9]|\b1[0-9]{2}|\b[1-9]?[0-9])\.(\b25[0-5]|\b2[0-4][0-9]|\b1[0-9]{2}|\b[1-9]?[0-9])\.(\b25[0-5]|\b2[0-4][0-9]|\b1[0-9]{2}|\b[1-9]?[0-9])\b/g;
const form = useForm<TStation>({
initialValues: dataStation,
validate: (values: TStation) => ({
ip: !ipRegex.test(values.ip) ? "IP address invalid" : null,
}),
});
useEffect(() => {
if (dataStation) {
form.setFieldValue("name", dataStation.name);
form.setFieldValue("ip", dataStation.ip);
form.setFieldValue("port", dataStation.port);
form.setFieldValue("netmask", dataStation.netmask);
form.setFieldValue("network", dataStation.network);
form.setFieldValue("gateway", dataStation.gateway);
form.setFieldValue("tftp_ip", dataStation.tftp_ip);
form.setFieldValue("apc_1_ip", dataStation.apc_1_ip);
form.setFieldValue("apc_1_port", dataStation.apc_1_port);
form.setFieldValue("apc_1_username", dataStation.apc_1_username);
form.setFieldValue("apc_1_password", dataStation.apc_1_password);
form.setFieldValue("apc_2_ip", dataStation.apc_2_ip);
form.setFieldValue("apc_2_port", dataStation.apc_2_port);
form.setFieldValue("apc_2_username", dataStation.apc_2_username);
form.setFieldValue("apc_2_password", dataStation.apc_2_password);
form.setFieldValue("switch_control_ip", dataStation.switch_control_ip);
form.setFieldValue(
"switch_control_port",
dataStation.switch_control_port
);
form.setFieldValue(
"switch_control_username",
dataStation.switch_control_username
);
form.setFieldValue(
"switch_control_password",
dataStation.switch_control_password
);
const dataLine = dataStation.lines.map((value) => ({
id: value.id,
lineNumber: value.line_number || 0,
port: value.port,
lineClear: value.line_clear || 0,
apc_name: value.apc_name,
outlet: value.outlet,
station_id: value.station_id,
}));
setLines(dataLine);
}
}, [dataStation]);
useEffect(() => {
if (lines.length > 0) {
const lastLine = lines[lines.length - 1];
if (lastLine?.lineNumber || lastLine?.port)
setLines((pre) => [...pre, lineInit]);
}
}, [lines]);
useEffect(() => {
if (!isOpen) {
setLines([lineInit]);
setOpenConfirm(false);
form.reset();
}
}, [isOpen]);
const renderLinesTable = () => {
const rows = lines?.map((row: TLine, index: number) => {
return (
<Table.Tr key={index}>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
<NumberInput
value={row?.lineNumber}
onChange={(e) =>
setLines((pre) =>
pre.map((value, i) =>
i === index ? { ...value, lineNumber: Number(e!) } : value
)
)
}
/>
</Table.Td>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
<NumberInput
value={row?.port}
onChange={(e) =>
setLines((pre) =>
pre.map((value, i) =>
i === index ? { ...value, port: Number(e!) } : value
)
)
}
/>
</Table.Td>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
<NumberInput
value={row?.lineClear}
onChange={(e) =>
setLines((pre) =>
pre.map((value, i) =>
i === index ? { ...value, lineClear: Number(e!) } : value
)
)
}
/>
</Table.Td>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
<Select
data={[
{ label: "APC1", value: "apc_1" },
{ label: "APC2", value: "apc_2" },
]}
value={row?.apc_name}
onChange={(e) =>
setLines((pre) =>
pre.map((value, i) =>
i === index ? { ...value, apc_name: e! } : value
)
)
}
/>
</Table.Td>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
<NumberInput
value={row?.outlet}
onChange={(e) =>
setLines((pre) =>
pre.map((value, i) =>
i === index ? { ...value, outlet: Number(e!) } : value
)
)
}
/>
</Table.Td>
<Table.Td fz={"sm"} p="3px" ta={"center"} fw={700}>
{row?.lineNumber ? (
<ActionIcon
title="Remove line"
variant="outline"
color="red"
onClick={() => {
setLines((pre) => [
...pre.slice(0, index),
...pre.slice(index + 1, lines.length),
]);
}}
>
<IconInputX />
</ActionIcon>
) : (
""
)}
</Table.Td>
</Table.Tr>
);
});
return rows;
};
const handleSave = async () => {
try {
const payload = {
...form.values,
lines: lines.filter((el) => el.lineNumber && el.port),
};
if (isEdit) payload.id = dataStation?.id || 0;
const url = isEdit ? "api/stations/update" : "api/stations/create";
const response = await axios.post(apiUrl + url, payload);
if (response.data.status) {
if (response.data.data) {
const station = response.data.data[0];
setStations((pre) =>
isEdit
? pre.map((el) =>
el.id === station.id ? { ...el, ...station } : el
)
: [...pre, station]
);
}
notifications.show({
title: "Success",
message: response.data.message,
color: "green",
});
onClose();
} else
notifications.show({
title: "Error",
message: response.data.message,
color: "red",
});
} catch (err) {
console.log(err);
notifications.show({
title: "Error",
message: "Error save station",
color: "red",
});
}
};
const handleDelete = async () => {
try {
const response = await axios.post(apiUrl + "api/stations/delete", {
id: dataStation?.id,
});
if (response.data.status) {
const listStations = stations.filter((el) => el.id !== dataStation?.id);
setStations(listStations);
setActiveTab(
listStations.length ? listStations[0]?.id.toString() : "0"
);
notifications.show({
title: "Success",
message: response.data.message,
color: "green",
});
}
} catch (error) {
console.log(error);
notifications.show({
title: "Error",
message: "Error delete station",
color: "red",
});
}
};
return (
<Box>
<Modal
title={
<div
style={{
display: "flex",
alignItems: "center",
gap: "10px",
}}
>
{isEdit ? "Edit Station" : "Add Station"}{" "}
<Button
style={{ height: "30px" }}
color="green"
onClick={() => {
handleSave();
}}
>
Save
</Button>
{isEdit && (
<Button
style={{ height: "30px" }}
color="red"
onClick={() => {
setOpenConfirm(true);
}}
>
Delete
</Button>
)}
</div>
}
size={"80%"}
style={{ position: "absolute", left: 0 }}
centered
opened={isOpen}
onClose={() => {
onClose();
}}
>
<Flex justify={"space-between"} gap={"sm"}>
{/* Station info */}
<Box w={"40%"}>
<TextInput
required
name="name"
label="Name"
size="sm"
value={form.values.name || ""}
onChange={(e) => form.setFieldValue("name", e.target.value)}
/>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
gap: "6px",
}}
>
<TextInput
required
label="IP"
size="sm"
value={form.values.ip || ""}
onChange={(e) => form.setFieldValue("ip", e.target.value)}
/>
<NumberInput
required
size="sm"
label="Port"
value={form.values.port || ""}
onChange={(e) => form.setFieldValue("port", Number(e!))}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
gap: "6px",
}}
>
<TextInput
value={form.values.netmask || ""}
label={"Netmask"}
onChange={(e) => {
form.setFieldValue("netmask", e.target.value);
}}
/>
<TextInput
value={form.values.network || ""}
label={"Network"}
onChange={(e) => {
form.setFieldValue("network", e.target.value);
}}
/>
</div>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
gap: "6px",
}}
>
<TextInput
value={form.values.gateway || ""}
label={"Gateway"}
onChange={(e) => {
form.setFieldValue("gateway", e.target.value);
}}
/>
<TextInput
value={form.values.tftp_ip || ""}
label={"TFTP IP"}
onChange={(e) => {
form.setFieldValue("tftp_ip", e.target.value);
}}
/>
</div>
<div style={{}}>
<Group
mt={"md"}
title="APC 1"
style={{
border: "1px solid #e1e1e1",
padding: "10px",
borderRadius: "5px",
}}
>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="APC1 IP"
size="xs"
w={"60%"}
value={form.values.apc_1_ip || ""}
onChange={(e) =>
form.setFieldValue("apc_1_ip", e.target.value)
}
/>
<NumberInput
label="APC1 Port"
w={"39%"}
size="xs"
value={form.values.apc_1_port || ""}
onChange={(e) =>
form.setFieldValue("apc_1_port", parseInt(e.toString()))
}
/>
</Box>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="Username"
w={"49%"}
size="xs"
autoComplete="new-username"
value={form.values.apc_1_username || ""}
onChange={(e) =>
form.setFieldValue("apc_1_username", e.target.value)
}
/>
<PasswordInput
label="Password"
w={"49%"}
size="xs"
autoComplete="new-password"
value={form.values.apc_1_password || ""}
onChange={(e) =>
form.setFieldValue("apc_1_password", e.target.value)
}
/>
</Box>
</Group>
{/* APC2 configuration */}
<Group
mt={"md"}
title="APC 2"
style={{
border: "1px solid #e1e1e1",
padding: "10px",
borderRadius: "5px",
}}
>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="APC2 IP"
w={"60%"}
size="xs"
value={form.values.apc_2_ip || ""}
onChange={(e) =>
form.setFieldValue("apc_2_ip", e.target.value)
}
/>
<NumberInput
label="APC2 Port"
w={"39%"}
size="xs"
value={form.values.apc_2_port || ""}
onChange={(e) =>
form.setFieldValue("apc_2_port", parseInt(e.toString()))
}
/>
</Box>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="Username"
w={"49%"}
size="xs"
value={form.values.apc_2_username || ""}
onChange={(e) =>
form.setFieldValue("apc_2_username", e.target.value)
}
/>
<PasswordInput
label="Password"
w={"49%"}
size="xs"
value={form.values.apc_2_password || ""}
onChange={(e) =>
form.setFieldValue("apc_2_password", e.target.value)
}
/>
</Box>
</Group>
{/* APC1 configuration */}
<Group
mt={"md"}
title="Switch control"
style={{
border: "1px solid #e1e1e1",
padding: "10px",
borderRadius: "5px",
}}
>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="Switch IP"
size="xs"
w={"60%"}
value={form.values.switch_control_ip || ""}
onChange={(e) =>
form.setFieldValue("switch_control_ip", e.target.value)
}
/>
<NumberInput
label="Switch Port"
w={"39%"}
size="xs"
value={form.values.switch_control_port || ""}
onChange={(e) =>
form.setFieldValue(
"switch_control_port",
parseInt(e.toString())
)
}
/>
</Box>
<Box
w={"100%"}
style={{
display: "flex",
justifyContent: "space-between",
}}
>
<TextInput
label="Username"
size="xs"
w={"49%"}
value={form.values.switch_control_username || ""}
onChange={(e) =>
form.setFieldValue(
"switch_control_username",
e.target.value
)
}
/>
<PasswordInput
label="Password"
size="xs"
w={"49%"}
value={form.values.switch_control_password || ""}
onChange={(e) =>
form.setFieldValue(
"switch_control_password",
e.target.value
)
}
/>
</Box>
</Group>
</div>
</Box>
{/* Lines */}
<Box w={"60%"}>
<Table
verticalSpacing="xs"
horizontalSpacing="lg"
striped
highlightOnHover
withTableBorder
withColumnBorders
>
<Table.Thead>
<Table.Tr>
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
Line number
</Table.Th>
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
Port
</Table.Th>
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
Clear line
</Table.Th>
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
APC
</Table.Th>
<Table.Th fz={"sm"} w={"15%"} ta={"center"}>
Outlet
</Table.Th>
<Table.Th fz={"sm"} w={"10%"} ta={"center"}></Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>{renderLinesTable()}</Table.Tbody>
</Table>
</Box>
</Flex>
</Modal>
<DialogConfirm
opened={openConfirm}
close={() => {
setOpenConfirm(false);
}}
message={"Are you sure delete this station?"}
handle={() => {
handleDelete();
setOpenConfirm(false);
onClose();
}}
centered={true}
/>
</Box>
);
};
export default StationSetting;