담당자
diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx
index 83ce2c2..dccec77 100644
--- a/src/pages/Settings.jsx
+++ b/src/pages/Settings.jsx
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import Modal from "../components/Modal";
import ZoneInfoBox from "../components/ZoneInfoBox";
import axios from "axios";
+import FacilityModal from "../components/FacilityModal";
/* ────────────────────────────────
1. 센서 타입 → 한글 명/분류 매핑
──────────────────────────────── */
@@ -27,33 +28,12 @@ const toKoName = (type) => {
export default function Settings() {
const [sensorInfo, setSensorInfo] = useState();
- const [isModalOpen, setModalOpen] = useState(false);
+ const [selectedZone, setSelectedZone] = useState();
+ const [isSensorModalOpen, setSensorModalOpen] = useState(false);
+ const [isFacilityModalOpen, setFacilityModalOpen] = useState(false);
const [zoneList, setZoneList] = useState([]);
useEffect(() => {
- // axios
- // .get("/api/zones")
- // .then((res) => console.log(res))
- // .catch((e) => console.log(e));
-
- // axios.get(`http://localhost:8080/api/zones`) // ← GET /api/zones
- // .then((res) => {
- // const list = res.data.map((z) => ({
- // id: z.zoneId, // ← key 용
- // title: z.zoneName, // ← 화면에 찍힐 이름
- // // get 요청에서 데이터가 없으면 빈 배열 혹은 "" 할당해주기
- // env_sensor: z.env_sensor || [],
- // fac_sensor: z.fac_sensor || [],
- // master: z.master || "",
- // }));
- // setZoneList(list);
- // })
- // .catch((e) => console.error(e));
- // // db에 존 데이터가 없을 경우 초기 예시 데이터 보여줌
- // if (zoneList.length === 0) {
- // setZoneList(initialZoneList);
- // }
-
/* ────────────────────────────────
2. ① 공간 + ② 센서를 한 번에 받아서 매핑
───────────────────────────────── */
@@ -103,9 +83,14 @@ export default function Settings() {
.catch(console.error);
}, []);
- const handleOpenModal = (zoneName, sensorName, thres) => {
+ const handleOpenSensorModal = (zoneName, sensorName, thres) => {
setSensorInfo({ zoneName, sensorName, thres });
- setModalOpen(true);
+ setSensorModalOpen(true);
+ };
+
+ const handleOpenFacilityModal = (zoneName) => {
+ setSelectedZone(zoneName);
+ setFacilityModalOpen(true);
};
const handleThresUpdate = (newValue) => {
@@ -122,7 +107,12 @@ export default function Settings() {
});
console.log(li);
setZoneList(li);
- setModalOpen(false);
+ setSensorModalOpen(false);
+ };
+
+ const handleFacilityUpdate = (newValue) => {
+ console.log(`공간명: ${selectedZone} 설비명: ${newValue}`);
+ /* TODO :: 설비 목록 업데이트하기 */
};
const handleAddZone = async (newZone) => {
@@ -160,14 +150,25 @@ export default function Settings() {
return (
<>
setModalOpen(false)}
+ isOpen={isSensorModalOpen}
+ onClose={() => setSensorModalOpen(false)}
sensorInfo={sensorInfo}
onUpdate={handleThresUpdate}
/>
+ setFacilityModalOpen(false)}
+ zoneInfo={selectedZone}
+ onUpdate={handleFacilityUpdate}
+ />
센서 관리
{zoneList.map((z, i) => (
-
+
))}
>
diff --git a/src/styles/style.css b/src/styles/style.css
index b3692b3..d94b077 100644
--- a/src/styles/style.css
+++ b/src/styles/style.css
@@ -6,7 +6,7 @@
--c4: #ebf8fe;
}
-/* 기본 마진 삭제 */
+/* 기본 설정 삭제 */
body {
margin: 0;
min-height: 100vh;
@@ -20,6 +20,15 @@ a:visited {
color: white;
}
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+a:visited {
+ color: inherit;
+}
+
/* 전체 화면 기본 틀 정의 */
.main {
display: flex;
@@ -471,3 +480,24 @@ p.sidebar-open {
.monitor-box.warn {
background-color: #fde1ad;
}
+
+.no-flex-button {
+ width: 6.5rem;
+ height: auto;
+ padding: 0.3rem;
+ border-radius: 1rem;
+ border: none;
+ color: white;
+ background-color: #82b5f7;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+}
+.no-flex-button:hover {
+ background-color: #5ba2ff;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
+}
+
+.no-flex-button:active {
+ background-color: #3389fa;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
+ transform: translateY(2px);
+}
From 01aea6924da02c0ac99cf851d2e6405855317466 Mon Sep 17 00:00:00 2001
From: YeIn kim <2076070@ewhain.net>
Date: Wed, 7 May 2025 16:30:40 +0900
Subject: [PATCH 3/4] =?UTF-8?q?feature=20|=20sprint0=20|=20FRB-63=20|=20Ax?=
=?UTF-8?q?ios=ED=86=B5=EC=8B=A0=20=ED=95=B4=EC=A0=9C=20=EB=B0=8F=20mock-d?=
=?UTF-8?q?ata=20=EB=8C=80=EC=B2=B4=20|=20=EA=B9=80=EC=98=88=EC=9D=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/assets/edit_icon.svg | 13 +
src/components/{Modal.jsx => SensorModal.jsx} | 2 +-
src/pages/Settings.jsx | 254 +++++++++++-------
src/styles/style.css | 4 +
4 files changed, 180 insertions(+), 93 deletions(-)
create mode 100644 src/assets/edit_icon.svg
rename src/components/{Modal.jsx => SensorModal.jsx} (93%)
diff --git a/src/assets/edit_icon.svg b/src/assets/edit_icon.svg
new file mode 100644
index 0000000..e1476af
--- /dev/null
+++ b/src/assets/edit_icon.svg
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file
diff --git a/src/components/Modal.jsx b/src/components/SensorModal.jsx
similarity index 93%
rename from src/components/Modal.jsx
rename to src/components/SensorModal.jsx
index 4d1c934..fff1a64 100644
--- a/src/components/Modal.jsx
+++ b/src/components/SensorModal.jsx
@@ -1,7 +1,7 @@
import { useState } from "react";
import XIcon from "../assets/x_icon.svg?react";
-export default function Modal({ isOpen, onClose, sensorInfo, onUpdate }) {
+export default function SensorModal({ isOpen, onClose, sensorInfo, onUpdate }) {
if (isOpen) {
const { zoneName, sensorName, thres } = sensorInfo;
const [newThres, setNewThres] = useState(thres);
diff --git a/src/pages/Settings.jsx b/src/pages/Settings.jsx
index dccec77..da9802c 100644
--- a/src/pages/Settings.jsx
+++ b/src/pages/Settings.jsx
@@ -1,88 +1,139 @@
import { useEffect, useState } from "react";
-import Modal from "../components/Modal";
+import SensorModal from "../components/SensorModal";
import ZoneInfoBox from "../components/ZoneInfoBox";
import axios from "axios";
import FacilityModal from "../components/FacilityModal";
-/* ────────────────────────────────
- 1. 센서 타입 → 한글 명/분류 매핑
-──────────────────────────────── */
-const ENV_TYPES = ["temp", "humid"]; // 환경센서
-const FAC_TYPES = ["dust", "current", "vibration"]; // 설비센서
-
-const toKoName = (type) => {
- switch (type) {
- case "temp":
- return "온도 센서";
- case "humid":
- return "습도 센서";
- case "dust":
- return "먼지 센서";
- case "current":
- return "전류 센서";
- case "vibration":
- return "진동 센서";
- default:
- return type;
- }
-};
+// /* ────────────────────────────────
+// 1. 센서 타입 → 한글 명/분류 매핑
+// ──────────────────────────────── */
+// const ENV_TYPES = ["temp", "humid"]; // 환경센서
+// const FAC_TYPES = ["dust", "current", "vibration"]; // 설비센서
+
+// const toKoName = (type) => {
+// switch (type) {
+// case "temp":
+// return "온도 센서";
+// case "humid":
+// return "습도 센서";
+// case "dust":
+// return "먼지 센서";
+// case "current":
+// return "전류 센서";
+// case "vibration":
+// return "진동 센서";
+// default:
+// return type;
+// }
+// };
export default function Settings() {
- const [sensorInfo, setSensorInfo] = useState();
- const [selectedZone, setSelectedZone] = useState();
- const [isSensorModalOpen, setSensorModalOpen] = useState(false);
- const [isFacilityModalOpen, setFacilityModalOpen] = useState(false);
- const [zoneList, setZoneList] = useState([]);
+ const [sensorInfo, setSensorInfo] = useState(); // 모달 전달용
+ const [selectedZone, setSelectedZone] = useState(); // 모달 전달용
+ const [isSensorModalOpen, setSensorModalOpen] = useState(false); // 모달 열기
+ const [isFacilityModalOpen, setFacilityModalOpen] = useState(false); // 모달 열기
+ const [zoneList, setZoneList] = useState([]); // 존 추가하고 열기
+
+ const initialZoneList = [
+ {
+ title: "보일러실",
+ env_sensor: [
+ { name: "온도 센서", thres: 60, sensorId: "TEMP001" },
+ { name: "습도 센서", thres: 75, sensorId: "HUMID001" },
+ ],
+ facility: [
+ {
+ name: "설비A",
+ fac_sensor: [
+ { name: "진동 센서", id: "UA10T-VIB-24060890" },
+ { name: "온도 센서", id: "UA10T-TEM-24060890" },
+ ],
+ },
+ ],
+ master: "김00",
+ },
+ {
+ title: "휴게실",
+ env_sensor: [],
+ facility: [
+ {
+ name: "설비B",
+ fac_sensor: [
+ { name: "진동 센서", id: "UA10T-VIB-24060891" },
+ { name: "온도 센서", id: "UA10T-TEM-24060891" },
+ ],
+ },
+ ],
+ master: "윤00",
+ },
+ { title: "테스트룸A", env_sensor: [], facility: [], master: "정00" },
+ {
+ title: "테스트룸B",
+ env_sensor: [],
+ facility: [{ name: "설비A" }],
+ master: "윤00",
+ },
+ ];
useEffect(() => {
- /* ────────────────────────────────
- 2. ① 공간 + ② 센서를 한 번에 받아서 매핑
- ───────────────────────────────── */
- Promise.all([
- axios.get("http://localhost:8080/api/zones"),
- axios.get("http://localhost:8080/api/sensors"), // ← location 컬럼 포함
- ])
- .then(([zoneRes, sensorRes]) => {
- const sensors = sensorRes.data;
-
- /* 센서를 zoneId 기준으로 그룹핑 → { 5: {env:[], fac:[]}, … } */
- const mapByZone = sensors.reduce((acc, s) => {
- const zoneId = s.location?.trim(); // ← 공백 대비
- if (!zoneId) return acc;
-
- if (!acc[zoneId]) acc[zoneId] = { env: [], fac: [] };
-
- const koName = toKoName(s.sensorType); // ※ 백엔드 키 sensorType
- //const item = { name: koName, thres: s.threshold ?? "-" };
- const item = {
- // ← 센서 공통 구조
- id: s.sensorId, // 새 필드
- name: toKoName(s.sensorType),
- thres: s.threshold ?? "-",
- };
-
- if (ENV_TYPES.includes(s.sensorType)) {
- acc[zoneId].env.push(item); // env → 객체 그대로
- } else if (FAC_TYPES.includes(s.sensorType)) {
- acc[zoneId].fac.push(item); // fac → 문자열만 push
- }
- return acc;
- }, {});
-
- /* zones 에 센서 배열을 끼워 넣어 최종 zoneList 작성 */
- const list = zoneRes.data.map((z) => ({
- id: z.zoneId.trim(),
- title: z.zoneName,
- env_sensor: mapByZone[z.zoneId]?.env || [],
- fac_sensor: mapByZone[z.zoneId]?.fac || [],
- master: z.master || "",
- }));
-
- /* 레코드가 하나도 없으면 예시 데이터 사용 */
- setZoneList(list.length ? list : initialZoneList);
- })
- .catch(console.error);
+ // axios
+ // .get("/api/zones")
+ // .then((res) => console.log(res))
+ // .catch((e) => console.log(e));
+ if (zoneList.length === 0) {
+ setZoneList(initialZoneList);
+ }
}, []);
+ // useEffect(() => {
+ /* ────────────────────────────────
+ 2. ① 공간 + ② 센서를 한 번에 받아서 매핑
+ ───────────────────────────────── */
+ // Promise.all([
+ // axios.get("http://localhost:8080/api/zones"),
+ // axios.get("http://localhost:8080/api/sensors"), // ← location 컬럼 포함
+ // ])
+ // .then(([zoneRes, sensorRes]) => {
+ // const sensors = sensorRes.data;
+
+ // /* 센서를 zoneId 기준으로 그룹핑 → { 5: {env:[], fac:[]}, … } */
+ // const mapByZone = sensors.reduce((acc, s) => {
+ // const zoneId = s.location?.trim(); // ← 공백 대비
+ // if (!zoneId) return acc;
+
+ // if (!acc[zoneId]) acc[zoneId] = { env: [], fac: [] };
+
+ // const koName = toKoName(s.sensorType); // ※ 백엔드 키 sensorType
+ // const item = {
+ // // ← 센서 공통 구조
+ // id: s.sensorId, // 새 필드
+ // name: toKoName(s.sensorType),
+ // thres: s.threshold ?? "-",
+ // };
+
+ // if (ENV_TYPES.includes(s.sensorType)) {
+ // acc[zoneId].env.push(item); // env → 객체 그대로
+ // } else if (FAC_TYPES.includes(s.sensorType)) {
+ // acc[zoneId].fac.push(item); // fac → 문자열만 push
+ // }
+ // return acc;
+ // }, {});
+
+ // /* zones 에 센서 배열을 끼워 넣어 최종 zoneList 작성 */
+ // const list = zoneRes.data.map((z) => ({
+ // id: z.zoneId.trim(),
+ // title: z.zoneName,
+ // env_sensor: mapByZone[z.zoneId]?.env || [],
+ // fac_sensor: mapByZone[z.zoneId]?.fac || [],
+ // master: z.master || "",
+ // }));
+
+ // /* 레코드가 하나도 없으면 예시 데이터 사용 */
+ // setZoneList(list.length ? list : initialZoneList);
+ // })
+ // .catch(console.error);
+ // }, []);
+
+ /* 모달 여는 동작 전용 함수 */
const handleOpenSensorModal = (zoneName, sensorName, thres) => {
setSensorInfo({ zoneName, sensorName, thres });
setSensorModalOpen(true);
@@ -94,8 +145,10 @@ export default function Settings() {
};
const handleThresUpdate = (newValue) => {
+ /* TODO :: 임계값 업데이트하기 */
+ /* 화면 표현하기 (완료) */
const value = Number(newValue);
- const li = zoneList.map((zone) => {
+ const updated = zoneList.map((zone) => {
if (zone.title !== sensorInfo.zoneName) return zone;
return {
...zone,
@@ -105,14 +158,30 @@ export default function Settings() {
}),
};
});
- console.log(li);
- setZoneList(li);
+ console.log(updated);
+ setZoneList(updated);
setSensorModalOpen(false);
};
const handleFacilityUpdate = (newValue) => {
console.log(`공간명: ${selectedZone} 설비명: ${newValue}`);
/* TODO :: 설비 목록 업데이트하기 */
+ /* 화면 표현하기 */
+ const updated = zoneList.map((z) => {
+ if (z.title !== selectedZone) return z;
+ return {
+ ...z,
+ facility: [
+ ...(z.facility || []),
+ {
+ name: newValue,
+ fac_sensor: [],
+ },
+ ],
+ };
+ });
+ setZoneList(updated);
+ setFacilityModalOpen(false);
};
const handleAddZone = async (newZone) => {
@@ -120,26 +189,27 @@ export default function Settings() {
if (!confirmed) return;
else {
try {
- /* 1) 백엔드에 공간 생성 요청 */
- const { data } = await axios.post(
- "http://localhost:8080/api/zones",
- { zoneName: newZone } // 요청하는 zoneName이 title이 됨
- );
+ // const { data } = await axios.post("http://localhost:8080/api/zones", {
+ // zoneName: newZone,
+ // });
+
+ // const newItem = {
+ // id: data.zoneId,
+ // title: data.zoneName,
+ // env_sensor: [],
+ // fac_sensor: [],
+ // master: "",
+ // };
- /* 2) 전체 요소를를 화면에 출력하기위한 데이터터 */
const newItem = {
- id: data.zoneId, // key 로도 사용
- title: data.zoneName, // UI 에 표시할 이름
+ title: newZone,
env_sensor: [],
- fac_sensor: [],
+ facility: [],
master: "",
};
- /* 3) 화면 상태 업데이트 */
setZoneList((prev) => [...prev, newItem]);
-
- /* 4) 로그 (센서 API와 동일한 패턴) */
- console.log(`ZONE 생성: ${data.zoneId} / ${data.zoneName}`);
+ // console.log(`ZONE 생성: ${data.zoneId} / ${data.zoneName}`);
} catch (err) {
console.error(err);
alert("공간 생성에 실패했습니다.");
@@ -149,7 +219,7 @@ export default function Settings() {
return (
<>
- setSensorModalOpen(false)}
sensorInfo={sensorInfo}
diff --git a/src/styles/style.css b/src/styles/style.css
index d94b077..c63058b 100644
--- a/src/styles/style.css
+++ b/src/styles/style.css
@@ -164,6 +164,10 @@ p.sidebar-open {
flex-direction: row;
}
+.arrow {
+ cursor: pointer;
+}
+
.moving-box:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
transform: translateY(-3px);
From 0fb52fce6f4ec5acb035d5cdd03108ee8d7f6281 Mon Sep 17 00:00:00 2001
From: YeIn kim <2076070@ewhain.net>
Date: Wed, 7 May 2025 16:31:21 +0900
Subject: [PATCH 4/4] =?UTF-8?q?feature=20|=20sprint0=20|=20FRB-63=20|=20?=
=?UTF-8?q?=EC=84=A4=EB=B9=84=20=EB=B0=8F=20=EC=84=A4=EB=B9=84=20=EC=84=BC?=
=?UTF-8?q?=EC=84=9C=20=EB=AA=A9=EB=A1=9D=20|=20=EA=B9=80=EC=98=88?=
=?UTF-8?q?=EC=9D=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Axios는 해제된 상태
---
src/components/ZoneInfoBox.jsx | 69 ++++++++++++++++++++++++----------
1 file changed, 50 insertions(+), 19 deletions(-)
diff --git a/src/components/ZoneInfoBox.jsx b/src/components/ZoneInfoBox.jsx
index d3791d7..380d2e3 100644
--- a/src/components/ZoneInfoBox.jsx
+++ b/src/components/ZoneInfoBox.jsx
@@ -6,10 +6,19 @@ export default function ZoneInfoBox({
facilityModalBtn,
onAddZone = null,
}) {
- const { title, env_sensor = [], fac_sensor = [], master = "" } = zone;
+ const { title, env_sensor = [], facility = [], master = "" } = zone;
const [isOpen, setIsOpen] = useState(false);
const addZone = zone === "공간 추가";
const [newZone, setNewZone] = useState("");
+ const [facilityInfoOpen, setFacilityInfoOpen] = useState({});
+
+ const toggleFacility = (i) => {
+ setFacilityInfoOpen((prev) => ({
+ ...prev,
+ [i]: !prev[i],
+ }));
+ };
+
return (
(
- {/*
{sen.name}
*/}
{sen.name}
- ({sen.id}){" "}
- {/* ← 추가 */}
+
+ {" "}
+ (UA10T-TEM-24060890)
+ {" "}
-
현재 설정값:{" "}
-
{sen.thres}
+
+
현재 설정값: {sen.thres}
sensorModalBtn(title, sen.name, sen.thres)}
>
@@ -55,22 +65,43 @@ export default function ZoneInfoBox({
설비 관리 센서
- {fac_sensor.length === 0 &&
설비 관리 센서가 없습니다
}
- {fac_sensor.length !== 0 &&
- fac_sensor.map((sen, i) => (
-
-
- {sen.name}
-
({sen.id})
+ {facility.length === 0 &&
등록된 설비가 없습니다
}
+ {facility.length !== 0 &&
+ facility.map((f, i) => (
+
+ {/* 설비 목록 */}
+
+
{f.name}
+
+
toggleFacility(i)}>
+ {facilityInfoOpen[i] ? "▲" : "▼"}
+
+ {/* 설비 센서 목록 */}
+ {facilityInfoOpen[i] && (
+
+ {(!Array.isArray(f.fac_sensor) ||
+ f.fac_sensor.length === 0) && (
+
설비 관리용 센서가 없습니다
+ )}
+ {f.fac_sensor?.length !== 0 &&
+ f.fac_sensor?.map((s, i) => (
+
+ {s.name} ({s.id})
+
+ ))}
+
+ )}
))}
-
+
+
+