수요정보 등록 수질 입력 부분 validation check

This commit is contained in:
leedj312
2025-08-01 14:38:38 +09:00
parent e54a0d0ef8
commit ca9f2da83c
4 changed files with 130 additions and 63 deletions
@@ -1,7 +1,7 @@
"use client";
import { useDmStore } from "@/app/store";
import type { DemandCompBizType } from "@/app/store/sdmMng";
import { sdmInfoFormSchema, type DemandCompBizType, type SdmInfoFormData } from "@/app/store/sdmMng";
import { SdmInfoRegForm } from "@/components/ui/sdmInfoInsertForm/component";
import { UiModal } from "@common/business_components/ui/dialog_ui";
import { Button } from "@common/components/ui";
@@ -10,48 +10,9 @@ import { useRouter, useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
import { FormProvider, useForm, type SubmitHandler } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import * as z from "zod";
import SdmLogin from "../sdmLogin/page";
const sdmInfoFormSchema = z.object({
sdmNm: z.string().min(1, "수요처를 선택해주세요"),
demandYmd: z.date().optional(),
demandQnty: z
.string()
.min(1, "요구수량을 입력해주세요")
.regex(/^[0-9]+$/, "요구수량은 숫자만 입력 가능합니다"),
usePurpose: z.string().optional(),
money: z.string().regex(/^[0-9]+$/, "희망단가는 숫자만 입력 가능합니다"),
/**요구수질 */
reqWaterQlty: z.string().optional(),
grssClfg: z.number().nullish(),
tn: z.number().nullish(),
combResdch: z.number().nullish(),
tph: z.number().nullish(),
trbt: z.number().nullish(),
ph: z.string().nullish(),
bod: z.number().nullish(),
/**컬럼확인필요 */
clIonQnt: z.number().nullish(),
smll: z.string(),
cdtnv: z.number().nullish(),
crmty: z.number().nullish(),
rmrk: z.string().optional(),
/* telno: z.string()
.min(8, "전화번호는 8자 이상 입력해주세요")
.max(12, "전화번호는 12자 이내로 입력해주세요")
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), */
candidateTitle: z
.array(
z.object({
ruseUtztnFcltNm: z.string(),
ruseUtztnFcltCd: z.string(),
})
)
.optional(),
});
export type FormData = z.infer<typeof sdmInfoFormSchema>;
function SdmInfoInsertContent() {
const searchParams = useSearchParams();
@@ -62,7 +23,7 @@ function SdmInfoInsertContent() {
const [isLogin, setIsLogin] = useState(false);
const [facilityName, setFacilityName] = useState<string>("");
const [defaultValues, setDefaultValues] = useState<FormData>({
const [defaultValues, setDefaultValues] = useState<SdmInfoFormData>({
sdmNm: "",
demandYmd: new Date(),
demandQnty: "",
@@ -109,10 +70,10 @@ function SdmInfoInsertContent() {
setSubsInfo([]); // subsInfo 초기화 후보지 처음 화면 들어올때 초기화(스토어내 객체만)
}, [setSubsInfo]);
const form = useForm<FormData>({
const form = useForm<SdmInfoFormData>({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onChange",
mode: "onSubmit",
});
const subsInfo = useDmStore((state) => state.map.subsInfo);
@@ -167,7 +128,7 @@ function SdmInfoInsertContent() {
}
}, [ismoneyDisabled, form]);
const router = useRouter();
const onSubmit: SubmitHandler<FormData> = async (data) => {
const onSubmit: SubmitHandler<SdmInfoFormData> = async (data) => {
try {
const dmSubsInfo = data.candidateTitle?.filter(
(item) => item.ruseUtztnFcltNm && item.ruseUtztnFcltCd
+106 -3
View File
@@ -3,10 +3,105 @@ import type { SelectDataType } from "@common/business_components/ui";
import type { TableUpperProps } from "../common";
import toast from "react-hot-toast";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import { z } from "zod";
/**====================================
* Store - 타입
=====================================*/
export const sdmInfoFormSchema = z.object({
sdmNm: z.string().min(1, "수요처를 선택해주세요"),
demandYmd: z.date().optional(),
demandQnty: z
.string()
.min(1, "요구수량을 입력해주세요")
.regex(/^[0-9]+$/, "요구수량은 숫자만 입력 가능합니다"),
usePurpose: z.string().optional(),
money: z.string().regex(/^[0-9]+$/, "희망단가는 숫자만 입력 가능합니다"),
/**요구수질 */
reqWaterQlty: z.string().optional(),
grssClfg: z.number().nullish(),
tn: z.number().nullish(),
combResdch: z.number().nullish(),
tph: z.number().nullish(),
trbt: z.number().nullish(),
ph: z.string().nullish(),
bod: z.number().nullish(),
/**컬럼확인필요 */
clIonQnt: z.number().nullish(),
smll: z.string(),
cdtnv: z.number().nullish(),
crmty: z.number().nullish(),
rmrk: z.string().optional(),
/* telno: z.string()
.min(8, "전화번호는 8자 이상 입력해주세요")
.max(12, "전화번호는 12자 이내로 입력해주세요")
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), */
candidateTitle: z
.array(
z.object({
ruseUtztnFcltNm: z.string(),
ruseUtztnFcltCd: z.string(),
})
)
.optional(),
}).superRefine((data, ctx) => {
const disabledFields = disableRules[data.usePurpose ?? ""] || [];
// 모든 수질 관련 필드
// 총대장균군수는 null 값을 불검출로 표시하기때문에 해당 검사에서 제외함.
const allFields: (keyof typeof data)[] = [
"tn", "combResdch", "tph",
"trbt", "ph", "bod", "clIonQnt",
"smll", "cdtnv", "crmty",
];
const allFieldsName: Record<string, string> = {
tn: "총질소(T-N)(mg/L)",
combResdch: "결합잔류염소(mg/L)",
tph: "총인(T-P)(mg/L)",
trbt: "탁도(NTU)",
ph: "수소이온농도(pH)",
bod: "생물학적 산소요구량(BOD)(mg/L)",
clIonQnt: "염화물(mgCL/L)",
smll: "냄새",
cdtnv: "전기전도도(㎲/cm)",
crmty: "색도(도)",
};
allFields.forEach((field) => {
const isDisabled = disabledFields.includes(field);
if (!isDisabled) {
// disabled가 아닌 필드는 반드시 값이 있어야 함
const value = data[field];
if (typeof value === "string") {
if (value.trim().length === 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [field],
message: `${allFieldsName[field]} 을 입력해주세요.`,
});
}
} else if (typeof value === "number") {
if (value <= 0 || Number.isNaN(value)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [field],
message: `${allFieldsName[field]} 을 입력해주세요.`,
});
}
} else if (value == null) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: [field],
message: `${allFieldsName[field]} 을 입력해주세요.`,
});
}
}
});
});
export type SdmInfoFormData = z.infer<typeof sdmInfoFormSchema>;
// 수요처 정보
export type DemandCompBizType = {
index : number | '';
@@ -167,8 +262,8 @@ const demandCompList : DemandCompType[] = [
const disableRules: DisableRulesType = {
"청소": ["tn", 'tph',"clIonQnt" ,'cdtnv'],
"화장실용수": ["tn", 'tph',"clIonQnt" ,'cdtnv'],
"세척": ["combResdch", "crmty",'tph',"clIonQnt",'cdtnv'],
"살수": ["combResdch", "crmty",'tph',"clIonQnt",'cdtnv'],
"세척": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'],
"살수": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'],
"조경": ["combResdch", "crmty",'tph','cdtnv'],
"농업_간접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'],
"농업_직접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'],
@@ -184,6 +279,7 @@ const disableRules: DisableRulesType = {
bod: 5,
crmty: 20,
ph: "5.8~8.5",
smll:'불쾌하지않을것',
tn:null,
tph: null,
clIonQnt: null,
@@ -196,6 +292,7 @@ const disableRules: DisableRulesType = {
trbt: 2,
bod: 5,
crmty: 20,
smll:'불쾌하지않을것',
ph: "5.8~8.5",
tn:null,
tph: null,
@@ -208,6 +305,7 @@ const disableRules: DisableRulesType = {
bod: 5,
ph: "5.8~8.5",
combResdch:null,
smll:'불쾌하지않을것',
crmty:null,
tn:null,
tph: null,
@@ -219,6 +317,7 @@ const disableRules: DisableRulesType = {
trbt: 2,
bod: 5,
ph: "5.8~8.5",
smll:'불쾌하지않을것',
combResdch:null,
crmty:null,
tn:null,
@@ -231,6 +330,7 @@ const disableRules: DisableRulesType = {
trbt: 2,
bod: 5,
ph: "5.8~8.5",
smll:'불쾌하지않을것',
clIonQnt:250,
combResdch:null,
crmty:null,
@@ -254,6 +354,7 @@ const disableRules: DisableRulesType = {
grssClfg: null,
trbt: 2,
bod: 8,
smll:'불쾌하지않을것',
ph: "5.8~8.5",
cdtnv:700,
combResdch:null,
@@ -267,6 +368,7 @@ const disableRules: DisableRulesType = {
bod: 5,
tn: 10,
tph: 0.5,
smll:'불쾌하지않을것',
ph: "5.8~8.5",
combResdch:null,
trbt:null,
@@ -274,13 +376,14 @@ const disableRules: DisableRulesType = {
clIonQnt: null,
cdtnv:null
},
공업:{
'공업':{
grssClfg: null,
bod: null,
tn: null,
tph: null,
ph: "~",
combResdch:null,
smll:'불쾌하지않을것',
trbt:null,
crmty:null,
clIonQnt: null,