diff --git a/packages/common/business_components/src/ui/chart/DoubleBar.tsx b/packages/common/business_components/src/ui/chart/DoubleBar.tsx
new file mode 100644
index 000000000..9627485aa
--- /dev/null
+++ b/packages/common/business_components/src/ui/chart/DoubleBar.tsx
@@ -0,0 +1,137 @@
+"use client"
+import { Bar, BarChart, CartesianGrid, Legend, XAxis, YAxis } from "recharts";
+import {
+ ChartContainer,
+ ChartTooltip,
+ type ChartConfig
+} from "./lib/chart";
+import type { CSSProperties } from "react";
+
+const customStyles = {
+ legendContainer: {
+ display: "flex",
+ justifyContent: "center",
+ gap: "20px",
+ fontSize: "9px",
+ fontWeight : "bold",
+ marginTop: "10px",
+ } as CSSProperties,
+};
+
+type BarChartProps = {
+ chartConfig: ChartConfig | undefined;
+ chartData: any[] | undefined;
+ chartTitle: string;
+ chartClassName?: string;
+ graphSize?: number;
+ yFieldText?: string;
+ showLegend?: boolean;
+}
+
+// 커스텀 툴팁
+const CustomTooltip = ({
+ active,
+ payload,
+ label
+}: {
+ active?: boolean;
+ payload?: Array<{
+ name: string;
+ value: number;
+ color: string;
+ }>;
+ label?: string;
+}) => {
+ if (active && payload && payload.length) {
+ return (
+
+
{label}
+ {payload.map((item, index) => (
+
+ {/* 색상 박스 추가 (범례 색상과 동일) */}
+
+
{` ${item.name} : ${item.value}`}
+
+ ))}
+
+ );
+ }
+ return null;
+};
+
+// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
+const CustomLegend = (props: any) => {
+ const { payload } = props;
+ return (
+
+ {payload.map((entry: unknown, index: number) => (
+
+
+
{(entry as {value: string}).value}
+
+ ))}
+
+ );
+};
+
+export default function DoubleBarChart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) {
+ const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"];
+ const getMaxValue = (data: any[], keyPrefix: string) => {
+ return Math.max(...data.map(item =>
+ Object.keys(item)
+ .filter(key => key.includes(keyPrefix))
+ .map(key => item[key])
+ ).flat());
+ };
+ console.log(chartData);
+ const barMaxValue = getMaxValue(chartData, 'barVal');
+ return (
+ <>
+ {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
+
+ {chartTitle}
+
): null}
+
+
+
+
+
+
+ }/>
+ {chartData && chartData.length > 0 && (
+ Object.keys(chartData[0])
+ .filter(key => key !== "label")
+ .map((key, idx) => (
+
+ ))
+ )}
+ {showLegend &&
+
+
+ >
+ )
+}
\ No newline at end of file
diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx
index f88b35476..d666f1d14 100644
--- a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx
+++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx
@@ -1,27 +1,116 @@
"use client"
import { Pie, PieChart } from "recharts";
-
+import type { CSSProperties } from "react";
import {
Card,
CardContent
} from "./lib/card";
-import {
- ChartContainer,
- ChartTooltip,
- ChartTooltipContent,
- type ChartConfig
-} from "./lib/chart";
+import { ChartContainer, ChartTooltip } from "./lib/chart";
-// eslint-disable-next-line react-refresh/only-export-components
-export default (props: { chartConfig: ChartConfig; chartData: never[] | undefined; chartTitle:string}) =>{
+type ChartDataItem = {
+ label: string;
+ val: number;
+};
+
+type ChartConfigItem = {
+ label: string;
+ fill: string;
+};
+
+type ChartConfig = {
+ [key: string]: ChartConfigItem;
+};
+
+type TooltipPayloadItem = {
+ name: string;
+ value: number;
+ payload: {
+ label: string;
+ val: number;
+ fill: string;
+ };
+};
+
+type LegendEntry = {
+ value: string;
+ fill: string;
+ type?: string;
+ payload: {
+ label: string;
+ val: number;
+ fill: string;
+ };
+};
+
+type LegendProps = {
+ payload?: LegendEntry[];
+};
+
+/** 차트 기본 스타일 */
+const customStyles = {
+ legendContainer: {
+ display: "flex",
+ justifyContent: "center",
+ gap: "20px",
+ fontSize: "9px",
+ fontWeight: "bold",
+ marginTop: "10px",
+ } as CSSProperties,
+};
+
+// 커스텀 툴팁
+const CustomTooltip = ({ active, payload, label }: {
+ active?: boolean;
+ payload?: TooltipPayloadItem[];
+ label?: string
+}) => {
+ if (active && payload && payload.length) {
+ return (
+
+
{label}
+ {payload.map((item, index) => (
+
+
+
{` ${item.name} : ${item.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`}
+
+ ))}
+
+ );
+ }
+ return null;
+};
+
+// 커스텀 범례
+const CustomLegend = (props: LegendProps) => {
+ const { payload } = props;
+ if (!payload) return null;
+
+ return (
+
+ {payload.map((entry: LegendEntry, index: number) => (
+
+ ))}
+
+ );
+};
+
+export default function DoughnutChart(props: {
+ chartConfig: ChartConfig;
+ chartData: ChartDataItem[] | undefined;
+ chartTitle: string
+}) {
return (
- {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
-
- {props.chartTitle}
-
): null}
+ {props.chartTitle && props.chartTitle !== "" && props.chartTitle !== "undefined" ? (
+
+ {props.chartTitle}
+
+ ) : null}
}
+ content={}
/>
({
+ ...item,
+ fill: props.chartConfig[item.label]?.fill || 'blue'
+ }))}
dataKey="val"
nameKey="label"
innerRadius={60}
/>
+
- )
+ );
}
diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx
index 2e83458d8..8c7202104 100644
--- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx
+++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx
@@ -92,9 +92,6 @@ export default function LineBarchart({
return 데이터가 없습니다.
;
}
- const barConfig = chartConfig?.barVal;
- const lineConfig = chartConfig?.lineVal;
-
// 데이터의 최대값 계산
const getMaxValue = (data: any[], keyPrefix: string) => {
return Math.max(...data.map(item =>
diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx
index 0d17a99b1..4c58549cc 100644
--- a/packages/common/business_components/src/ui/chart/index.tsx
+++ b/packages/common/business_components/src/ui/chart/index.tsx
@@ -5,6 +5,7 @@ import DoughnutChart from './DoughnutChart';
import { type ChartConfig } from './lib/chart';
import LineChart from './LineChart';
import LineBarchart from './LineBarChart';
+import DoubleBarChart from './DoubleBar';
/**차트 FORM */
const CHART_FORM = {
@@ -15,7 +16,9 @@ const CHART_FORM = {
/**도넛 차트 형태*/
DN :'daughnut',
/**LINE,BAR차트 */
- LINEBAR : 'linebar'
+ LINEBAR : 'linebar',
+ /**더블바 차트 형태*/
+ DOUBLEBAR : 'doublebar'
};
/**
@@ -61,6 +64,10 @@ function MainChart(props: MainChartProps) {
return (
);
+ } else if (props.chartForm == CHART_FORM.DOUBLEBAR){
+ return (
+
+ );
}
}
diff --git a/packages/common/business_components/src/ui/form_ui/index.tsx b/packages/common/business_components/src/ui/form_ui/index.tsx
index 96214b6cf..27b74bd97 100644
--- a/packages/common/business_components/src/ui/form_ui/index.tsx
+++ b/packages/common/business_components/src/ui/form_ui/index.tsx
@@ -39,7 +39,7 @@ const FormUi = React.forwardRef(
checked={isChecked}
onCheckedChange={handleCheckboxChange}
/>
-
+
{children}
diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx
index c5c0ee0db..6ccaac946 100644
--- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx
+++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx
@@ -2,10 +2,43 @@
import { SelectBox } from "../select";
import { Input } from "@common/components";
import { FormUi } from "../form_ui";
-import { useCallback, useState } from "react";
+import { useCallback, useEffect, useState } from "react";
import type { ControllerRenderProps, UseFormReturn } from "react-hook-form";
import { SdmSearchForm } from "./sdmNameBox";
-import type { DemandCompType } from "../../../../../../apps/dm/app/store/sdmMng";
+import { set } from "date-fns";
+
+// 수요처 정보
+export type DemandCompType = {
+ index : number | '';
+ dmdInfoPk : string | '';
+ dmdInfoFk : string | '';
+ bzmnNo : string | '';
+ dmdCoNm : string | '';
+ daddr : string | '';
+ telno : string | '';
+ rgtr : string | '';
+ useUsgCd : string | '';
+ ctpvCd : string | '';
+ sggCd : string | '';
+ dmdAplyPswd : string | '';
+ wntUntprc : number | '';
+ dmndTmng : Date | '';
+ dmndQty : number | '';
+ grssClfg : number | '';
+ tn : number | '';
+ combResdch : number | '';
+ tph : number | '';
+ trbt : number | '';
+ ph : number | '';
+ blgcOxygDmndQnt : number | '';
+ clIonQnt : number | '';
+ smll : number | '';
+ cdtnv : number | '';
+ crmty : number | '';
+ rmrk : string | '';
+ reqDt : string | '';
+}
+
type SelectDataType = {
text: string;
@@ -32,16 +65,19 @@ interface SdmRegFormProps {
dataList: DemandCompType[];
rtnValue: (searchText: string, pageCnt: number) => void;
setIsDuplicate: (value: boolean) => void;
+ updateList?:DemandCompType
}
type FieldComponent = (field: ControllerRenderProps) => JSX.Element;
-export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigunData, dataList, rtnValue, setIsDuplicate }: SdmRegFormProps) {
+export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigunData, dataList, rtnValue, setIsDuplicate ,updateList}: SdmRegFormProps) {
const [sidoValue, setSidoValue] = useState('');
const [sigunValue, setSigunValue] = useState('');
const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]);
const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true);
const [orgBznmNo, setOrgBznmNo] = useState('');
+ const [updateCoNm, setUpdateNm] = useState('')
+
const initializeSigunData = useCallback((sidoCd: string) => {
if (sidoCd === '0') {
@@ -62,6 +98,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
type: string,
field: ControllerRenderProps | undefined
) => {
+ if(!updateList?.dmdInfoPk)
switch (type) {
case "ctpvCd":
initializeSigunData(val);
@@ -77,7 +114,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
form.setValue('bznmTotNo', '');
break;
}
- }, [initializeSigunData, form]);
+ }, [initializeSigunData, form,updateList]);
const handleCheckboxChange = useCallback((checked: boolean) => {
setIsBznmTotNoDisabled(checked);
@@ -90,6 +127,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
const setRtnData = useCallback((item: DemandCompType) => {
const rtnCompData = item;
+
form.setValue('dmdCoNm', rtnCompData?.dmdCoNm || '');
form.setValue('dmdInfoPk', rtnCompData?.dmdInfoPk.substring(0,10) || '');
form.setValue('bznmTotNo', rtnCompData?.dmdInfoPk.substring(10,14)==='0000' ? '' : rtnCompData?.dmdInfoPk.substring(10,14));
@@ -124,26 +162,46 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
form.setValue('dmdCoNm', text);
}, [form]);
- const bznNoChange = useCallback((e: React.ChangeEvent, noType: string) => {
+ const bznNoChange = useCallback((e: React.ChangeEvent, noType: string, field?: ControllerRenderProps) => {
const value = e.target.value;
-
+
if(noType === 'dmdInfoPk') {
if(orgBznmNo.substring(0,10) === value) {
setIsDuplicate(true);
} else {
setIsDuplicate(false);
}
- form.setValue('dmdInfoPk', value);
+ field?.onChange(value);
} else {
if(orgBznmNo.substring(10,14) === value) {
setIsDuplicate(true);
} else {
setIsDuplicate(false);
}
- form.setValue('bznmTotNo', value);
+ field?.onChange(value);
}
+ }, [orgBznmNo, setIsDuplicate]);
- }, [form, orgBznmNo, setIsDuplicate]);
+
+ useEffect(() => {
+ if(updateList?.dmdInfoPk){
+ form.reset(updateList)
+ if(updateList?.dmdInfoPk.slice(10,14) !== '0000') {
+ handleCheckboxChange( false);
+ form.setValue('bznmTotNo', updateList?.dmdInfoPk.slice(10,14));
+ } else {
+ handleCheckboxChange( true);
+ }
+ form.setValue('dmdInfoPk', updateList?.dmdInfoPk.slice(0,10));
+ setUpdateNm(updateList?.dmdCoNm)
+ // setSigunValue(updateList?.sggCd)
+ }else{
+ handleCheckboxChange( true);
+ }
+ }, [updateList,form]);
+
+ useEffect(() => {
+ },[form.formState.errors])
const formFields: Array<{
name: keyof FormSchema;
@@ -154,28 +212,51 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
name: "ctpvCd",
label: "시도",
component: (field) => (
- {field.onChange(e.target.value)} }
+ value={ selectSidoData.find(item => item.val === field.value?.trim() )?.text??''}
+ readOnly={!!updateList?.dmdInfoPk}
+ />
+ ) : (
+ onSelectValue(val.toString(), 'ctpvCd', field)}
+ onSelectValue={(val) => onSelectValue(val.toString(), 'ctpvCd', field??'')}
className=""
value={sidoValue}
/>
+ )
)
},
{
name: "sggCd",
label: "시군구",
component: (field) => (
+ updateList?.dmdInfoPk ? (
+ {field.onChange(e.target.value)} }
+ value={selectSigunData.find(item => item.val === field.value?.trim() )?.text??''}
+ readOnly={!!updateList?.dmdInfoPk}
+ />
+ ) : (
onSelectValue(val.toString(), 'sggCd', field)}
className=""
value={sigunValue}
/>
+
+ )
)
},
{
@@ -185,7 +266,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
bznNoChange(e, 'dmdInfoPk')}
+ onChange={(e) => bznNoChange(e, 'dmdInfoPk', field)}
+ value={field.value}
+ readOnly={!!updateList?.dmdInfoPk}
/>
)
},
@@ -220,8 +302,10 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
type="text"
className="!mt-5 !text-xl text-gray-600"
disabled={isBznmTotNoDisabled}
- {...field}
- onChange={(e) => bznNoChange(e, 'bznmTotNo')}
+ onChange={(e) => bznNoChange(e, 'bznmTotNo', field)}
+ // {...field}
+ value={ field.value}
+ readOnly={!!updateList?.dmdInfoPk}
/>
)
@@ -250,6 +334,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
/>
)
},
+
{
name: "dmdAplyPswd",
label: "패스워드",
@@ -262,11 +347,18 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
/>
)
}
- ];
+ ]
return (
<>
- {formFields.map(({ name, label, component }) => (
+ {formFields.filter((item)=>
+ {if(updateList?.dmdInfoPk){
+ return item.name !== 'dmdAplyPswd'
+ }else{
+ return item
+ }
+ })
+ .map(({ name, label, component }) => (
void;
rtnInputData: (data: DemandCompType) => void;
bodyClassNm?: string;
+ updateList?:DemandCompType
}
-const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean) => z.object({
+const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,updateList?:DemandCompType) => z.object({
ctpvCd: z.string().min(1, "시/도를 선택해주세요"),
sggCd: z.string().min(1, "시/군/구를 선택해주세요"),
dmdCoNm: z.string()
@@ -57,19 +40,19 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean) =>
.max(40, "주소는 40자 이내로 입력해주세요"),
telno: z.string()
.min(8, "전화번호는 8자 이상 입력해주세요")
- .max(12, "전화번호는 12자 이내로 입력해주세요")
+ .max(11, "전화번호는 11자 이내로 입력해주세요")
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"),
rgtr: z.string().optional(),
- dmdAplyPswd : z.string()
- .min(1, "패스워드를 입력해주세요")
- .min(8, "패스워드는 8자 이상 입력해주세요")
- .max(16, "패스워드는 16자 이내로 입력해주세요"),
+ dmdAplyPswd : updateList?.dmdInfoPk // 업데이트일경우
+ ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음
+ : z.string().optional()
+ .refine((val) => !val || (val.length >= 8 && val.length <= 16), "패스워드는 8자 이상 16자 이내로 입력해주세요"),
+
});
-type FormSchema = ReturnType;
-export type FormData = z.infer;
+type FormSchema = z.infer>;
-const defaultValues: FormData = {
+const defaultValues: FormSchema = {
ctpvCd: '',
sggCd: '',
dmdCoNm: '',
@@ -78,7 +61,7 @@ const defaultValues: FormData = {
daddr: '',
telno: '',
rgtr: "3fa85f64-5717-4562-b3fc-2c963f66afa6",
- dmdAplyPswd : '',
+ dmdAplyPswd: '',
};
/**
@@ -117,15 +100,15 @@ const defaultValues: FormData = {
}, [insertSdmData]);
*/
-export default function SdmInsertForm({selectSidoData, selectSigunData, dataList, rtnValue, rtnInputData,bodyClassNm}: thisProps) {
+export default function SdmInsertForm({selectSidoData, selectSigunData, dataList, rtnValue, rtnInputData,bodyClassNm, updateList}: thisProps) {
/* const { insertSdmData } = useDmStore((state) => state);
const router = useRouter(); */
- const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true);
+ const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(false);
const [isDuplicate, setIsDuplicate] = useState(false);
- const formSchema = createFormSchema(isBznmTotNoDisabled, isDuplicate);
+ const formSchema = createFormSchema(isBznmTotNoDisabled, isDuplicate, updateList );
- const form = useForm>({
+ const form = useForm({
resolver: zodResolver(formSchema),
defaultValues,
mode: "onChange",
@@ -136,7 +119,7 @@ export default function SdmInsertForm({selectSidoData, selectSigunData, dataList
// 체크박스 상태가 변경될 때마다 bznmTotNo 필드의 에러를 초기화
useEffect(() => {
- if (isBznmTotNoDisabled) {
+ if (isBznmTotNoDisabled===true) {
form.clearErrors('bznmTotNo');
form.setValue('bznmTotNo', '');
}
@@ -146,7 +129,7 @@ export default function SdmInsertForm({selectSidoData, selectSigunData, dataList
rtnValue(searchText, pageCnt);
}, [rtnValue]);
- const onSubmit: SubmitHandler> = async (data) => {
+ const onSubmit: SubmitHandler = async (data) => {
try {
// form 에러 체크
const formErrors = form.formState.errors;
@@ -160,27 +143,18 @@ export default function SdmInsertForm({selectSidoData, selectSigunData, dataList
const formData = {
...data,
dmdInfoPk,
+ bzmnNo: data.dmdInfoPk,
};
+
+ console.log(' formData ', formData)
- console.log(formData);
/* await insertSdmData(formData); */
rtnInputData(formData as DemandCompType);
- const queryParams = new URLSearchParams({
- dmdCoNm: data.dmdCoNm,
- ctpvCd: data.ctpvCd,
- sggCd: data.sggCd,
- dmdInfoPk,
- bznmTotNo: data.bznmTotNo || '',
- daddr: data.daddr,
- telno: data.telno,
- });
//router.push(`/pages/sdmMng/sdmInfoInsert?${queryParams.toString()}`);
- toast.success('수요처가 성공적으로 등록되었습니다.');
} catch (error) {
console.error('Error submitting form:', error);
- toast.error('수요처 등록에 실패했습니다.');
}
};
@@ -203,6 +177,7 @@ export default function SdmInsertForm({selectSidoData, selectSigunData, dataList
dataList={dataList}
rtnValue={setRtnValue}
setIsDuplicate={setIsDuplicate}
+ updateList={updateList}
/>