+ {item.ruseUtztnFcltNm}
+
{Number(item.splyExtraCpct || 0).toLocaleString()}
-
+
{Number(item.ruseCpct || 0).toLocaleString()}
-
+
handleDelete(item.pbnstRuseFcltId)}
- className="text-red-500 hover:text-red-700 text-sm font-bold"
+ className="text-gray-400 hover:text-red-500 text-[11px] font-bold transition-colors"
aria-label="삭제"
title="삭제"
>
@@ -64,15 +60,13 @@ export default function SelectedFcltList({
>
);
- if (data.length === 0) return null;
-
return (
-
-
+
+
선택된 시설
-
-
+
diff --git a/apps/dm/app/(popup-layout)/map/service/map.ts b/apps/dm/app/(popup-layout)/map/service/map.ts
index 00e3ab561..dea14f0d0 100644
--- a/apps/dm/app/(popup-layout)/map/service/map.ts
+++ b/apps/dm/app/(popup-layout)/map/service/map.ts
@@ -6,7 +6,7 @@ import { DEV_URL_WR, DEV_URL_SN } from "@/components/ui/config";
export type LyrStngProps = {
lyr_sn: string
lyr_nm: string
- upper_lyr_sn: string
+ upper_lyr_sn: string
drawOrdr: string
flterSql: string
gobjTy: string
@@ -29,7 +29,7 @@ export type LyrStngProps = {
export const mapService = {
//레이어 설정 조회
getLyrStngs: async () => {
- const response = await fetch(`${DEV_URL_SN}/api/v1/map/lyrStngs/DM_NETWORK`, {
+ const response = await fetch(`${DEV_URL_SN}/map/lyrStngs/DM_NETWORK`, {
method: 'GET',
headers: {
'Accept': 'application/json',
@@ -42,7 +42,7 @@ export const mapService = {
return response.json();
},
- getPbnstRuseFclts: async (ruseFcltPk:any) => {
+ getPbnstRuseFclts: async (ruseFcltPk: any) => {
const response = await fetch(`${DEV_URL_WR}/reuseFc/pubFc/${ruseFcltPk}`, {
method: 'GET',
headers: {
diff --git a/apps/dm_new/app/(with-layout)/apply/dmdco/page.tsx b/apps/dm/app/(with-layout)/apply/dmdco/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/apply/dmdco/page.tsx
rename to apps/dm/app/(with-layout)/apply/dmdco/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/apply/layout.tsx b/apps/dm/app/(with-layout)/apply/layout.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/apply/layout.tsx
rename to apps/dm/app/(with-layout)/apply/layout.tsx
diff --git a/apps/dm_new/app/(with-layout)/apply/modal/WaterQualityRefModal.tsx b/apps/dm/app/(with-layout)/apply/modal/WaterQualityRefModal.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/apply/modal/WaterQualityRefModal.tsx
rename to apps/dm/app/(with-layout)/apply/modal/WaterQualityRefModal.tsx
diff --git a/apps/dm/app/(with-layout)/apply/register/page.tsx b/apps/dm/app/(with-layout)/apply/register/page.tsx
new file mode 100644
index 000000000..db1c47cb5
--- /dev/null
+++ b/apps/dm/app/(with-layout)/apply/register/page.tsx
@@ -0,0 +1,895 @@
+// app/(with-layout)/apply/register/page.tsx
+/**
+ * 수요정보등록(수정) 화면
+ *
+ * 🔗 사용 API
+ * - 수요정보등록: POST /reuseFc/request/demandInfo
+ * - 수요정보조회: GET /reuseFc/request/' + dmdInfoId - getSdmInfoStatusList
+ */
+"use client";
+
+import { useMemo, useState, useRef, useEffect } from "react";
+import { useRouter } from "next/navigation";
+import SubPageHeader from "@/components/SubPageHeader";
+import WaterQualityRefModal from "../modal/WaterQualityRefModal";
+import CandidateSiteModal, { type CandidateSite } from "@/components/CandidateSiteModal";
+import { useDmStore } from "@/app/store";
+import type { SdmInfoFormData } from "@/app/store/sdmMng";
+import SimilarCaseModal, { type SimilarCaseDetail, type SimilarCaseRow } from "@/app/(landing)/reuse/apply/modal/SimilarCaseModal";
+//import type { SubsInfo } from "@/app/store/map/type";
+
+type PHRange = { min: string; max: string };
+
+export default function DemandRegisterPage() {
+ const router = useRouter();
+ const today = useMemo(() => {
+ const d = new Date();
+ const yyyy = d.getFullYear();
+ const mm = String(d.getMonth() + 1).padStart(2, "0");
+ const dd = String(d.getDate()).padStart(2, "0");
+ return `${yyyy}-${mm}-${dd}`;
+ }, []);
+
+ const [openRef, setOpenRef] = useState(false);
+
+ const [openCand, setOpenCand] = useState(false);
+ const [selectedCands, setSelectedCands] = useState([]);
+
+ // 기본정보
+ const [reqDate, setReqDate] = useState(today);
+ const [reqQty, setReqQty] = useState("");
+ const [usePurpose, setUsePurpose] = useState("");
+ const [hopePrice, setHopePrice] = useState("");
+
+ // 요구수질
+ const [coliform, setColiform] = useState(""); // 총대장균군수
+ const [noDetect, setNoDetect] = useState(false);
+
+ const [tn, setTn] = useState("");
+ const [residualCl, setResidualCl] = useState("");
+ const [tp, setTp] = useState("");
+ const [turbidity, setTurbidity] = useState("");
+ const [ph, setPh] = useState({ min: "", max: "" });
+ const [bod, setBod] = useState("");
+ const [chloride, setChloride] = useState("");
+ const [odor, setOdor] = useState("불쾌하지 않을 것");
+ const [ec, setEc] = useState("");
+ const [color, setColor] = useState("");
+ const [remark, setRemark] = useState("");
+
+ // 공급처 후보지 선택(필수아님)
+ const [cand1, setCand1] = useState("");
+ const [cand2, setCand2] = useState("");
+ const [cand3, setCand3] = useState("");
+ //const [candidateList, setCandidateList] = useState([]);
+
+ const useTypesData = useDmStore((state) => state.Sdm.useTypesData);
+ const getUseTypesData = useDmStore((state) => state.getUseTypesData);
+ const [openSimilar, setOpenSimilar] = useState(false);
+
+ // 후보지 팝업 메시지 핸들러
+ const candsRef = useRef<{ name: string; code: string }[]>([]);
+
+ useEffect(() => {
+ const origin = window.location.origin;
+
+ const handleMessage = (event: MessageEvent) => {
+ const data = event.data;
+ if (!data || typeof data !== "object") return;
+
+ if (data.type === "SET_SUBS_INFO") {
+ // 팝업에서 배열로 전송 (최대 3개)
+ const list = Array.isArray(data.payload) ? data.payload : [];
+ setCand1(list[0]?.ruseUtztnFcltNm ?? "");
+ setCand2(list[1]?.ruseUtztnFcltNm ?? "");
+ setCand3(list[2]?.ruseUtztnFcltNm ?? "");
+ // 추후 REQUEST_INIT_SUBS_INFO 응답용 ref 갱신
+ candsRef.current = list.map((x: any) => ({
+ name: x.ruseUtztnFcltNm ?? "",
+ code: x.ruseUtztnFcltCd ?? "",
+ }));
+ }
+
+ if (data.type === "REQUEST_INIT_SUBS_INFO") {
+ // 팝업이 현재 선택된 후보지 초기값을 요청하면 응답
+ const initList = candsRef.current
+ .filter((x) => x.name)
+ .map((x) => ({
+ ruseUtztnFcltNm: x.name,
+ ruseUtztnFcltCd: x.code,
+ }));
+ const target = event.source as Window | null;
+ target?.postMessage(
+ { type: "INIT_SUBS_INFO", payload: initList },
+ { targetOrigin: origin }
+ );
+ }
+ };
+
+ window.addEventListener("message", handleMessage);
+ return () => window.removeEventListener("message", handleMessage);
+ }, []);
+
+ // 유사사례
+ const similarCaseList = useDmStore((state) => state.Sdm.similarCaseList);
+ const getSimilarCaseList = useDmStore((state) => state.getSimilarCaseList);
+ const [farmText, setFarmText] = useState("이하");
+
+ const insertSdmInfoData = useDmStore(
+ (state) => state.insertSdmInfoData
+ );
+
+ useEffect(() => {
+ getUseTypesData();
+ }, [getUseTypesData]);
+
+ useEffect(() => {
+ const handler = (event: MessageEvent) => {
+ if (!event.data) return;
+
+ if (event.data.type === "SET_SUBS_INFO") {
+ const list = event.data.payload || [];
+
+ // 최대 3개
+ setCand1(list[0]?.ruseUtztnFcltNm || "");
+ setCand2(list[1]?.ruseUtztnFcltNm || "");
+ setCand3(list[2]?.ruseUtztnFcltNm || "");
+ //setCandidateList(list);
+ }
+ };
+
+ window.addEventListener("message", handler);
+
+ return () => {
+ window.removeEventListener("message", handler);
+ };
+ }, []);
+
+ // option
+ // 'req':필수 / 'num' : 숫자 / 'num+[숫자] : 자릿수
+ const validation = (val, text, option: string[] = []) => {
+ if (option.includes('req')) {
+ if (val.trim() === "" || val === undefined) {
+ alert(`${text} 항목은 필수 입력값입니다.`);
+ return false;
+ }
+ } else {
+ if (val.trim() === "") {
+ return true;
+ }
+ }
+
+ const numOption = option.find((opt) => opt.startsWith("num"));
+ if (numOption) {
+ const isDot = numOption.includes("Dot");
+
+ // 3. 숫자 체크
+ const regex = isDot ? /^\d+(\.\d+)?$/ : /^\d+$/;
+
+ if (!regex.test(val)) {
+ alert(
+ `${text}은(는) ${isDot ? "숫자 또는 소수" : "숫자"
+ }만 입력 가능합니다.`
+ );
+ return false;
+ }
+
+ // 4. 자리수 체크
+ const lengthStr = numOption.replace("numDot", "").replace("num", "");
+
+ if (lengthStr) {
+ const length = Number(lengthStr);
+
+ if (isDot) {
+ // 🔥 소수 자리수 체크 (소수점 기준)
+ const decimalPart = val.split(".")[1];
+
+ if (decimalPart && decimalPart.length > length) {
+ alert(`${text}은(는) 소수점 ${length}자리까지 가능합니다.`);
+ return false;
+ }
+ } else {
+ // 정수 자리수 체크
+ if (val.length !== length) {
+ alert(`${text}은(는) ${length}자리여야 합니다.`);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ const rows: SimilarCaseRow[] = similarCaseList.map((el, idx) => ({
+ caseId: String(idx),
+ projectName: el.ruseBizNm,
+ period: `${el.bizBgngDate} ~ ${el.bizEndDate}`,
+
+ supplyFacilityType: el.splyFcltKnd,
+ supplyName: el.splyFcltNm,
+ supplyCapacity: Number(el.splyFcltCpct),
+
+ demandName: el.dmdFcltNm,
+ demandAddress: el.dmdFcltAddr,
+
+ facilityCapacity: Number(el.ruseFcltCpct),
+ pipeKm: Number(el.ruseFcltPplLen),
+ purpose: el.ruseFcltUsg,
+
+ supplyAmt: Number(el.ruseFcltSplyCpct),
+ treatment: el.ruseFcltPrcsMsrc,
+ usePurpose: el.ruseFcltUsg,
+ }));
+
+ const detailsByCaseId: Record = {};
+ similarCaseList.forEach((el, idx) => {
+ detailsByCaseId[String(idx)] = {
+ coliform: el.grnteWqGrssClfg,
+ residualCl: el.grnteWqCombResdch,
+ turbidity: el.grnteWqTrbt,
+ bod: el.grnteWqBod,
+ odor: el.grnteWqSmll,
+ color: el.grnteWqCrmty,
+ tn: el.grnteWqTn,
+ tp: el.grnteWqTp,
+ ph: el.grnteWqPh,
+ chloride: el.grnteWqClIonQnt,
+ ec: el.grnteWqCndctStat,
+ note: el.grnteWqRmrk,
+
+ costTotal: Number(el.reqBizGrssCst),
+ costFacility: Number(el.reqBizReuseFcltCst),
+ costPipe: Number(el.reqBizPplCst),
+ costDesign: Number(el.reqBizDesignCst),
+ costSupervision: Number(el.reqBizSpvsCst),
+ costOther: Number(el.reqBizEtc),
+
+ fundingTotal: Number(el.prcmPlanGrssCst),
+ fundingNational: Number(el.prcmPlanNlep),
+ fundingLocal: Number(el.prcmPlanLcep),
+ fundingSelf: Number(el.prcmPlanBrdnCst),
+ fundingRate: Number(el.prcmPlanEcqrRt),
+ };
+ });
+
+ // 데이터 저장
+ const saveInfo = async () => {
+ if (!validation(reqQty, '요구수량', ['req', 'numDot'])) return false;
+ if (!validation(usePurpose, '사용용도', ['req'])) return false;
+ if (!validation(hopePrice, '희망단가', ['req', 'numDot'])) return false;
+ /* if(!noDetect && !validation(coliform, '총대장균군수', ['req','numDot'])) return false;
+ if(!validation(residualCl, ['req', '결합잔류염소', 'numDot'])) return false;
+ if(!validation(turbidity, '탁도', ['req', 'numDot'])) return false;
+ if(!validation(bod, '생물화학적산소요구량', ['req', 'numDot'])) return false;
+ if(!validation(odor, '냄새', ['req'])) return false; */
+
+ // 필수는 아니지만 숫자로는 들어와야함.
+ /* if(!validation(tn, '총질소', ['numDot'])) return false;
+ if(!validation(residualCl, '결합잔류염소', ['numDot'])) return false;
+ if(!validation(tp, '총인', ['numDot'])) return false;
+ if(!validation(ph.min, 'ph', ['numDot'])) return false;
+ if(!validation(ph.max, 'ph', ['numDot'])) return false;
+ if(!validation(chloride, '염화물', ['numDot'])) return false;
+ if(!validation(ec, '전기전도도', ['numDot'])) return false; */
+
+ const userStr = sessionStorage.getItem("user");
+ const user = userStr ? JSON.parse(userStr) : null;
+
+ if (user) {
+ // 후보지 목록 (candsRef에 코드까지 보관됨)
+ const splyCndtList = candsRef.current
+ .filter((x) => x.name)
+ .map((x) => ({
+ ruseUtztnFcltNm: x.name,
+ ruseUtztnFcltCd: x.code,
+ }));
+
+ const payload: SdmInfoFormData = {
+ dmndTmng: new Date(reqDate),
+ dmdInfoId: user.dmdInfoId,
+ dmndQty: Number(reqQty),
+ wntUntprc: Number(hopePrice),
+ useUsgCd: usePurpose,
+ // 수질
+ grssClfg: Number(coliform),
+ tn: Number(tn) || null,
+ combResdch: Number(residualCl) || null,
+ tph: Number(tp) || null,
+ trbt: Number(turbidity),
+ ph: ph.min || '' + ((ph.min || ph.max) ? '~' : '') + ph.max || '',
+ blgcOxygDmndQnt: Number(bod),
+ clIonQnt: Number(chloride) || null,
+ smll: odor,
+ cdtnv: Number(ec) || null,
+ crmty: Number(color) || null,
+ rmrk: remark,
+ splyCndtList, // 후보지 목록 추가
+ }
+ const success = await insertSdmInfoData(payload);
+ if (success) {
+ router.push("/apply/status");
+ }
+ }
+ }
+
+ const searchCandidate = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const dmdInfoId = JSON.parse(
+ sessionStorage.getItem("user") || "{}"
+ )?.dmdInfoId?.toString();
+
+ const url = `/map/popup?dmdInfoId=${encodeURIComponent(dmdInfoId)}`;
+ const width = window.screen.availWidth;
+ const height = window.screen.availHeight;
+ const left = 0;
+ const top = 0;
+ const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`;
+ window.open(url, "candidatePopup", popupOptions);
+ };
+
+ const getCriterionCode = (criterion: string) => {
+ switch (criterion) {
+ case "supplyAmt":
+ return "S";
+ case "money":
+ return "M";
+ case "usePurpose":
+ return "U";
+ default:
+ return "";
+ }
+ };
+
+ const searchSimilarCase = async (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setOpenSimilar(true);
+ };
+
+ const initQualityForm = () => {
+ setColiform("");
+ setNoDetect(false);
+ setTn("");
+ setResidualCl("");
+ setTp("");
+ setTurbidity("");
+ setPh({ min: "", max: "" });
+ setBod("");
+ setChloride("");
+ setOdor("불쾌하지 않을 것");
+ setEc("");
+ setColor("");
+ setRemark("");
+ setFarmText("이하");
+ }
+
+ const fncSetOptionValue = (e: React.ChangeEvent) => {
+ const value = e.target.value;
+ initQualityForm();
+
+ switch (value) {
+ // 청소화장실용도
+ case '01':
+ setTp('0.2');
+ setTurbidity('2');
+ setPh({ min: '5.8', max: '8.5' });
+ setBod('5');
+ setColor('20');
+ break;
+ // 세척살수용도
+ case '02':
+ setTp('0.2');
+ setTurbidity('2');
+ setPh({ min: '5.8', max: '8.5' });
+ setBod('5');
+ break;
+ // 조경용도
+ case '03':
+ setTp('0.2');
+ setTurbidity('2');
+ setPh({ min: '5.8', max: '8.5' });
+ setBod('5');
+ setChloride('250');
+ break;
+ // 지하수
+ case '05':
+ break;
+ // 농업용도
+ case '06':
+ setTurbidity('5');
+ setPh({ min: '5.8', max: '8.5' });
+ setBod('8');
+ setEc('2000');
+ setFarmText("이하 (** 간접:2000,직접:700 )");
+ break;
+ // 하천 등 유지보수
+ case '07':
+ setColiform('1000');
+ setTn('10');
+ setTp('0.5');
+ setPh({ min: '5.8', max: '8.5' });
+ setBod('5');
+ break;
+ // 공업
+ case '08':
+ setPh({ min: '5.8', max: '8.5' });
+ break;
+ default:
+ console.log('잘못된 선택입니다.');
+ }
+
+ setUsePurpose(value);
+ }
+
+ return (
+ <>
+
+
+
+ {/* 서브 타이틀 + 브레드크럼 */}
+
+
+ {/* 컨텐츠 영역 */}
+
+
+ {/* 기본정보 테이블 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ fncSetOptionValue(e)}
+ className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
+ >
+ 선택하세요
+ {useTypesData.map((item) => (
+
+ {item.text}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ {/* 요구수질 섹션 타이틀 바 */}
+
+
+ 요구수질
+ setOpenRef(true)}
+ className="rounded border-[2px] border-[#DE3412] px-2 py-3 text-[15px] font-medium text-[#4F4F4F] hover:bg-orange-50"
+ >
+ 수질기준 참고
+
+ {/* 수질기준참고 모달 */}
+ setOpenRef(false)}
+ imageSrc="/images/landing/water_quality_ref_1.png"
+ />
+
+
+
+ {/* 요구수질 테이블 */}
+
+ {/* 1행 */}
+
+
+
+ {
+ setNoDetect(e.target.checked);
+ if (e.target.checked) setColiform("");
+ }}
+ className="h-4 w-4 rounded border-neutral-300"
+ />
+ 불검출
+
+
+ }
+ rightLabel="총질소(T-N)(mg/L)"
+ rightControl={
+
+ }
+ />
+
+
+ {/* 2행 */}
+
}
+ rightLabel="총인(T-P)(mg/L)"
+ rightControl={
+
+ }
+ />
+
+
+ {/* 3행 */}
+
+ }
+ rightLabel="수소이온농도(pH)"
+ rightControl={
+
+ setPh((p) => ({ ...p, min: v }))}
+ className="w-32"
+ />
+ ~
+ setPh((p) => ({ ...p, max: v }))}
+ className="w-32"
+ />
+
+ }
+ />
+
+
+ {/* 4행 */}
+
+ 생물화학적산소요구량
+
+ (BOD)(mg/L)
+ >
+ }
+ /* leftRequired */
+ leftControl={ }
+ rightLabel="염화물(mgCL/L)"
+ rightControl={ }
+ />
+
+
+ {/* 5행 */}
+ setOdor(e.target.value)}
+ className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
+ >
+ 불쾌하지 않을 것
+ 약간 있음
+ 있음
+
+ }
+ rightLabel="전기전도도(µs/cm)"
+ rightControl={ }
+ />
+
+
+ {/* 6행 */}
+ }
+ rightLabel="비고"
+ rightControl={
+
+
+ {/* 하단 버튼 */}
+
+ {
+ // TODO: 저장 처리
+ saveInfo();
+ }}
+ >
+ 수요정보 등록
+
+
+
+
+
+ setOpenSimilar(false)}
+ rows={rows} // 실제 데이터
+ detailsByCaseId={detailsByCaseId}
+ onSearch={async (criterion) => {
+ const code = getCriterionCode(criterion);
+ if (!code) return;
+ // 값은 상황에 맞게 (예시: usePurpose)
+ console.log(code+' req: '+reqQty+' purpose :'+usePurpose);
+ await getSimilarCaseList(code, code == 'S' ? reqQty : code == 'U' ? usePurpose : '');
+ }}
+ />
+
+ setOpenCand(false)}
+ initialSelected={selectedCands}
+ onApply={(list) => {
+ setSelectedCands(list);
+
+ // ✅ cand1~3 입력칸에 반영(시설명만 넣는 버전)
+ setCand1(list[0]?.name ?? "");
+ setCand2(list[1]?.name ?? "");
+ setCand3(list[2]?.name ?? "");
+
+ setOpenCand(false);
+ }}
+ />
+ >
+ );
+}
+
+/* ----------------------------- UI helpers ----------------------------- */
+
+function Divider() {
+ return
;
+}
+
+function LabelCell({
+ children,
+ required,
+}: {
+ children: React.ReactNode;
+ required?: boolean;
+}) {
+ return (
+
+
+ {children}
+ {required ? * : null}
+
+
+ );
+}
+
+function FormRow({
+ label,
+ required,
+ children,
+}: {
+ label: string;
+ required?: boolean;
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
+
+function QualityRow({
+ leftLabel,
+ leftRequired,
+ leftControl,
+ rightLabel,
+ rightControl,
+}: {
+ leftLabel: React.ReactNode;
+ leftRequired?: boolean;
+ leftControl: React.ReactNode;
+ rightLabel: React.ReactNode;
+ rightControl: React.ReactNode;
+}) {
+ return (
+
+ {/* 좌측 라벨 */}
+
+
+ {leftLabel}
+ {leftRequired ? * : null}
+
+
+
+ {/* 좌측 입력 */}
+
{leftControl}
+
+ {/* 우측 라벨 */}
+
+ {rightLabel}
+
+
+ {/* 우측 입력 */}
+
{rightControl}
+
+ );
+}
+
+function TextInput({
+ value,
+ onChange,
+ placeholder,
+ disabled,
+ className,
+}: {
+ value: string;
+ onChange: (v: string) => void;
+ placeholder?: string;
+ disabled?: boolean;
+ className?: string;
+}) {
+ return (
+ onChange(e.target.value)}
+ placeholder={placeholder}
+ disabled={disabled}
+ className={[
+ "h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none",
+ "focus:border-neutral-500",
+ disabled ? "cursor-not-allowed" : "",
+ className ?? "",
+ ].join(" ")}
+ />
+ );
+}
+
+function InlineSuffixInput({
+ value,
+ onChange,
+ suffix,
+}: {
+ value: string;
+ onChange: (v: string) => void;
+ suffix: string;
+}) {
+ return (
+
+
+ {suffix}
+
+ );
+}
+
+function DateInput({
+ value,
+ onChange,
+}: {
+ value: string;
+ onChange: (v: string) => void;
+}) {
+
+ const ref = useRef(null);
+
+ const openPicker = () => {
+ const el = ref.current;
+ if (!el) return;
+
+ // Chrome/Edge 등 지원
+ if (typeof (el as any).showPicker === "function") {
+ (el as any).showPicker();
+ return;
+ }
+
+ // fallback (일부 브라우저)
+ el.focus();
+ el.click();
+ };
+ return (
+
+
onChange(e.target.value)}
+ className={[
+ "h-11 w-full rounded-md border border-neutral-300 bg-white px-3 pr-10 text-sm outline-none focus:border-neutral-500",
+ // ✅ 기본 달력 아이콘 숨김 (Tailwind arbitrary selector)
+ "[&::-webkit-calendar-picker-indicator]:opacity-0",
+ "[&::-webkit-calendar-picker-indicator]:absolute",
+ "[&::-webkit-calendar-picker-indicator]:right-0",
+ "[&::-webkit-calendar-picker-indicator]:w-10",
+ "[&::-webkit-calendar-picker-indicator]:h-full",
+ "[&::-webkit-calendar-picker-indicator]:cursor-pointer",
+ ].join(" ")}
+ />
+
+ {/* ✅ 클릭 가능한 아이콘 버튼 */}
+
+
+
+
+
+
+ );
+}
diff --git a/apps/dm/app/(with-layout)/apply/status/page.tsx b/apps/dm/app/(with-layout)/apply/status/page.tsx
new file mode 100644
index 000000000..dab5d46bb
--- /dev/null
+++ b/apps/dm/app/(with-layout)/apply/status/page.tsx
@@ -0,0 +1,727 @@
+"use client";
+
+import { useDmStore } from "@/app/store";
+import type { DemandCompBizType } from "@/app/store/sdmMng";
+import SubPageHeader from "@/components/SubPageHeader";
+import { useEffect, useMemo, useRef, useState } from "react";
+
+type TopField = { label: string; key: string, value: string; required?: boolean };
+type PairField = { label: string; key: string, value: string };
+type SessionUser = { dmdInfoId?: string };
+
+const formatDate = (value: string | Date | "" | undefined) => {
+ if (!value) return "-";
+ const date = new Date(value);
+ if (Number.isNaN(date.getTime())) return String(value);
+ const yyyy = date.getFullYear();
+ const mm = String(date.getMonth() + 1).padStart(2, "0");
+ const dd = String(date.getDate()).padStart(2, "0");
+ return `${yyyy}-${mm}-${dd}`;
+};
+
+const formatValue = (value: string | number | null | undefined) => {
+ if (value === null || value === undefined || value === "") return "-";
+ return String(value);
+};
+
+const formatBelow = (value: string | number | null | undefined) => {
+ if (value === null || value === undefined || value === "") return "-";
+ if (Number(value) === -1) return "불검출";
+ return `${value} 이하`;
+};
+
+function StatusBadge({ v }: { v: string }) {
+ const cls = v === "신청" ? "text-red-600" : "text-lime-600";
+ return {v || "-"} ;
+}
+
+function TextInput({
+ value,
+ onChange,
+ placeholder,
+ disabled,
+ className,
+}: {
+ value: string;
+ onChange: (v: string) => void;
+ placeholder?: string;
+ disabled?: boolean;
+ className?: string;
+}) {
+ return (
+ onChange(e.target.value)}
+ placeholder={placeholder}
+ disabled={disabled}
+ className={[
+ "h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none",
+ "focus:border-neutral-500",
+ disabled ? "cursor-not-allowed" : "",
+ className ?? "",
+ ].join(" ")}
+ />
+ );
+}
+
+function DateInput({
+ value,
+ onChange,
+}: {
+ value: string;
+ onChange: (v: string) => void;
+}) {
+
+ const ref = useRef(null);
+
+ const openPicker = () => {
+ const el = ref.current;
+ if (!el) return;
+
+ // Chrome/Edge 등 지원
+ if (typeof (el as any).showPicker === "function") {
+ (el as any).showPicker();
+ return;
+ }
+
+ // fallback (일부 브라우저)
+ el.focus();
+ el.click();
+ };
+ return (
+
+
onChange(e.target.value)}
+ className={[
+ "h-11 w-full rounded-md border border-neutral-300 bg-white px-3 pr-10 text-sm outline-none focus:border-neutral-500",
+ // ✅ 기본 달력 아이콘 숨김 (Tailwind arbitrary selector)
+ "[&::-webkit-calendar-picker-indicator]:opacity-0",
+ "[&::-webkit-calendar-picker-indicator]:absolute",
+ "[&::-webkit-calendar-picker-indicator]:right-0",
+ "[&::-webkit-calendar-picker-indicator]:w-10",
+ "[&::-webkit-calendar-picker-indicator]:h-full",
+ "[&::-webkit-calendar-picker-indicator]:cursor-pointer",
+ ].join(" ")}
+ />
+
+ {/* ✅ 클릭 가능한 아이콘 버튼 */}
+
+
+
+
+
+
+ );
+}
+
+export default function DemandStatusPage() {
+ const { getSdmInfoStatusList, updateSdmInfoData, Sdm, Common } = useDmStore((state) => state);
+ const { successLogin, sdmInfoStatusList } = Sdm;
+ const { selectSidoData, selectSigunData } = Common;
+
+ const detailRef = useRef(null);
+
+ const [isLoginStorageInfo, setIsLoginStorageInfo] = useState(false);
+ const [selectedDmdInfoSn, setSelectedDmdInfoSn] = useState(null);
+ const [transformedSdmInfoStatusList, setTransformedSdmInfoStatusList] = useState([]);
+
+ // 수정 유무
+ const [ isEditMode, setIsEditMode ] = useState(false);
+ // 수정용 (UI에서 바뀌는 값)
+ const [editData, setEditData] = useState(null);
+ // 원본 백업 (취소용)
+ const [originData, setOriginData] = useState(null);
+ // 사용용도
+ const useTypesData = useDmStore((state) => state.Sdm.useTypesData);
+ const getUseTypesData = useDmStore((state) => state.getUseTypesData);
+
+ useEffect(() => {
+ const user = sessionStorage.getItem("user");
+ if (user) {
+ setIsLoginStorageInfo(true);
+ }
+ }, []);
+
+ useEffect(() => {
+ getUseTypesData();
+ }, [getUseTypesData]);
+
+ useEffect(() => {
+ if (!successLogin && !isLoginStorageInfo) return;
+
+ const user = sessionStorage.getItem("user");
+ if (!user) return;
+
+ try {
+ const userData = JSON.parse(user) as SessionUser;
+ const dmdInfoId = userData.dmdInfoId?.trim();
+ if (dmdInfoId) {
+ getSdmInfoStatusList(dmdInfoId);
+ }
+ } catch (error) {
+ console.error("Error parsing user data:", error);
+ }
+ }, [successLogin, isLoginStorageInfo, getSdmInfoStatusList]);
+
+ useEffect(() => {
+ const mappedList = sdmInfoStatusList.map((item) => ({
+ ...item,
+ ctpvCd: selectSidoData.find((sido) => sido.val === item.ctpvCd?.trim())?.text || item.ctpvCd,
+ sggCd: selectSigunData.find((sigun) => sigun.val === item.sggCd?.trim())?.text || item.sggCd,
+ }));
+
+ setTransformedSdmInfoStatusList(mappedList);
+ }, [sdmInfoStatusList, selectSidoData, selectSigunData]);
+
+ useEffect(() => {
+ const first = transformedSdmInfoStatusList[0];
+ if (!first) {
+ setSelectedDmdInfoSn(null);
+ return;
+ }
+
+ if (selectedDmdInfoSn == null) {
+ setSelectedDmdInfoSn(first.dmdInfoSn);
+ return;
+ }
+
+ const exists = transformedSdmInfoStatusList.some(
+ (item) => item.dmdInfoSn === selectedDmdInfoSn,
+ );
+ if (!exists) {
+ setSelectedDmdInfoSn(first.dmdInfoSn);
+ }
+ }, [transformedSdmInfoStatusList, selectedDmdInfoSn]);
+
+ const selectedSdmInfoStatus = useMemo(() => {
+ if (!transformedSdmInfoStatusList.length) return null;
+ return (
+ transformedSdmInfoStatusList.find((item) => item.dmdInfoSn === selectedDmdInfoSn) ??
+ transformedSdmInfoStatusList[0]
+ );
+ }, [transformedSdmInfoStatusList, selectedDmdInfoSn]);
+
+ const topFields: TopField[] = useMemo(() => {
+ if (!selectedSdmInfoStatus) return [];
+
+ return [
+ { label: "요구시기", key: "dmndTmng", value: formatDate(selectedSdmInfoStatus.dmndTmng), required: true },
+ { label: "요구수량(㎥/d)", key: "dmndQty", value: formatValue(selectedSdmInfoStatus.dmndQty) },
+ { label: "사용용도", key: "useUsgCd", value: formatValue(selectedSdmInfoStatus.useUsgCd) },
+ { label: "희망단가(원/㎥)", key: "wntUntprc", value: formatValue(selectedSdmInfoStatus.wntUntprc) },
+ ];
+ }, [selectedSdmInfoStatus]);
+
+ const waterFields: PairField[] = useMemo(() => {
+ if (!selectedSdmInfoStatus) return [];
+
+ return [
+ { label: "총대장균군수(개/100mL)", key: "grssClfg", value: formatBelow(selectedSdmInfoStatus.grssClfg) },
+ { label: "결합잔류염소(mg/L)", key:"combResdch", value: formatBelow(selectedSdmInfoStatus.combResdch) },
+ { label: "탁도(NTU)", key:"trbt", value: formatBelow(selectedSdmInfoStatus.trbt) },
+
+ { label: "생물학적산소요구량(BOD)(mg/L)", key:"blgcOxygDmndQnt", value: formatBelow(selectedSdmInfoStatus.blgcOxygDmndQnt) },
+ { label: "냄새", key:"smll", value: formatValue(selectedSdmInfoStatus.smll) },
+ { label: "색도(도)", key:"crmty", value: formatBelow(selectedSdmInfoStatus.crmty) },
+
+ { label: "총질소(T-N)(mg/L)", key:"tn", value: formatBelow(selectedSdmInfoStatus.tn) },
+ { label: "총인(T-P)(mg/L)", key:"tph", value: formatBelow(selectedSdmInfoStatus.tph) },
+ { label: "수소이온농도(pH)", key:"ph", value: formatValue(selectedSdmInfoStatus.ph) },
+
+ { label: "염화물(mgCL/L)", key:"clIonQnt", value: formatBelow(selectedSdmInfoStatus.clIonQnt) },
+ { label: "전기전도도(μS/cm)", key:"cdtnv", value: formatBelow(selectedSdmInfoStatus.cdtnv) },
+ { label: "비고", key:"rmrk", value: formatValue(selectedSdmInfoStatus.rmrk) },
+ ];
+ }, [selectedSdmInfoStatus]);
+
+ const candidateNames = useMemo(() => {
+ const list = isEditMode
+ ? editData?.splyCndtList ?? []
+ : selectedSdmInfoStatus?.splyCndtList ?? [];
+ return [
+ list[0]?.ruseUtztnFcltNm ?? "",
+ list[1]?.ruseUtztnFcltNm ?? "",
+ list[2]?.ruseUtztnFcltNm ?? "",
+ ];
+ }, [isEditMode, editData, selectedSdmInfoStatus]);
+
+ const onSelectSdmInfoStatus = (dmdInfoSn: number | "") => {
+ if(isEditMode){
+ if(confirm('저장되지 않은 데이터는 사라집니다.\n진행하시겠습니까?')){
+ setIsEditMode(false);
+ }
+ }
+ setSelectedDmdInfoSn(dmdInfoSn);
+ requestAnimationFrame(() => {
+ detailRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
+ });
+ };
+
+ // ph 전후로 나눔
+ const getRange = (val: string) => {
+ const a = val.split('~')[0]||''; const b = val.split('~')[1]||'';
+ return { a, b };
+ };
+
+ const searchCandidate = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const dmdInfoId = JSON.parse(
+ sessionStorage.getItem("user") || "{}"
+ )?.dmdInfoId?.toString();
+
+ const url = `/map/popup?dmdInfoId=${encodeURIComponent(dmdInfoId)}`;
+ const width = window.screen.availWidth;
+ const height = window.screen.availHeight;
+ const left = 0;
+ const top = 0;
+ const popupOptions = `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`;
+ window.open(url, "candidatePopup", popupOptions);
+ };
+
+ const saveInfo = () => {
+ if(confirm('수정된 데이터를 저장하시겠습니까?')){
+ const payload = {
+ ...editData,
+ splyCndtList : editData.splyCndtList ?? []
+ };
+
+ updateSdmInfoData(payload).then((res) => {
+ if (res) {
+ const user = JSON.parse(sessionStorage.getItem("user") || "{}");
+ if (user?.dmdInfoId) {
+ getSdmInfoStatusList(user.dmdInfoId);
+ }
+ setIsEditMode(false);
+ }
+ });
+ }
+ }
+
+ useEffect(() => {
+ const handler = (event: MessageEvent) => {
+ if (!event.data) return;
+
+ if (event.data.type === "SET_SUBS_INFO") {
+ const list = event.data.payload || [];
+ setEditData((prev) => ({
+ ...prev,
+ splyCndtList: list,
+ }));
+ }
+ };
+
+ window.addEventListener("message", handler);
+
+ return () => {
+ window.removeEventListener("message", handler);
+ };
+ }, []);
+
+ return (
+
+
+
+
+
+
+ 수요정보 신청현황
+
+
+
+
+
+ NO.
+ 시도
+ 시군구
+ 회사명
+ 공장주소
+ 전화번호
+ 요구시기
+ 등록일자
+ 수정일자
+ 진행상태
+
+
+
+
+ {transformedSdmInfoStatusList.length === 0 ? (
+
+
+ 조회된 신청내역이 없습니다.
+
+
+ ) : (
+ transformedSdmInfoStatusList.map((sdmInfoStatus, index) => {
+ const active = sdmInfoStatus.dmdInfoSn === selectedSdmInfoStatus?.dmdInfoSn;
+ return (
+ onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn)}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn);
+ }
+ }}
+ className={[
+ "border-t border-neutral-200 outline-none",
+ "cursor-pointer",
+ active ? "bg-slate-50" : "hover:bg-slate-50",
+ "focus-visible:ring-2 focus-visible:ring-slate-300",
+ ].join(" ")}
+ aria-selected={active}
+ >
+ {sdmInfoStatus.index || index + 1}
+ {formatValue(sdmInfoStatus.ctpvCd)}
+ {formatValue(sdmInfoStatus.sggCd)}
+ {formatValue(sdmInfoStatus.dmdCoNm)}
+ {formatValue(sdmInfoStatus.daddr)}
+ {formatValue(sdmInfoStatus.telno)}
+ {formatDate(sdmInfoStatus.dmndTmng)}
+ {formatDate(sdmInfoStatus.regDt)}
+ {formatDate(sdmInfoStatus.mdfcnDt)}
+
+
+
+
+ );
+ })
+ )}
+
+
+
+
+
+
+
+
수요신청 상세현황
+ {
+ !isEditMode ? (
+
{
+ setOriginData(selectedSdmInfoStatus);
+ setEditData({...selectedSdmInfoStatus});
+ setIsEditMode(true)}
+ }
+ >
+ 수정
+
+ ) : (
+
+ {
+ saveInfo();
+ }}
+ >
+ 저장
+
+ {
+ setEditData(originData);
+ setIsEditMode(false)
+ }}
+ >
+ 취소
+
+
+ )
+ }
+
+
+ {!selectedSdmInfoStatus ? (
+
+ 상세 정보를 표시할 신청건이 없습니다.
+
+ ) : (
+
+
+ 요구수량
+
+
+
+ {topFields.map((field) => (
+
+
+ {field.label}
+ {field.required ? * : null}
+
+ {
+ !isEditMode ?
+
+ {field.key==='useUsgCd'? useTypesData.filter(item=>item.val===field.value)[0]?.text: field.value}
+
+ :
+ {field.key ==='dmndTmng'?
+
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: v,
+ }))
+ } />
+ : field.key ==='useUsgCd'?
+
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: e.target.value,
+ }))
+ }
+ className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
+ >
+ 선택하세요
+ {useTypesData.map((item) => (
+
+ {item.text}
+
+ ))}
+
+
+ :
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: v,
+ }))
+ }>
+ }
+
+ }
+
+ ))}
+
+
+ 요구수질
+
+
+ {waterFields.map((field) => {
+ const { a, b } = editData?.ph ? getRange(editData.ph) : { a: "", b: "" };
+ return (
+
+
+ {field.label}
+
+
+ {
+ !isEditMode ? field.value
+ :
+ field.key === "grssClfg" ? (
+
+
+ setEditData((prev) => ({
+ ...prev,
+ grssClfg: v,
+ }))
+ }
+ disabled={editData?.grssClfg === -1}
+ className={
+ editData?.grssClfg === -1
+ ? "bg-neutral-100 text-neutral-400"
+ : ""
+ }
+ />
+
+ {
+ const checked = e.target.checked;
+
+ setEditData((prev) => ({
+ ...prev,
+ grssClfg: checked ? -1 : "",
+ }));
+ }}
+ className="h-4 w-4 rounded border-neutral-300"
+ />
+ 불검출
+
+
+ ) : field.key === 'smll' ? (
+
+ setEditData((prev) => ({
+ ...prev,
+ grssClfg: v,
+ }))
+ }
+ className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500">
+ 불쾌하지 않을 것
+ 약간 있음
+ 있음
+
+ ) : field.key === 'ph' ? (
+ <>
+
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: `${v}~${b}`,
+ }))
+ }
+ />
+ ~
+
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: `${a}~${v}`,
+ }))
+ }
+ />
+ >
+ ) : (
+ <>
+
+ setEditData((prev) => ({
+ ...prev,
+ [field.key]: v,
+ }))
+ }
+ />
+ {
+ field.key==='tn'||field.key === 'tph'||field.key === 'trbt'||field.key==='blgcOxygDmndQnt'||field.key==='clIonQnt'||field.key==='cdtnv'|| field.key=='crmty'
+ ?<>이하>:<>>
+ }
+ >
+ )
+ }
+
+
+ )})}
+
+
+ 공급처 후보지
+ {
+ isEditMode &&
+ {
+ searchCandidate(e);
+
+ //splyList => 해당객체가 대민에서 선택한 후보처 리스트
+ //setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
+ }}>
+ 후보지 조회
+
+ }
+
+
+
+ {candidateNames.map((candidateName, index) => (
+ isEditMode ? (
+
+
+ 후보지{index + 1}
+
+
+ {
+ setEditData((prev) => {
+ const newList = [...(prev.splyCndtList || [])];
+ newList[index] = {
+ ...newList[index],
+ ruseUtztnFcltNm: v,
+ };
+ return {
+ ...prev,
+ splyCndtList: newList,
+ };
+ });
+ }}
+ />
+
+
+ ) :
+
+
+ 후보지{index + 1}
+
+
+ {candidateName}
+
+
+ ))}
+
+
+ 수요신청 진행상태
+
+
+
+
+ 진행상태
+
+
+ {formatValue(selectedSdmInfoStatus.status)}
+
+
+
+
+ 공급처매칭결과
+
+
+ {formatValue(selectedSdmInfoStatus.ruseUtztnFcltNm)}
+
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/dm_new/app/(with-layout)/example/globalCase/australia/page.tsx b/apps/dm/app/(with-layout)/example/globalCase/australia/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/globalCase/australia/page.tsx
rename to apps/dm/app/(with-layout)/example/globalCase/australia/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/globalCase/eu/page.tsx b/apps/dm/app/(with-layout)/example/globalCase/eu/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/globalCase/eu/page.tsx
rename to apps/dm/app/(with-layout)/example/globalCase/eu/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/globalCase/overview/page.tsx b/apps/dm/app/(with-layout)/example/globalCase/overview/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/globalCase/overview/page.tsx
rename to apps/dm/app/(with-layout)/example/globalCase/overview/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/globalCase/singapore/page.tsx b/apps/dm/app/(with-layout)/example/globalCase/singapore/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/globalCase/singapore/page.tsx
rename to apps/dm/app/(with-layout)/example/globalCase/singapore/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/globalCase/taiwan/page.tsx b/apps/dm/app/(with-layout)/example/globalCase/taiwan/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/globalCase/taiwan/page.tsx
rename to apps/dm/app/(with-layout)/example/globalCase/taiwan/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/layout.tsx b/apps/dm/app/(with-layout)/example/layout.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/layout.tsx
rename to apps/dm/app/(with-layout)/example/layout.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/rain/suwon/page.tsx b/apps/dm/app/(with-layout)/example/rain/suwon/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/rain/suwon/page.tsx
rename to apps/dm/app/(with-layout)/example/rain/suwon/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/bolyeong/page.tsx b/apps/dm/app/(with-layout)/example/sewage/bolyeong/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/bolyeong/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/bolyeong/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/cheongju/page.tsx b/apps/dm/app/(with-layout)/example/sewage/cheongju/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/cheongju/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/cheongju/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/guri/page.tsx b/apps/dm/app/(with-layout)/example/sewage/guri/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/guri/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/guri/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/hwanggu/page.tsx b/apps/dm/app/(with-layout)/example/sewage/hwanggu/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/hwanggu/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/hwanggu/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/paju/page.tsx b/apps/dm/app/(with-layout)/example/sewage/paju/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/paju/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/paju/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/example/sewage/yeosu/page.tsx b/apps/dm/app/(with-layout)/example/sewage/yeosu/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/example/sewage/yeosu/page.tsx
rename to apps/dm/app/(with-layout)/example/sewage/yeosu/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/intro/greywater/page.tsx b/apps/dm/app/(with-layout)/intro/greywater/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/intro/greywater/page.tsx
rename to apps/dm/app/(with-layout)/intro/greywater/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/intro/law/page.tsx b/apps/dm/app/(with-layout)/intro/law/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/intro/law/page.tsx
rename to apps/dm/app/(with-layout)/intro/law/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/intro/layout.tsx b/apps/dm/app/(with-layout)/intro/layout.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/intro/layout.tsx
rename to apps/dm/app/(with-layout)/intro/layout.tsx
diff --git a/apps/dm_new/app/(with-layout)/intro/rain/page.tsx b/apps/dm/app/(with-layout)/intro/rain/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/intro/rain/page.tsx
rename to apps/dm/app/(with-layout)/intro/rain/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/intro/sewage/page.tsx b/apps/dm/app/(with-layout)/intro/sewage/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/intro/sewage/page.tsx
rename to apps/dm/app/(with-layout)/intro/sewage/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/layout.tsx b/apps/dm/app/(with-layout)/layout.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/layout.tsx
rename to apps/dm/app/(with-layout)/layout.tsx
diff --git a/apps/dm_new/app/(with-layout)/status/greywater/page.tsx b/apps/dm/app/(with-layout)/status/greywater/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/status/greywater/page.tsx
rename to apps/dm/app/(with-layout)/status/greywater/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/status/layout.tsx b/apps/dm/app/(with-layout)/status/layout.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/status/layout.tsx
rename to apps/dm/app/(with-layout)/status/layout.tsx
diff --git a/apps/dm_new/app/(with-layout)/status/rain/page.tsx b/apps/dm/app/(with-layout)/status/rain/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/status/rain/page.tsx
rename to apps/dm/app/(with-layout)/status/rain/page.tsx
diff --git a/apps/dm_new/app/(with-layout)/status/sewage/page.tsx b/apps/dm/app/(with-layout)/status/sewage/page.tsx
similarity index 100%
rename from apps/dm_new/app/(with-layout)/status/sewage/page.tsx
rename to apps/dm/app/(with-layout)/status/sewage/page.tsx
diff --git a/apps/dm_new/app/api/postcode/route.ts b/apps/dm/app/api/postcode/route.ts
similarity index 100%
rename from apps/dm_new/app/api/postcode/route.ts
rename to apps/dm/app/api/postcode/route.ts
diff --git a/apps/dm/app/base.tsx b/apps/dm/app/base.tsx
deleted file mode 100644
index bae02acb2..000000000
--- a/apps/dm/app/base.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-"use client";
-
-import { BaseLayout } from "@common/components";
-import type { MenuItem } from "./store/common";
-import { useDmStore } from "./store";
-import { usePathname, useRouter } from "next/navigation";
-
-export default function Base({
- children,
-}: Readonly<{
- children: React.ReactNode;
-}>) {
- const { getMenuAuth } = useDmStore((state) => state);
-
- const router = useRouter();
- const pathname = usePathname();
-
- const handleClick = (id: MenuItem) => {
- getMenuAuth(id.menuCd);
- router.push(id.urlPath);
- };
-
- return (
-
- {children}
-
- );
-}
diff --git a/apps/dm_new/app/globals.css b/apps/dm/app/commonOrigin.css
similarity index 93%
rename from apps/dm_new/app/globals.css
rename to apps/dm/app/commonOrigin.css
index 2fee793a1..fdc8d0eb8 100644
--- a/apps/dm_new/app/globals.css
+++ b/apps/dm/app/commonOrigin.css
@@ -1,5 +1,4 @@
-/* @import "../../../packages/common/components/src/base.css"; */
-/* @import "../../../packages/common/components/src/base.css"; */
+@import "../../../packages/common/components/src/base.css";
@tailwind base;
@tailwind components;
@@ -80,4 +79,4 @@
body {
font-family: "NotoSansKR", sans-serif;
-}
+}
\ No newline at end of file
diff --git a/apps/dm/app/globals.css b/apps/dm/app/globals.css
index f30aa3031..2fee793a1 100644
--- a/apps/dm/app/globals.css
+++ b/apps/dm/app/globals.css
@@ -1,4 +1,5 @@
-@import "../../../packages/common/components/src/base.css";
+/* @import "../../../packages/common/components/src/base.css"; */
+/* @import "../../../packages/common/components/src/base.css"; */
@tailwind base;
@tailwind components;
@@ -68,3 +69,15 @@
@apply bg-background text-foreground;
}
}
+
+@font-face {
+ font-family: "NotoSansKR";
+ src: url("/fonts/NotoSansKR-Regular.woff2") format("woff2");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+
+body {
+ font-family: "NotoSansKR", sans-serif;
+}
diff --git a/apps/dm/app/layout.tsx b/apps/dm/app/layout.tsx
index 7224c9dc2..0e45865e7 100644
--- a/apps/dm/app/layout.tsx
+++ b/apps/dm/app/layout.tsx
@@ -15,7 +15,11 @@ export default function RootLayout({
물재이용 대민
- {children}
+
+
+ {children}
+
+