Merge commit '069e489d031203200fc4d73e470767503a3dc227' into iipbs/wr/dev

This commit is contained in:
leedj312
2025-07-21 18:01:43 +09:00
13 changed files with 557 additions and 278 deletions
+26
View File
@@ -0,0 +1,26 @@
"use client"
import { BaseLayout } from "@common/components/ui";
import { useWrStore } from "./store";
import { MainDiv } from "@common/business_components/ui";
import type { MenuItem } from "./store/common/type/menu";
export default function Base({
children,
changeMenu,
}: Readonly<{
children: React.ReactNode;
changeMenu: (path: string) => void;
}>) {
const { getMenuAuthrts } = useWrStore((state) => state);
return (
<BaseLayout
handleMenuClick={(e: MenuItem) => {
getMenuAutpnhrts(e.menuCd);
changeMenu(e.urlPath);
}}
>
<MainDiv>{children}</MainDiv>
</BaseLayout>
);
}
+18 -18
View File
@@ -1,12 +1,11 @@
'use client'
import { BaseLayout } from "@common/components";
"use client";
import "./globals.css";
import { StoreProvider } from "./store";
import { MainDiv } from "@common/business_components";
import koBase from '../public/locales/ko/message.json';
import { StoreProvider} from "./store";
import koBase from "../public/locales/ko/message.json";
import i18next from "i18next";
import { I18nextProvider } from "react-i18next";
import { useRouter } from "next/navigation";
import Base from "./base";
import { useRouter } from "next/navigation"; // ✅ App Router 전용
export default function RootLayout({
children,
@@ -14,34 +13,35 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
i18next.init({
lng: 'ko',
fallbackLng: 'ko',
lng: "ko",
fallbackLng: "ko",
resources: {
ko: {
base: koBase,
},
}
},
});
const router = useRouter();
const changeMenu = (path: string) => {
router.push(path);
};
return (
<html lang="en">
<body>
<I18nextProvider i18n={i18next}>
<StoreProvider>
<BaseLayout
handleMenuClick={(e) =>{
router.push(e.urlPath)
}}
<Base
changeMenu={changeMenu}
>
<MainDiv>
{children}
</MainDiv>
</BaseLayout>
{children}
</Base>
</StoreProvider>
</I18nextProvider>
</body>
</html >
</html>
);
}
+75
View File
@@ -0,0 +1,75 @@
import { getFetch, getFetchWithCount } from "@/components/lib/api";
import toast from "react-hot-toast";
export const getMenuAuthrts = async (menuCd: number) => {
const response = await getFetch(
"LOCAL",
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
authStateExport.auth = {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
};
};
export type authState = {
auth: {
mngAuthrtYn: string;
inqAuthrtYn: string;
regAuthrtYn: string;
mdfcnAuthrtYn: string;
delAuthrtYn: string;
};
};
export type authType = authState & authActions;
export const authStateExport: authState = {
auth: {
mngAuthrtYn: "Y",
inqAuthrtYn: "Y",
regAuthrtYn: "Y",
mdfcnAuthrtYn: "Y",
delAuthrtYn: "Y",
},
};
export type authActions = {
getMenuAuthrts: (menuCd: number) => void;
};
export const authActionsExport: (set: any) => authActions = (set: any) => {
return {
getMenuAuthrts: async (menuCd: number) => {
try{
const response = await getFetchWithCount( 'LOCAL',
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
set((state: authState) => {
return {
...state,
auth: {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
},
};
});
}catch(e){
toast.error('권한 정보를 가져오는 중 오류가 발생했습니다. 관리자에게 문의하세요.');
console.error('Error fetching menu authorities:', e);
}
},
};
};
@@ -0,0 +1,19 @@
export interface MenuItem {
menuCd: number;
menuKornNm: string;
menuEngNm: string;
parentId: number | null;
sortSeq: number;
iconNm?: string;
children: MenuItem[] | null;
levelNo: number;
urlPath: string;
menuLnkgTypeCd: string;
menuLnkgSn: number;
menuPath: string;
qrcd: string;
useYn: string;
otptYn: string;
newWndwYn: string;
requiredUrlPath: boolean;
}
@@ -42,17 +42,17 @@ const hdtflObjSchema = z.object({
utztnUsg: z
.string()
.min(1, { message: validationMessages.required("이용 용도") }),
grssClfg: z.number().default(0), // Measurements, 0 is fine as default
combResdch: z.number().default(0), // Measurements, 0 is fine as default
trbt: z.number().default(0), // Measurements, 0 is fine as default
bod: z.number().default(0), // Measurements, 0 is fine as default
smll: z.number().default(0), // Measurements, 0 is fine as default
crmty: z.number().default(0), // Measurements, 0 is fine as default
tn: z.number().default(0), // Measurements, 0 is fine as default
tph: z.number().default(0), // Measurements, 0 is fine as default
ph: z.number().default(0), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0), // Measurements, 0 is fine as default
cdtnv: z.number().default(0), // Measurements, 0 is fine as default
grssClfg: z.number().default(0).nullish(), // Measurements, 0 is fine as default
combResdch: z.number().default(0).nullish(), // Measurements, 0 is fine as default
trbt: z.number().default(0).nullish(), // Measurements, 0 is fine as default
bod: z.number().default(0).nullish(), // Measurements, 0 is fine as default
smll:z.string().default('').nullish(), // Measurements, 0 is fine as default
crmty: z.number().default(0).nullish(), // Measurements, 0 is fine as default
tn: z.number().default(0).nullish(), // Measurements, 0 is fine as default
tph:z.number().default(0).nullish(),// Measurements, 0 is fine as default
ph: z.string().default('').nullish(), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0).nullish(), // Measurements, 0 is fine as default
cdtnv: z.number().default(0).nullish(), // Measurements, 0 is fine as default
emsnExitTp: z.number()
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
.default(0), // Changed max to 100 based on message, // Measurements, 0 is fine as default
+10 -4
View File
@@ -98,6 +98,7 @@ import { rainStatActionsExport, rainStatExport, type rainStatState, type rainSta
import { rswtStatActionsExport, rswtStatExport, type rswtStatState, type rswtStatType } from "./stats/rswtStat";
//import { isLogin } from "./common/login";
import { LoginApi } from "@/src/entities/auth";
import { authActionsExport, authStateExport, type authState, type authType } from "./common/auth";
export type WrStore = comCodeType &
pubFacType &
@@ -124,7 +125,9 @@ export type WrStore = comCodeType &
ruseCaseType&
rainStatType&
rswtStatType&
comFcltType
comFcltType &
authType
;
export const StoreZus = (
@@ -153,7 +156,8 @@ export const StoreZus = (
drgtState&
ruseCaseState&
rainStatState&
rswtStatState
rswtStatState&
authState
= {
...comCodeStateExport,
...pubFacStateExport,
@@ -180,7 +184,8 @@ export const StoreZus = (
...ruseCaseStateExport,
...rainStatExport,
...rswtStatExport,
...comFcltStateExport
...comFcltStateExport,
...authStateExport
}
) => {
return createStore<WrStore>()((set) => {
@@ -218,7 +223,8 @@ export const StoreZus = (
...drgtActionsExport(set),
...ruseCaseActionsExport(set),
...rainStatActionsExport(set),
...rswtStatActionsExport(set)
...rswtStatActionsExport(set),
...authActionsExport(set)
};
});
};
+11
View File
@@ -116,6 +116,17 @@ export const handleCommaNumberChange = (
onChange(raw === "" ? "" : Number(raw));
};
export const handleOnlyNumberChange = (
e: React.ChangeEvent<HTMLInputElement>,
onChange: (value: number | "") => void
) => {
// 콤마 제거 후 숫자와 소수점만 남기기
const cleaned = e.target.value.replace(/,/g, "").replace(/[^0-9.]/g, "");
// 숫자 및 소수점 외 입력 차단
if (!/^\d*\.?\d*$/.test(cleaned)) return;
onChange(cleaned === "" ? "" : Number(cleaned));
};
// onchange 텍스트를 숫자로 and ,제거
export const handleMinusNumberChange = (
e: React.ChangeEvent<HTMLInputElement>,
@@ -12,6 +12,7 @@ import {
confirmToast,
formatCommaNumber,
handleCommaNumberChange,
handleOnlyNumberChange,
} from "@/components/lib/func/commom";
import {
Form,
@@ -19,9 +20,9 @@ import {
FormUi,
SelectBox,
} from "@common/business_components/ui";
import { Button, Input, ScrollArea } from "@common/components/ui";
import { Button, Input, Label, ScrollArea } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
@@ -40,6 +41,8 @@ export default function HtdflRegForm({
setIsModal?: (modal: boolean) => void;
}) {
const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state);
const [frontPHdata, setFrontPHdata] = useState("");
const [backPHdata, setBackPHdata] = useState("");
const defaultValues: z.infer<typeof hdtflZod> = hdtflZod.parse({});
@@ -51,7 +54,7 @@ export default function HtdflRegForm({
const bdstNm = form.watch("bdstNm");
const isPowerPlant = bdstNm?.includes("발전소");
const { t } = useTranslation('base');
const { t } = useTranslation("base");
const onSubmit = (values: z.infer<typeof hdtflZod>) => {
if (isModal) {
const data = regCtpvCd ? regCtpvCd : regSggcd ? regSggcd : "noCd";
@@ -63,13 +66,12 @@ export default function HtdflRegForm({
htdflBdstReg(values, data);
if (setIsModal) setIsModal(false);
};
confirmToast(t('isInsertMsg'), handleConfirm);
confirmToast(t("isInsertMsg"), handleConfirm);
} else {
const handleConfirm = async () => {
htdflBdstUpdate(values, "0");
};
confirmToast(t('isUpdateMsg'), handleConfirm);
confirmToast(t("isUpdateMsg"), handleConfirm);
}
};
@@ -97,12 +99,51 @@ export default function HtdflRegForm({
})),
};
};
const isSetFrontPh = useCallback(
(frontPHdata: string) => {
setFrontPHdata(frontPHdata);
if (!frontPHdata) {
form.setValue(`reuseBuilding.0.htdflObj.ph`, "");
return;
}
const PHdata = form.getValues(`reuseBuilding.0.htdflObj.ph`);
let backPH;
if (PHdata && String(PHdata).includes("~")) {
backPH = PHdata.split("~")[1];
} else {
backPH = String(Number(frontPHdata) + 1);
}
const MatchData = `${frontPHdata}~${backPH}`;
form.setValue(`reuseBuilding.0.htdflObj.ph`, MatchData);
},
[form]
);
const isSetBackPh = useCallback(
(backPHdata: string) => {
setBackPHdata(backPHdata);
if (!backPHdata) {
form.setValue(`reuseBuilding.0.htdflObj.ph`, "");
return;
}
const PHdata = form.getValues(`reuseBuilding.0.htdflObj.ph`);
let frontPH;
if (PHdata && String(PHdata).includes("~")) {
frontPH = PHdata.split("~")[0];
} else {
frontPH = String(Math.max(Number(backPHdata) - 1, 0));
}
const MatchData = `${frontPH}~${backPHdata}`;
form.setValue(`reuseBuilding.0.htdflObj.ph`, MatchData);
},
[form]
);
const [divStyle, setDivStyle] = useState("h-[53vh]");
useEffect(() => {
if (isModal === true) {
setDivStyle("h-[53vh] max-h-[37vh]")
setDivStyle("h-[53vh] max-h-[37vh]");
} else if (readonly === true) {
setDivStyle("h-[62vh]")
setDivStyle("h-[62vh]");
} else {
setDivStyle("h-[57vh]")
}
@@ -116,7 +157,25 @@ export default function HtdflRegForm({
useEffect(() => {
// Case 1: 수정 또는 '복사하여 등록' 등 데이터가 주입되는 경우
if (htdflForm && Object.keys(htdflForm).length > 0) {
const htdflPH = htdflForm.reuseBuilding[0]?.htdflObj.ph
if(htdflPH && String(htdflPH).includes("~")){
const [frontPH, backPH] = htdflPH.split("~");
if (frontPH) {
setFrontPHdata(frontPH);
}
if (backPH) {
setBackPHdata(backPH);
}
} else {
setFrontPHdata("");
setBackPHdata("");
}
form.reset(cleanHtdflData(htdflForm));
}else{
form.reset();
}
// Case 2: 데이터가 없는 순수 신규 등록 모드인 경우
if (isModal) {
@@ -125,15 +184,33 @@ export default function HtdflRegForm({
const firstHtdUtztnUsgCd = code.htdUtztnUsgCd?.[0]?.val;
if (firstHtdPowerMthdCd) {
form.setValue('reuseBuilding.0.htdflObj.etgtType', firstHtdPowerMthdCd, { shouldDirty: true });
form.setValue(
"reuseBuilding.0.htdflObj.etgtType",
firstHtdPowerMthdCd,
{ shouldDirty: true }
);
}
if (firstYnValue) {
form.setValue('reuseBuilding.0.htdflObj.plwtYn', firstYnValue as "Y" | "N", { shouldDirty: true });
form.setValue('reuseBuilding.0.htdflObj.prcsProcsYn', firstYnValue as "Y" | "N", { shouldDirty: true });
form.setValue('reuseBuilding.0.htdflObj.fmYn', firstYnValue as "Y" | "N", { shouldDirty: true });
form.setValue(
"reuseBuilding.0.htdflObj.plwtYn",
firstYnValue as "Y" | "N",
{ shouldDirty: true }
);
form.setValue(
"reuseBuilding.0.htdflObj.prcsProcsYn",
firstYnValue as "Y" | "N",
{ shouldDirty: true }
);
form.setValue(
"reuseBuilding.0.htdflObj.fmYn",
firstYnValue as "Y" | "N",
{ shouldDirty: true }
);
}
if (firstHtdUtztnUsgCd) {
form.setValue('reuseBuilding.0.htdflObj.utztnUsg', firstHtdUtztnUsgCd, { shouldDirty: true });
form.setValue("reuseBuilding.0.htdflObj.utztnUsg", firstHtdUtztnUsgCd, {
shouldDirty: true,
});
}
}
}, [htdflForm, code, form]);
@@ -142,12 +219,37 @@ export default function HtdflRegForm({
if (!isPowerPlant) {
form.setValue(
"reuseBuilding.0.htdflObj.etgtType",
"해당없음", // 빈 문자열로 초기화
{ shouldDirty: true } // dirty 상태로 남길지 여부는 취사선택
"해당없음", // 빈 문자열로 초기화
{ shouldDirty: true } // dirty 상태로 남길지 여부는 취사선택
);
}
}, [isPowerPlant, form]);
// useEffect(() => {
// const watchPH = form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.ph`);
// const checkPkAndCycle =
// form.getValues(`items.${indexMng}.pbnstRuseFcltId`) +
// String(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cycl`));
// console.log("checkPkAndCycle ", checkPkAndCycle);
// if (chekcPk !== checkPkAndCycle) {
// if (watchPH && watchPH !== undefined) {
// const [frontPH, backPH] = watchPH.split("~");
// if (frontPH) {
// setFrontPHdata(frontPH);
// }
// if (backPH) {
// setBackPHdata(backPH);
// }
// } else {
// setFrontPHdata("0");
// setBackPHdata("0");
// }
// setCheakPk(checkPkAndCycle);
// }
// }, [form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.ph`), chekcPk]);
return (
<div className="!mx-[1.6rem] !p-0r">
<Form {...form}>
@@ -198,9 +300,13 @@ export default function HtdflRegForm({
label={""}
selectClass={selectClass}
selectArray={code.htdPowerMthdCd}
onSelectValue={readonly ? () => { } : field.onChange}
onSelectValue={readonly ? () => {} : field.onChange}
// value={field.value ?? ""}
value={field.value || (code?.htdPowerMthdCd[0] &&code?.htdPowerMthdCd[0].val)}
value={
field.value ||
(code?.htdPowerMthdCd[0] &&
code?.htdPowerMthdCd[0].val)
}
readOnly={readonly || !isPowerPlant}
/>
</FormUi>
@@ -241,7 +347,7 @@ export default function HtdflRegForm({
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={readonly ? () => { } : field.onChange}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? ""}
readOnly={readonly}
/>
@@ -265,9 +371,9 @@ export default function HtdflRegForm({
className={selectClass}
placeholder=""
type="text"
value={field.value ?? ''}
value={field.value ?? ""}
onChange={(e) => {
if (!readonly) field.onChange((e.target.value));
if (!readonly) field.onChange(e.target.value);
}}
readOnly={readonly}
maxLength={txtSize}
@@ -374,7 +480,7 @@ export default function HtdflRegForm({
label={""}
selectClass={selectClass}
selectArray={code.yn}
onSelectValue={readonly ? () => { } : field.onChange}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? ""}
readOnly={readonly}
/>
@@ -415,7 +521,7 @@ export default function HtdflRegForm({
selectClass={selectClass}
label={""}
selectArray={code.yn}
onSelectValue={readonly ? () => { } : field.onChange}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? ""}
readOnly={readonly}
/>
@@ -456,7 +562,7 @@ export default function HtdflRegForm({
selectClass={selectClass}
label={""}
selectArray={code.htdUtztnUsgCd}
onSelectValue={readonly ? () => { } : field.onChange}
onSelectValue={readonly ? () => {} : field.onChange}
value={field.value ?? ""}
readOnly={readonly}
/>
@@ -477,18 +583,25 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.grssClfg"
render={({ field }) => (
<FormUi label="총대장균군(개/mg)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={6}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -500,18 +613,25 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.tn"
render={({ field }) => (
<FormUi label="총질소(T-N)(mg/L)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder=""
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={6}
/>{" "}
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -548,18 +668,25 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.tph"
render={({ field }) => (
<FormUi label="총인(T-P)(mg/L)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={6}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -573,41 +700,57 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.trbt"
render={({ field }) => (
<FormUi label="탁도(NTU)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
handleOnlyNumberChange(e, field.onChange);
}}
value={
field.value !== undefined && field.value !== 0
? `${field.value}`
: ""
}
readOnly={readonly}
maxLength={6}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이상
</span>
</div>
</div>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.ph"
name="reuseBuilding.0.htdflObj.crmty"
render={({ field }) => (
<FormUi label="수소이온농도(PH)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<FormUi label="색도(도)">
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이상
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -621,18 +764,25 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.bod"
render={({ field }) => (
<FormUi label="생물학적 산소요구량(mg/L)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -644,18 +794,25 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.clIonQnt"
render={({ field }) => (
<FormUi label="염화물(mg/L)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={7}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
@@ -669,69 +826,83 @@ export default function HtdflRegForm({
name="reuseBuilding.0.htdflObj.smll"
render={({ field }) => (
<FormUi label="냄새">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
<SelectBox
className={"!text-2xl"}
label={""}
selectClass={undefined}
selectArray={[
{ val: "불쾌하지않을것", text: "불쾌하지않을것" },
{ val: "없음", text: "없음" },
]}
readOnly={readonly}
maxLength={numSize}
/>
onSelectValue={(val) => {
field.onChange(val);
}}
value={field.value || undefined}
></SelectBox>
</FormUi>
)}
/>
</div>
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.cdtnv"
render={({ field }) => (
<FormUi label="전기전도도(μs/cm)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<div className="flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
<span className="text-xl absolute inset-y-0 right-3 flex items-center text-gray-500 pointer-events-none">
이하
</span>
</div>
</div>
</FormUi>
)}
/>
</div>
</div>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-6">
<FormField
control={form.control}
name="reuseBuilding.0.htdflObj.crmty"
render={({ field }) => (
<FormUi label="색도(도)">
<Input
className={selectClass}
placeholder="10"
type="text"
onChange={(e) => {
if (!readonly)
handleCommaNumberChange(e, field.onChange);
}}
value={formatCommaNumber(field.value)}
readOnly={readonly}
maxLength={numSize}
/>
</FormUi>
)}
/>
<Label className="text-xl">수소이온농도(PH)</Label>
<div className="flex flex-row items-center gap-2">
<Input
className="!text-xl w-1/2"
placeholder=""
type="text"
onChange={(e) =>
//setFrontPHdata(e.target.value)
isSetFrontPh(e.target.value)
}
value={frontPHdata ?? ""}
readOnly={readonly}
maxLength={3}
/>
<span className="text-xl">~</span>
<Input
className="!text-xl w-1/2"
placeholder=""
type="text"
onChange={(e) => {
isSetBackPh(e.target.value);
}}
value={backPHdata ?? ""}
readOnly={readonly}
maxLength={3}
/>
</div>
</div>
</div>
</ScrollArea>
+56 -35
View File
@@ -4,7 +4,7 @@ import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
// const CLAIM_NAME = "Authorization";
// const AUTH_USER_ID = "token-id";
const serverApiUrl = CS_URL;
const serverApiUrl = CS_URL;
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
@@ -25,28 +25,43 @@ export function middleware(req: NextRequest) {
if (pathname.startsWith("/server")) {
const decodedParams = Array.from(searchParams.entries())
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
.join("&");
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
.join("&");
console.log(' sesesesesesevereveraserwereqwrewbrtkehjwrtg server')
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
decodedParams ? `?${decodedParams}` : ""
}`;
return NextResponse.rewrite(new URL(newUrl));
}
if (pathname === "/") {
console.log(" maim 화면이동 ");
return NextResponse.redirect(new URL("/map/mainMap", req.url));
}
if (pathname.startsWith("/cms-service")) {
const requestHeaders = new Headers(req.headers);
const Authorization = req.cookies.get(CLAIM_NAME)?.value;
const newUrl = `${serverApiUrl}${pathname}`;
console.log("Authorization cms-service ", Authorization);
if (Authorization) requestHeaders.set(CLAIM_NAME, Authorization);
//if (userId) requestHeaders.set(AUTH_USER_ID, userId);
console.log(
`[Middleware] Rewriting from '/cms-service' path to: ${newUrl}`
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
}
if (pathname.startsWith("/attach-service")) {
const requestHeaders = new Headers(req.headers);
// const token =
// "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhMGVjN2EwNy03NTJhLTRmN2YtODlhNi04NTRlNjNkMmUzYTAiLCJhdXRob3JpdGllcyI6IlJPTEVfQURNSU4sUk9MRV9BTk9OWU1PVVMiLCJtbmdyWW4iOiJZIiwiZXhwIjoxNzUyMTM1OTk3fQ.itGY6M6mC2iwKvEpXw2KS6354T_B_OFz4hD26ResIgwJhpwiDJCMuSXD-rZ3AarmAIKd0udpCuJR7UlvFMN2ww";
// const userId = "a0ec7a07-752a-4f7f-89a6-854e63d2e3a0";
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${serverApiUrl}${pathname}`;
//wr:dev: pathname /attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad
//http://dev.onasys.co.kr:8000/attach-service/api/v1/tus/upload/5e60b70a-956e-40c3-be5a-cdd3100bf8ad
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
@@ -66,7 +81,7 @@ export function middleware(req: NextRequest) {
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders
headers: requestHeaders,
});
}
@@ -78,33 +93,31 @@ export function middleware(req: NextRequest) {
let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
if (
typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설
|| typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역
|| typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도
|| typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
typename == "hasudo:tb_il_swage_prcs_fclt" || //하수처리시설
typename == "hasudo:tb_ms_swage_prcs_zone" || //하수처리구역
typename == "hasudo:tb_ms_sbmrg_dngr_map" || //침수위험지도
typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
) {
newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
}
else if (
typename == "hasudo:tb_ms_swage_ppl" //관로
|| typename == "hasudo:tb_ms_swage_mhl" //맨홀
|| typename == "hasudo:tb_ms_swage_pmstn" //펌프장
|| typename == "hasudo:tb_ms_stofcbr" //우수토실
} else if (
typename == "hasudo:tb_ms_swage_ppl" || //관로
typename == "hasudo:tb_ms_swage_mhl" || //맨홀
typename == "hasudo:tb_ms_swage_pmstn" || //펌프장
typename == "hasudo:tb_ms_stofcbr" //우수토실
) {
newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
}
else if (
typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실
|| typename == "hasudo:om_wqlt_station" //관측소
|| typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설
|| typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설
|| typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설
|| typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설
|| typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처
|| typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
){
} else if (
typename == "hasudo:om_dt_untrt_swg_rain" || //중점우수토실
typename == "hasudo:om_wqlt_station" || //관측소
typename == "hasudo:tb_wr_bdst_ruse_fclt" || //재이용시설
typename == "hasudo:tb_wr_rnwtr_utztn_fclt" || //빗물이용시설
typename == "hasudo:tb_wr_rswtr_fclt" || //중수도시설
typename == "hasudo:tb_wr_htdfl_fclt" || //온배수시설
typename == "hasudo:tb_wr_ruse_fclt_dmd_co" || //수요처
typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
) {
const url = req.nextUrl.clone();
url.searchParams.set('TYPENAME', typename.replace("hasudo:",""));
url.searchParams.set("TYPENAME", typename.replace("hasudo:", ""));
queryParams = url.searchParams.toString();
newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`;
}
@@ -115,8 +128,9 @@ export function middleware(req: NextRequest) {
if (pathname.startsWith("/sewerage-service")) {
const queryParams = searchParams.toString();
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${queryParams ? `?${queryParams}` : ""
}`;
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
@@ -125,5 +139,12 @@ export function middleware(req: NextRequest) {
}
export const config = {
matcher: ["/server/:path*", "/user-service/:path*", "/attach-service/:path*", "/geoserver/:path*", "/sewerage-service/:path*"],
matcher: [
"/cms-service/:path*",
"/server/:path*",
"/user-service/:path*",
"/attach-service/:path*",
"/geoserver/:path*",
"/sewerage-service/:path*",
],
};
@@ -29,23 +29,18 @@ export class LoginApiService implements LoginService {
const accessToken = response.headers.get(ACCESS_TOKEN);
const tokenId = response.headers.get(AUTH_USER_ID);
console.log('accessToken ', accessToken)
console.log('tokenId ', tokenId)
if (!accessToken || !tokenId) {
throw new Error("Missing required authentication tokens");
}
tokenManager.setToken(CLAIM_NAME, accessToken);
tokenManager.setToken(AUTH_USER_ID, tokenId);
tokenManager.setToken("site-id-key", SITE_ID);
tokenManager.setToken(CLAIM_NAME, accessToken)
tokenManager.setToken(AUTH_USER_ID, tokenId)
tokenManager.setToken("site-id-key", SITE_ID)
tokenManager.setupRefreshTimer(
() => this.silentRefresh(),
JWT_EXPIRATION_TIME,
);
} catch (error) {
console.log(' login err ' , error)
throw new Error(`Login failed: ${error}`);
}
}
@@ -67,7 +62,7 @@ export class LoginApiService implements LoginService {
if (!accessToken || !tokenId) {
throw new Error("Missing required authentication tokens");
}
tokenManager.setToken(CLAIM_NAME, accessToken);
tokenManager.setToken(AUTH_USER_ID, tokenId);
} catch (error) {
@@ -1,45 +0,0 @@
// "use client";
// import { useEffect } from "react";
// import { useRouter } from "next/navigation";
// import { LoginApi } from "../api/login-service";
// const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
// const router = useRouter();
// const { userInfo, setUserInfo } = useUserStore((state) => state);
// async function authCheck() {
// const userId = getCookie(AUTH_USER_ID);
// if (!userId) {
// throw new Error("Missing required authentication tokens");
// }
// const response = await client.get<IUserInfo>(
// `${ENDPOINT.USER_SERVICE.USERS}/info/me`,
// );
// if (!userInfo) setUserInfo(response.data);
// }
// useEffect(() => {
// (async () => {
// try {
// await authCheck();
// } catch (error) {
// console.error(error);
// try {
// await LoginApi.silentRefresh();
// await authCheck();
// } catch (refreshError) {
// console.error("Token refresh failed:", refreshError);
// setUserInfo(null);
// router.push("/auth/login");
// }
// }
// })();
// }, []);
// return <>{children}</>;
// };
// export { AuthWrapper };
+1 -1
View File
@@ -1,6 +1,6 @@
import { PROXY_HOST } from "./env";
export const BASE_URL = `${PROXY_HOST}/server`;
export const BASE_URL = `${PROXY_HOST}`;
export const ENDPOINT = {
ATTACH_SERVICE: {