From 54ed6c95e8b7177b29f0b8f1db3bf1c96c5badf8 Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Thu, 30 Oct 2025 09:56:09 +0700 Subject: [PATCH] Update --- .../app/controllers/stations_controller.ts | 59 +++++++++++-------- FRONTEND/.gitignore | 6 ++ FRONTEND/src/App.tsx | 30 ++++++++-- .../components/Authentication/LoginPage.tsx | 8 ++- FRONTEND/src/components/DragTabs.tsx | 26 ++++---- FRONTEND/src/components/FormAddEdit.tsx | 11 +++- FRONTEND/src/context/SocketContext.tsx | 3 +- FRONTEND/src/untils/helper.ts | 12 ++++ 8 files changed, 111 insertions(+), 44 deletions(-) diff --git a/BACKEND/app/controllers/stations_controller.ts b/BACKEND/app/controllers/stations_controller.ts index e0a7ec2..530ac83 100644 --- a/BACKEND/app/controllers/stations_controller.ts +++ b/BACKEND/app/controllers/stations_controller.ts @@ -8,38 +8,39 @@ export default class StationsController { } public async store({ request, response }: HttpContext) { - let payload = request.only(Array.from(Station.$columnsDefinitions.keys())) - let lines: Line[] = request.body().lines || [] + const payload = request.only(Array.from(Station.$columnsDefinitions.keys())) + const lines: Line[] = request.body().lines || [] try { - const stationName = await Station.findBy('name', payload.name) - if (stationName) return response.status(400).json({ message: 'Station name exist!' }) + // Kiểm tra trùng name hoặc ip + if (await Station.findBy('name', payload.name)) + return response.status(400).json({ message: 'Station name exist!' }) - const stationIP = await Station.findBy('ip', payload.ip) - if (stationIP) return response.status(400).json({ message: 'Ip of station is exist!' }) + if (await Station.findBy('ip', payload.ip)) + return response.status(400).json({ message: 'Ip of station is exist!' }) + // Tạo station const station = await Station.create(payload) + // Xử lý lines (chờ từng dòng) const newLines: Line[] = [] - if (lines && Array.isArray(lines)) { - lines.forEach(async (line) => { - if (line.id) { - const value = await Line.find(line.id) - if (value) { - Object.assign(value, line) - await value.save() - newLines.push(value) - } else { - const value1 = await Line.create({ ...line, stationId: station.id }) - newLines.push(value1) - } - } else { - const value2 = await Line.create({ ...line, stationId: station.id }) - newLines.push(value2) + for (const line of lines) { + if (line.id) { + const existing = await Line.find(line.id) + if (existing) { + Object.assign(existing, line) + await existing.save() + newLines.push(existing) + continue } - }) + } + + // Tạo mới nếu không tồn tại + const created = await Line.create({ ...line, stationId: station.id }) + newLines.push(created) } + // Lấy lại station kèm lines const resStation = await Station.query().where('id', station.id).preload('lines') return response.created({ @@ -48,7 +49,12 @@ export default class StationsController { data: resStation.map((el) => ({ ...el.$original, lines: newLines })), }) } catch (error) { - return response.badRequest({ error: error, message: 'Station create failed', status: false }) + console.error(error) + return response.badRequest({ + error, + message: 'Station create failed', + status: false, + }) } } @@ -65,6 +71,13 @@ export default class StationsController { let lines: Line[] = request.body().lines || [] try { + // Kiểm tra trùng name hoặc ip + if (await Station.findBy('name', payload.name)) + return response.status(400).json({ message: 'Station name exist!' }) + + if (await Station.findBy('ip', payload.ip)) + return response.status(400).json({ message: 'Ip of station is exist!' }) + const station = await Station.find(request.body().id) // If the station does not exist, return a 404 response diff --git a/FRONTEND/.gitignore b/FRONTEND/.gitignore index a547bf3..ef57cd0 100644 --- a/FRONTEND/.gitignore +++ b/FRONTEND/.gitignore @@ -22,3 +22,9 @@ dist-ssr *.njsproj *.sln *.sw? + +# Secrets +.env +.env.local +.env.production.local +.env.development.local diff --git a/FRONTEND/src/App.tsx b/FRONTEND/src/App.tsx index b4000a4..825366c 100644 --- a/FRONTEND/src/App.tsx +++ b/FRONTEND/src/App.tsx @@ -37,6 +37,7 @@ import ModalTerminal from "./components/ModalTerminal"; import PageLogin from "./components/Authentication/LoginPage"; import DrawerLogs from "./components/DrawerLogs"; import DraggableTabs from "./components/DragTabs"; +import { isJsonString } from "./untils/helper"; const apiUrl = import.meta.env.VITE_BACKEND_URL; @@ -46,11 +47,15 @@ const apiUrl = import.meta.env.VITE_BACKEND_URL; function App() { const user = useMemo(() => { return localStorage.getItem("user") && - typeof localStorage.getItem("user") === "string" + isJsonString(localStorage.getItem("user")) ? JSON.parse(localStorage.getItem("user") || "") : null; }, []); - if (!user) window.location.href = "/"; + + if (!user) { + localStorage.removeItem("user"); + window.location.href = "/"; + } document.title = "Automation Test"; const { socket } = useSocket(); @@ -484,6 +489,8 @@ function App() { setLoadingTerminal(true); }, 100); }} + setActive={setActiveTab} + active={activeTab} /> - setActiveTab(stations.length ? stations[0]?.id.toString() : "0") - } + setActiveTab={(id: string) => { + setActiveTab(id); + setLoadingTerminal(false); + setTimeout(() => { + setLoadingTerminal(true); + }, 100); + }} + stations={stations} /> { + return localStorage.getItem("user") && + isJsonString(localStorage.getItem("user")) + ? JSON.parse(localStorage.getItem("user") || "") + : null; + }, []); + return ( diff --git a/FRONTEND/src/components/Authentication/LoginPage.tsx b/FRONTEND/src/components/Authentication/LoginPage.tsx index 59c0be1..6ea65c4 100644 --- a/FRONTEND/src/components/Authentication/LoginPage.tsx +++ b/FRONTEND/src/components/Authentication/LoginPage.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { Anchor, Image, Paper, Text } from "@mantine/core"; import Login from "./Login"; import Register from "./Register"; @@ -7,6 +7,12 @@ import classes from "./AuthenticationImage.module.css"; export const PageLogin = () => { const [isRegister, setIsRegister] = useState(false); + useEffect(() => { + if (localStorage.getItem("user")) { + localStorage.removeItem("user"); + } + }, []); + return (
) => void; setIsOpenAddStation: (value: React.SetStateAction) => void; setStationEdit: (value: React.SetStateAction) => void; + active: string; + setActive: (value: React.SetStateAction) => void; } function SortableTab({ @@ -99,6 +101,8 @@ export default function DraggableTabs({ setIsEditStation, setIsOpenAddStation, setStationEdit, + active, + setActive, }: DraggableTabsProps) { const user = useMemo(() => { return localStorage.getItem("user") && @@ -109,9 +113,9 @@ export default function DraggableTabs({ const [tabs, setTabs] = useState(tabsData); const [isChangeTab, setIsChangeTab] = useState(false); const [isSetActive, setIsSetActive] = useState(false); - const [active, setActive] = useState( - tabsData?.length > 0 ? tabsData[0]?.id.toString() : null - ); + // const [active, setActive] = useState( + // tabsData?.length > 0 ? tabsData[0]?.id.toString() : null + // ); const sensors = useSensors(useSensor(PointerSensor)); @@ -119,10 +123,12 @@ export default function DraggableTabs({ useEffect(() => { if (isChangeTab) { setTabs((pre) => - pre.map((t) => { - const updatedTab = tabsData.find((td) => td.id === t.id); - return updatedTab ? updatedTab : t; - }) + pre + .map((t) => { + const updatedTab = tabsData.find((td) => td.id === t.id); + return updatedTab ? updatedTab : t; + }) + .filter((t) => (tabsData.find((td) => td.id === t.id) ? true : false)) ); } else { const saved = localStorage.getItem(storageKey); @@ -193,7 +199,7 @@ export default function DraggableTabs({ setIsChangeTab(false); setIsSetActive(false); setTabs([]); - setActive(null); + setActive("0"); }; }, []); @@ -208,7 +214,7 @@ export default function DraggableTabs({ onChange={(val) => { setIsChangeTab(true); onChange(val); - setActive(val); + setActive(val || "0"); }} w={w} > @@ -242,7 +248,7 @@ export default function DraggableTabs({ ))} - + {Number(active) ? ( void; dataStation?: TStation; setStations: (value: React.SetStateAction) => void; - setActiveTab: () => void; + setActiveTab: (value: string) => void; + stations: TStation[]; }) => { const [lines, setLines] = useState([lineInit]); const [openConfirm, setOpenConfirm] = useState(false); @@ -258,8 +260,11 @@ const StationSetting = ({ id: dataStation?.id, }); if (response.data.status) { - setStations((pre) => pre.filter((el) => el.id !== dataStation?.id)); - setActiveTab(); + 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, diff --git a/FRONTEND/src/context/SocketContext.tsx b/FRONTEND/src/context/SocketContext.tsx index a215894..8ceb005 100644 --- a/FRONTEND/src/context/SocketContext.tsx +++ b/FRONTEND/src/context/SocketContext.tsx @@ -8,6 +8,7 @@ import React, { import { io, Socket } from "socket.io-client"; import { SOCKET_EVENTS } from "../untils/constanst"; import { notifications } from "@mantine/notifications"; +import { isJsonString } from "../untils/helper"; interface ISocketContext { socket: Socket | null; @@ -23,7 +24,7 @@ export const SocketProvider: React.FC<{ children: React.ReactNode }> = ({ const [socket, setSocket] = useState(null); const user = useMemo(() => { return localStorage.getItem("user") && - typeof localStorage.getItem("user") === "string" + isJsonString(localStorage.getItem("user")) ? JSON.parse(localStorage.getItem("user") || "") : null; }, []); diff --git a/FRONTEND/src/untils/helper.ts b/FRONTEND/src/untils/helper.ts index ab956b4..1798806 100644 --- a/FRONTEND/src/untils/helper.ts +++ b/FRONTEND/src/untils/helper.ts @@ -7,3 +7,15 @@ export const passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&#])[A-Za-z\d@$!%*?&#]{8,}$/; export const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; + +export function isJsonString(str: string | null) { + if (typeof str !== "string") return false; + try { + const parsed = JSON.parse(str); + // Kiểm tra xem parsed có phải là object hoặc array thật sự + return parsed !== null && typeof parsed === "object"; + } catch (e) { + console.log("Error isJsonString", e); + return false; + } +}