Merge remote-tracking branch 'origin/main' into iipbs/dm/dev
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
import { LinkPage } from "@/pages/link";
|
||||
export default LinkPage;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { ModifyLinkPage as VModifyLinkPage } from "@/pages/link";
|
||||
|
||||
export default async function ModifyLinkPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const { id } = await params;
|
||||
return <VModifyLinkPage linkSn={id} />;
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
import { RegistLinkPage } from "@/pages/link";
|
||||
export default RegistLinkPage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { LinkLogPage } from "@/pages/linkLog";
|
||||
export default LinkLogPage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { LinkParamsPage } from "@/pages/linkParams";
|
||||
export default LinkParamsPage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { LinkStatsPage } from "@/pages/linkStats";
|
||||
export default LinkStatsPage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { LinkTablePage } from "@/pages/linkTable";
|
||||
export default LinkTablePage;
|
||||
@@ -0,0 +1,2 @@
|
||||
import { UserStatisticsPage } from "@/pages/user-statistics";
|
||||
export default UserStatisticsPage;
|
||||
@@ -0,0 +1,191 @@
|
||||
import axios from "axios";
|
||||
import {
|
||||
type LinkService,
|
||||
type LinkSearchParam,
|
||||
type LinkList,
|
||||
type LinkFormData,
|
||||
type LinkInfo,
|
||||
type LinkLogList,
|
||||
type LinkStatsList,
|
||||
type LinkVrblList,
|
||||
type LinkVrblFormData,
|
||||
type LinkVrbl,
|
||||
type LinkTable,
|
||||
type LinkTableColumnsList,
|
||||
type LinkTableColumns,
|
||||
type LinkTableColData,
|
||||
type LinkTableFormData,
|
||||
} from "../model/link-interface";
|
||||
|
||||
export class LinkApiService implements LinkService {
|
||||
public async getLinkList(params: LinkSearchParam): Promise<LinkList> {
|
||||
const response = await axios.get<LinkList>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`,
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getLinkInfo(linkSn: string): Promise<LinkInfo> {
|
||||
const response = await axios.get<LinkInfo>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkInfo/${linkSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async linkQuickStart(linkSn: string): Promise<string> {
|
||||
const response = await axios.patch<string>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/linkQuickStart/${linkSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async updateLinkInfo(data: LinkFormData): Promise<void> {
|
||||
await axios.put<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/updateLinkInfo`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
public async insertLinkInfo(data: LinkFormData): Promise<void> {
|
||||
await axios.post<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/insertLinkInfo`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteLink(linkSn: string): Promise<void> {
|
||||
await axios.delete<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/deleteLink/${linkSn}`,
|
||||
);
|
||||
}
|
||||
|
||||
public async getLinkLogList(params: LinkSearchParam): Promise<LinkLogList> {
|
||||
const response = await axios.get<LinkLogList>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/selectLinkLoglist`,
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getLinkStatsList(params: LinkSearchParam): Promise<LinkStatsList> {
|
||||
const response = await axios.get<LinkStatsList>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/selectLinkStats`,
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getLinkVrblList(linkSn: string): Promise<LinkVrblList> {
|
||||
const response = await axios.get<LinkVrblList>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkVrblList/${linkSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async insertLinkVrbl(data: LinkVrblFormData): Promise<number> {
|
||||
const response = await axios.post<number>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/insertLinkVrbl`,
|
||||
data,
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async deleteLinkVrbl(data: string[]): Promise<void> {
|
||||
await axios.delete<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/deleteLinkVrbl`,
|
||||
{
|
||||
data
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public async getLinkVrblInfo(linkVrblSn: string): Promise<LinkVrbl> {
|
||||
const response = await axios.get<LinkVrbl>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkVrblInfo/${linkVrblSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async updateLinkVrblInfo(data: LinkVrblFormData): Promise<void> {
|
||||
await axios.put<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/updateLinkVrblInfo`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
public async getLinkTableInfo(linkSn: string): Promise<LinkTable> {
|
||||
const response = await axios.get<LinkTable>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo/${linkSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getLinkTableColList(linkSn: string): Promise<LinkTableColumnsList> {
|
||||
const response = await axios.get<LinkTableColumnsList>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList/${linkSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getLinkTableColInfo(params: { colSn: string; tblSn:string; }): Promise<LinkTableColumns> {
|
||||
const response = await axios.get<LinkTableColumns>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColInfo`,
|
||||
{ params }
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async insertLinkTableCol(data: LinkTableColData): Promise<void> {
|
||||
await axios.post<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/insertLinkTableCol`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
public async deleteLinkTableCol(data: string[]): Promise<void> {
|
||||
await axios.delete<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/deleteLinkTableCol`,
|
||||
{
|
||||
data
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
public async updateLinkTableCol(data: LinkTableColData): Promise<void> {
|
||||
axios.put<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/updateLinkTableCol`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
public async insertLinkTableInfo(data: LinkTableFormData): Promise<void> {
|
||||
await axios.post<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/insertLinkTableInfo`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
public async updateLinkTableInfo(data: LinkTableFormData): Promise<void> {
|
||||
await axios.put<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/updateLinkTableInfo`,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
public async countLinkTblNm(tblNm: string): Promise<{count: number, tbl_sn: string}> {
|
||||
const response = await axios.get<{count: number, tbl_sn: string}>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/countLinkTblNm/${tblNm}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async updateLinkResultCode(linkSn: string): Promise<void> {
|
||||
await axios.patch<void>(
|
||||
`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/updateLinkResultCode/${linkSn}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const LinkApi = new LinkApiService();
|
||||
@@ -0,0 +1,18 @@
|
||||
export { LinkApi } from "./api/link-service";
|
||||
export {
|
||||
type LinkSearchParam,
|
||||
type Link,
|
||||
type LinkFormData,
|
||||
type LinkLog,
|
||||
type LinkStats,
|
||||
type LinkVrbl,
|
||||
type LinkVrblFormData,
|
||||
type LinkTable,
|
||||
type LinkTableColumns,
|
||||
type LinkTableColData,
|
||||
type LinkTableFormData,
|
||||
linkFormSchema,
|
||||
linkVrblFormSchema,
|
||||
LinkTableColumnsSchema,
|
||||
LinkTableSchema,
|
||||
} from "./model/link-interface"
|
||||
@@ -0,0 +1,235 @@
|
||||
import { z } from "zod";
|
||||
import type { PageableParams, PageableList } from "@/shared/model";
|
||||
|
||||
export interface Link {
|
||||
link_sn: string;
|
||||
link_nm: string;
|
||||
link_clsf_cd: string;
|
||||
link_url: string;
|
||||
link_srvr_ip: string;
|
||||
link_db_cd: string;
|
||||
link_key: string;
|
||||
link_id: string;
|
||||
link_pswd: string;
|
||||
link_cycle_stng: string;
|
||||
srvc_usg: string;
|
||||
last_link_rslt_cd: string;
|
||||
last_link_excn_dt: string;
|
||||
next_link_excn_dt: string;
|
||||
last_link_err_cd: string;
|
||||
mdfcn_dt: string;
|
||||
mdfr: string;
|
||||
link_batch_time: string;
|
||||
}
|
||||
|
||||
export interface LinkLog {
|
||||
rownum: number;
|
||||
link_sn: string;
|
||||
link_nm: string;
|
||||
link_rslt_cd: string;
|
||||
err_cn: string;
|
||||
link_err_cd: string;
|
||||
log_crt_dt: string;
|
||||
}
|
||||
|
||||
export interface LinkInfo extends LinkFormData {
|
||||
link_sn: string;
|
||||
link_nm: string;
|
||||
link_clsf_cd: string;
|
||||
link_url: string;
|
||||
link_srvr_ip: string;
|
||||
link_db_cd: string;
|
||||
link_key: string;
|
||||
link_id: string;
|
||||
link_pswd: string;
|
||||
srvc_usg: string;
|
||||
last_link_rslt_cd: string;
|
||||
last_link_excn_dt: string;
|
||||
next_link_excn_dt: string;
|
||||
last_link_err_cd: string;
|
||||
mdfcn_dt: string;
|
||||
mdfr: string;
|
||||
link_batch_time: number;
|
||||
}
|
||||
|
||||
export interface LinkStats {
|
||||
link_sn: string;
|
||||
link_nm: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface LinkVrbl {
|
||||
link_vrbl_sn: string;
|
||||
link_vrbl_nm: string;
|
||||
link_vrbl_vl: string;
|
||||
link_vrbl_cd: string;
|
||||
link_vrbl_expln: string;
|
||||
mdfcn_dt: string;
|
||||
mdfr: string;
|
||||
}
|
||||
|
||||
export interface LinkTable {
|
||||
link_nm: string;
|
||||
link_sn: string;
|
||||
tbl_sn: string;
|
||||
sou_tbl_nm: string;
|
||||
sou_tbl_korn_nm: string;
|
||||
sou_tbl_expln: string;
|
||||
trsf_tbl_nm: string;
|
||||
trsf_tbl_korn_nm: string;
|
||||
trsf_tbl_expln: string;
|
||||
}
|
||||
|
||||
export interface LinkTableColumns {
|
||||
tbl_sn: string;
|
||||
col_sn: string;
|
||||
sou_col_nm: string;
|
||||
sou_col_korn_nm: string;
|
||||
sou_col_data_frm: string;
|
||||
sou_col_sz: number;
|
||||
trsf_col_nm: string;
|
||||
trsf_col_korn_nm: string;
|
||||
trsf_col_data_frm: string;
|
||||
trsf_col_sz: number
|
||||
pk_yn: string;
|
||||
col_seq: number;
|
||||
mdfcn_dt: string;
|
||||
mdfr: string;
|
||||
}
|
||||
|
||||
export const linkFormSchema = z.object({
|
||||
link_clsf_cd: z.string().max(8).nullable().refine((val) => {
|
||||
// 빈 값이나 "none" 값이면 false 반환
|
||||
return val && val !== "none";
|
||||
}, {
|
||||
message: "연계 분류를 선택해주세요"
|
||||
}),
|
||||
link_sn: z.string().max(50).nullable().optional(),
|
||||
link_nm: z.string().max(40, { message: '연계명은 40자 이하로 입력해 주세요.' }).nonempty({ message: '연계명은 필수입니다.' }),
|
||||
link_url: z.string().max(1000, { message: '연계 URL은 1000자 이하로 입력해 주세요.' }).nonempty({ message: '연계 URL은 필수입니다.' }),
|
||||
link_srvr_ip: z.string().max(20).nullable().optional(),
|
||||
link_db_cd: z.string().max(10).nullable().optional(),
|
||||
link_key: z.string().max(100, { message: '연계 KEY는 100자 이하로 입력해 주세요.' }).nonempty({ message: '연계 KEY는 필수입니다.' }),
|
||||
link_id: z.string().max(20).nullable().optional(),
|
||||
link_pswd: z.string().max(128).nullable().optional(),
|
||||
srvc_usg: z.string().max(40).nullable().optional(),
|
||||
last_link_rslt_cd: z.string().max(4).nullable().optional(),
|
||||
last_link_excn_dt: z.string().max(40).nullable().optional(),
|
||||
next_link_excn_dt: z.string().max(40).nullable().optional(),
|
||||
last_link_err_cd: z.string().max(10).nullable().optional(),
|
||||
mdfcn_dt: z.string().nullable().optional(),
|
||||
mdfr: z.string().nullable().optional(),
|
||||
link_batch_time: z.coerce.number().nullable().optional().refine((val) => {
|
||||
if (val == null) return true;
|
||||
return val >= 5;
|
||||
}, {
|
||||
message: "연계 주기는 5분 이상이어야 합니다."
|
||||
}),
|
||||
});
|
||||
|
||||
export const linkVrblFormSchema = z.object({
|
||||
link_sn: z.string().max(50).nullable().optional(),
|
||||
link_vrbl_sn: z.string().max(50).nullable().optional(),
|
||||
link_vrbl_nm: z.string().max(40, { message: '변수명은 40자 이하로 입력해 주세요.' }).nonempty({ message: '변수명은 필수입니다.' }),
|
||||
link_vrbl_expln: z.string().max(4000, { message: '설명은 400자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
link_vrbl_cd: z.string().max(10, { message: '변수코드는 10자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
link_vrbl_vl: z.string().max(1000, { message: '변수값은 1000자 이하로 입력해 주세요.' }).nonempty({ message: '변수값은 필수입니다.' }),
|
||||
mdfcn_dt: z.string().nullable().optional(),
|
||||
mdfr: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const LinkTableColumnsSchema = z.object({
|
||||
link_sn: z.string().max(50).nullable().optional(),
|
||||
tbl_sn: z.string().max(50).nullable().optional(),
|
||||
col_sn: z.string().max(50).nullable().optional(),
|
||||
sou_col_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
sou_col_korn_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
sou_col_data_frm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
sou_col_sz: z.coerce.number().nullable().optional().refine((val) => {
|
||||
if (val == null) return true;
|
||||
return val.toString().replace('.', '').length <= 14;
|
||||
}, {
|
||||
message: '최대 14자리까지 입력 가능합니다.',
|
||||
}),
|
||||
pk_yn: z.string().max(1).nonempty(),
|
||||
trsf_col_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nonempty({ message: '변환컬럼명은 필수입니다.' }),
|
||||
trsf_col_korn_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nonempty({ message: '변환컬럼 한글명은 필수입니다.' }),
|
||||
trsf_col_data_frm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nonempty({ message: '변환컬럼 데이터 형식은 필수입니다.' }),
|
||||
trsf_col_sz: z.coerce.number().nullable().optional().refine((val) => {
|
||||
if (val == null) return true;
|
||||
return val.toString().replace('.', '').length <= 14;
|
||||
}, {
|
||||
message: '최대 14자리까지 입력 가능합니다.',
|
||||
}),
|
||||
col_seq: z.coerce.number().nullable().optional().refine((val) => {
|
||||
if (val == null) return true;
|
||||
return val > 0;
|
||||
}, {
|
||||
message: "컬럼 매칭 순서는 0 이상이어야 합니다."
|
||||
}),
|
||||
mdfcn_dt: z.string().nullable().optional(),
|
||||
mdfr: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
export const LinkTableSchema = z.object({
|
||||
link_sn: z.string().max(50).nullable().optional(),
|
||||
tbl_sn: z.string().max(50).nullable().optional(),
|
||||
link_nm: z.string().max(50).nullable().optional(),
|
||||
sou_tbl_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
sou_tbl_korn_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
sou_tbl_expln: z.string().max(2000, { message: '설명은 2000자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
trsf_tbl_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nonempty({ message: '변환테이블명은 필수입니다.' }),
|
||||
trsf_tbl_korn_nm: z.string().max(40, { message: '40자 이하로 입력해 주세요.' }).nonempty({ message: '변환테이블 한글명은 필수입니다.' }),
|
||||
trsf_tbl_expln: z.string().max(2000, { message: '설명은 2000자 이하로 입력해 주세요.' }).nullable().optional(),
|
||||
mdfcn_dt: z.string().nullable().optional(),
|
||||
mdfr: z.string().nullable().optional(),
|
||||
});
|
||||
|
||||
interface LinkStatsListObject {
|
||||
linkSuccStats: LinkStats[] | null;
|
||||
linkFailStats: LinkStats[] | null;
|
||||
}
|
||||
|
||||
export interface LinkSearchParam extends PageableParams {
|
||||
linkSn?: string;
|
||||
linkNm?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
isSucc?: string;
|
||||
}
|
||||
|
||||
export type LinkFormData = z.infer<typeof linkFormSchema>;
|
||||
export type LinkVrblFormData = z.infer<typeof linkVrblFormSchema>;
|
||||
export type LinkTableColData = z.infer<typeof LinkTableColumnsSchema>;
|
||||
export type LinkTableFormData = z.infer<typeof LinkTableSchema>;
|
||||
export type LinkList = PageableList<Link>;
|
||||
export type LinkLogList = PageableList<LinkLog>;
|
||||
export type LinkStatsList = LinkStatsListObject;
|
||||
export type LinkVrblList = PageableList<LinkVrbl>;
|
||||
export type LinkTableColumnsList = PageableList<LinkTableColumns>;
|
||||
|
||||
export interface LinkService {
|
||||
getLinkList(params: LinkSearchParam): Promise<LinkList>;
|
||||
getLinkInfo(params: string): Promise<LinkInfo>;
|
||||
linkQuickStart(params: string): Promise<string>;
|
||||
updateLinkInfo(data: LinkFormData): Promise<void>;
|
||||
deleteLink(params: string): Promise<void>;
|
||||
getLinkLogList(params: LinkSearchParam): Promise<LinkLogList>;
|
||||
getLinkStatsList(params: LinkSearchParam): Promise<LinkStatsList>;
|
||||
getLinkVrblList(params: string): Promise<LinkVrblList>;
|
||||
insertLinkVrbl(data: LinkVrblFormData): Promise<number>;
|
||||
deleteLinkVrbl(data: string[]): Promise<void>;
|
||||
getLinkVrblInfo(params: string): Promise<LinkVrbl>;
|
||||
updateLinkVrblInfo(data: LinkVrblFormData): Promise<void>;
|
||||
getLinkTableInfo(params: string): Promise<LinkTable>;
|
||||
getLinkTableColList(params: string): Promise<LinkTableColumnsList>;
|
||||
getLinkTableColInfo(params: { colSn: string; tblSn: string; }): Promise<LinkTableColumns>;
|
||||
insertLinkTableCol(data: LinkTableColData): Promise<void>;
|
||||
deleteLinkTableCol(data: string[]): Promise<void>;
|
||||
updateLinkTableCol(data: LinkTableColData): Promise<void>;
|
||||
insertLinkTableInfo(data: LinkTableFormData): Promise<void>;
|
||||
updateLinkTableInfo(data: LinkTableFormData): Promise<void>;
|
||||
insertLinkInfo(data: LinkFormData): Promise<void>;
|
||||
countLinkTblNm(tblNm: string): Promise<{ count: number, tbl_sn: string }>;
|
||||
updateLinkResultCode(linkSn: string): Promise<void>;
|
||||
}
|
||||
@@ -47,7 +47,7 @@ function AddAdminDialog() {
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [userSearchQuery, setUserSearchQuery] = useState<UserSearchParams>({
|
||||
keywordType: "",
|
||||
keywordType: "userName",
|
||||
keyword: "",
|
||||
});
|
||||
const [selectedGroupId, setSelectedGroupId] = useState("");
|
||||
@@ -193,6 +193,7 @@ function AddAdminDialog() {
|
||||
|
||||
<form onSubmit={handleSearch} className="flex items-center gap-2">
|
||||
<Select
|
||||
value={userSearchQuery.keywordType}
|
||||
onValueChange={(value) =>
|
||||
setUserSearchQuery({ ...userSearchQuery, keywordType: value })
|
||||
}
|
||||
@@ -201,8 +202,8 @@ function AddAdminDialog() {
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="userId">아이디</SelectItem>
|
||||
<SelectItem value="userName">이름</SelectItem>
|
||||
<SelectItem value="userId">아이디</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Input
|
||||
|
||||
@@ -7,7 +7,6 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -15,10 +14,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@common/components";
|
||||
import { columns } from "@/pages/apilog/model/table-columns";
|
||||
import { ApiLogApi, type ApiLogParams } from "@/entities/apilog";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/apilog/model/table-columns";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
|
||||
type DateRange = {
|
||||
from?: Date | undefined;
|
||||
@@ -133,6 +133,11 @@ function ApiLogPage() {
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="card card-border">
|
||||
@@ -148,20 +153,9 @@ function ApiLogPage() {
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">사용자ID</Label>
|
||||
<Input
|
||||
|
||||
@@ -110,10 +110,16 @@ function DashboardPage() {
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter text-white">
|
||||
<h3 className="text-2xl font-medium">총 회원수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.memberTotCnt ?? "-"}
|
||||
{data?.memberTotCnt
|
||||
? data.memberTotCnt.toLocaleString()
|
||||
: "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
금일 신규 가입자 {data?.memberTodayCnt ?? "-"}명
|
||||
금일 신규 가입자
|
||||
{data?.memberTodayCnt
|
||||
? data.memberTodayCnt.toLocaleString()
|
||||
: "-"}
|
||||
명
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
@@ -131,10 +137,12 @@ function DashboardPage() {
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">전체 게시물</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.pstTotCnt ?? "-"}
|
||||
{data?.pstTotCnt ? data.pstTotCnt.toLocaleString() : "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
금일 +{data?.pstTodayCnt ?? "-"} 등록
|
||||
금일 +
|
||||
{data?.pstTodayCnt ? data.pstTodayCnt.toLocaleString() : "-"}{" "}
|
||||
등록
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
@@ -152,10 +160,16 @@ function DashboardPage() {
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">오늘 접속자 수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.accessTodayCnt ?? "-"}
|
||||
{data?.accessTodayCnt
|
||||
? data.accessTodayCnt.toLocaleString()
|
||||
: "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
전일 대비 +{data?.accessBefDayPer ?? "-"}%
|
||||
전일 대비 +
|
||||
{data?.accessBefDayPer
|
||||
? data.accessBefDayPer.toLocaleString()
|
||||
: "-"}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
@@ -173,7 +187,7 @@ function DashboardPage() {
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">API 호출 건수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.apiTodayCnt ?? "-"}
|
||||
{data?.apiTodayCnt ? data.apiTodayCnt.toLocaleString() : "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">금일 누적</p>
|
||||
</div>
|
||||
@@ -221,7 +235,10 @@ function DashboardPage() {
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">
|
||||
{data?.waitMemberCnt}건
|
||||
{data?.waitMemberCnt
|
||||
? data.waitMemberCnt.toLocaleString()
|
||||
: "0"}
|
||||
건
|
||||
</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
신규 가입 승인 대기
|
||||
@@ -253,7 +270,9 @@ function DashboardPage() {
|
||||
<span className="text-2xl font-medium">메뉴 승인</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">3건</div>
|
||||
<div className="text-5xl font-bold">
|
||||
{(3).toLocaleString()}건
|
||||
</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
매뉴 수정 승인 대기
|
||||
</div>
|
||||
@@ -283,7 +302,10 @@ function DashboardPage() {
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">
|
||||
{data?.waitCntnCnt}건
|
||||
{data?.waitCntnCnt
|
||||
? data.waitCntnCnt.toLocaleString()
|
||||
: "0"}
|
||||
건
|
||||
</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
컨텐츠 수정 승인 대기
|
||||
@@ -303,19 +325,19 @@ function DashboardPage() {
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">신규 사용자 가입</span>
|
||||
<span>1분 전</span>
|
||||
<span>{(1).toLocaleString()}분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">메뉴 수정 요청</span>
|
||||
<span>5분 전</span>
|
||||
<span>{(5).toLocaleString()}분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">게시물 신고</span>
|
||||
<span>10분 전</span>
|
||||
<span>{(10).toLocaleString()}분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">컨텐츠 승인 요청</span>
|
||||
<span>15분 전</span>
|
||||
<span>{(15).toLocaleString()}분 전</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -328,20 +350,22 @@ function DashboardPage() {
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">3월 정기 시스템 점검</span>
|
||||
<span className="font-medium">
|
||||
{(3).toLocaleString()}월 정기 시스템 점검
|
||||
</span>
|
||||
<span>내일</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">서버 업데이트 예정</span>
|
||||
<span>3일 후</span>
|
||||
<span>{(3).toLocaleString()}일 후</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">백업 스케줄</span>
|
||||
<span>5일 후</span>
|
||||
<span>{(5).toLocaleString()}일 후</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">정기 보안 점검</span>
|
||||
<span>7일 후</span>
|
||||
<span>{(7).toLocaleString()}일 후</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -161,11 +161,11 @@ function GroupForm({ form }: GroupFormProps) {
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="Y" id="mngrY" />
|
||||
<Label htmlFor="mngrY">Y</Label>
|
||||
<Label htmlFor="mngrY">예</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="N" id="mngrN" />
|
||||
<Label htmlFor="mngrN">N</Label>
|
||||
<Label htmlFor="mngrN">아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { memo, useState } from "react";
|
||||
import { memo, useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
@@ -43,7 +43,7 @@ const GroupsDialog = memo(
|
||||
const { data: group } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.GROUPS, groupId],
|
||||
queryFn: () => GroupApi.findGroupById(groupId!),
|
||||
enabled: !!groupId && open, // open 시점 groupId가 있을 때만 상세 조회 API 호출
|
||||
enabled: !!groupId && open, // open 시점 groupId가 있을 때만 상세 조회 API 호출
|
||||
});
|
||||
|
||||
const { mutateAsync: createGroup } = useMutationWrapper({
|
||||
@@ -77,7 +77,7 @@ const GroupsDialog = memo(
|
||||
delYn: group?.delYn ?? "",
|
||||
authrtId: group?.authrtId ?? "",
|
||||
siteId: group?.siteId ?? "",
|
||||
mngrYn: group?.mngrYn ?? "",
|
||||
mngrYn: group?.mngrYn ?? "Y",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,6 +91,10 @@ const GroupsDialog = memo(
|
||||
if (await confirm(confirmMessage)) callback(data);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) form.reset();
|
||||
}, [open]);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen} {...props}>
|
||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||
|
||||
@@ -23,6 +23,7 @@ function CreatePage() {
|
||||
resolver: zodResolver(imageFormSchema),
|
||||
defaultValues: {
|
||||
atflYn: "Y",
|
||||
useYn: "Y",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
||||
{...field}
|
||||
type="number"
|
||||
min={0}
|
||||
max={9999}
|
||||
value={field.value ?? ""}
|
||||
onChange={(e) =>
|
||||
field.onChange(parseInt(e.target.value))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export { ModifyLinkPage } from "./ui/modify-page";
|
||||
export { RegistLinkPage } from "./ui/regist-page";
|
||||
export { LinkPage } from "./ui/link-page";
|
||||
@@ -0,0 +1,88 @@
|
||||
import dayjs from "dayjs";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type Link } from "@/entities/link";
|
||||
import QuickStartButton from "../ui/QuickStartButton";
|
||||
|
||||
const columnHelper = createColumnHelper<Link>();
|
||||
|
||||
const convertMinutesToPeriod = (minutes: number): string => {
|
||||
if (!minutes) return '';
|
||||
|
||||
const year = Math.floor(minutes / (525600)); // 365일 * 24시간 * 60분
|
||||
minutes = minutes % 525600;
|
||||
|
||||
const month = Math.floor(minutes / (43200)); // 30일 * 24시간 * 60분
|
||||
minutes = minutes % 43200;
|
||||
|
||||
const day = Math.floor(minutes / 1440); // 24시간 * 60분
|
||||
minutes = minutes % 1440;
|
||||
|
||||
const hour = Math.floor(minutes / 60);
|
||||
const minute = minutes % 60;
|
||||
|
||||
const parts = [];
|
||||
if (year > 0) parts.push(`${year}년`);
|
||||
if (month > 0) parts.push(`${month}개월`);
|
||||
if (day > 0) parts.push(`${day}일`);
|
||||
if (hour > 0) parts.push(`${hour}시간`);
|
||||
if (minute > 0) parts.push(`${minute}분`);
|
||||
|
||||
return parts.join(' ');
|
||||
};
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor((_, index) => index + 1, {
|
||||
id: 'no',
|
||||
header: 'NO',
|
||||
}),
|
||||
columnHelper.accessor('link_sn', {
|
||||
id: 'link_sn',
|
||||
header: '연계일련번호',
|
||||
}),
|
||||
columnHelper.accessor('link_nm', {
|
||||
id: 'link_nm',
|
||||
header: '연계 명',
|
||||
}),
|
||||
columnHelper.accessor('link_url', {
|
||||
id: 'link_url',
|
||||
header: '연계 URL',
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
columnHelper.accessor('link_srvr_ip', {
|
||||
id: 'link_srvr_ip',
|
||||
header: '연계서버 IP',
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
columnHelper.accessor('link_batch_time', {
|
||||
id: 'link_batch_time',
|
||||
header: '연계 주기',
|
||||
cell: (info) => convertMinutesToPeriod(Number(info.getValue())),
|
||||
}),
|
||||
columnHelper.accessor('srvc_usg', {
|
||||
id: 'srvc_usg',
|
||||
header: '서비스용도',
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'last_link_rslt_cd',
|
||||
header: '연계상태',
|
||||
cell: (info) => {
|
||||
const resultCd = info.row.original.last_link_rslt_cd;
|
||||
return <span className={`${(resultCd === 'SUCC') ? 'text-[rgb(53,162,235)]' : (resultCd === 'FAIL' ? 'text-[rgb(255,99,132)]' : 'text-[rgba(31,208,120,1)]')}`}>{resultCd === 'SUCC' ? '성공' : (resultCd === 'FAIL' ? '실패' : '대기')}</span>
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor('last_link_excn_dt', {
|
||||
id: 'last_link_excn_dt',
|
||||
header: '최종연계일',
|
||||
cell: (info) => (info.getValue() ? dayjs(info.getValue()).format('YYYY-MM-DD HH:mm') : '-'),
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'quick_start_btn',
|
||||
header: '즉시 실행',
|
||||
cell: (info) => {
|
||||
return <QuickStartButton linkSn={info.row.original.link_sn} />
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
export { columns };
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { LinkApi } from "@/entities/link";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { Button } from "@common/components";
|
||||
|
||||
const QuickStartButton: React.FC<{ linkSn: string }> = ({ linkSn }) => {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { mutateAsync: updateQuickStart } = useMutation({
|
||||
mutationFn: (linkSn: string) => LinkApi.linkQuickStart(linkSn),
|
||||
onSuccess: (result) => {
|
||||
if (result === 'succ') {
|
||||
alert('즉시 실행 요청이 완료되었습니다.');
|
||||
} else {
|
||||
alert('기존 요청이 실행 대기중입니다.');
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
alert('즉시 실행 요청을 실패했습니다.');
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
color="sky"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
updateQuickStart(linkSn);
|
||||
}}
|
||||
>
|
||||
즉시 실행
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuickStartButton;
|
||||
@@ -0,0 +1,590 @@
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
} from "@common/components";
|
||||
import { FormItem, FormControl, FormField, Form } from "@/shared/ui/form";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { type LinkFormData } from "@/entities/link";
|
||||
import dayjs from "dayjs";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useState } from "react";
|
||||
|
||||
|
||||
type LinkFormProps = {
|
||||
form: UseFormReturn<LinkFormData>;
|
||||
linkSn?: string;
|
||||
updateLinkInfo?: (data: LinkFormData) => void;
|
||||
handleInsertLink?: (data: LinkFormData) => void;
|
||||
deleteLink?: () => void;
|
||||
updateLinkResultCode?: () => void;
|
||||
}
|
||||
|
||||
const LinkForm = ({ form, linkSn, updateLinkInfo, handleInsertLink, deleteLink, updateLinkResultCode }: LinkFormProps) => {
|
||||
const isModifyMode = !!linkSn;
|
||||
const router = useRouter();
|
||||
|
||||
const { confirm } = useConfirm();
|
||||
const [onConfirm, setOnConfirm] = useState(false);
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
const handleSubmit = async (data: LinkFormData) => {
|
||||
const confirmMsg = !!linkSn ? '수정 하시겠습니까?' : '저장 하시겠습니까?';
|
||||
|
||||
await handleConfirm(async () => {
|
||||
|
||||
if (await confirm(confirmMsg)) {
|
||||
if (linkSn) {
|
||||
updateLinkInfo?.(data);
|
||||
} else {
|
||||
handleInsertLink?.(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleDelete = async () => {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm('삭제하시겠습니까?')) {
|
||||
deleteLink?.();
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
const convertMinutesToPeriod = (minutes: number) => {
|
||||
if (!minutes) return {
|
||||
year: 0,
|
||||
month: 0,
|
||||
day: 0,
|
||||
hour: 0,
|
||||
minute: 0
|
||||
};
|
||||
|
||||
const year = Math.floor(minutes / (525600)); // 365일 * 24시간 * 60분
|
||||
minutes = minutes % 525600;
|
||||
|
||||
const month = Math.floor(minutes / (43200)); // 30일 * 24시간 * 60분
|
||||
minutes = minutes % 43200;
|
||||
|
||||
const day = Math.floor(minutes / 1440); // 24시간 * 60분
|
||||
minutes = minutes % 1440;
|
||||
|
||||
const hour = Math.floor(minutes / 60);
|
||||
const minute = minutes % 60;
|
||||
|
||||
const timeParts = {
|
||||
year: year,
|
||||
month: month,
|
||||
day: day,
|
||||
hour: hour,
|
||||
minute: minute,
|
||||
};
|
||||
|
||||
return timeParts;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="80%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
{isModifyMode &&
|
||||
<TableRow>
|
||||
<TableHead>연계 일련번호</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_sn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="w-[12rem] !text-[1.3rem]"
|
||||
disabled={true}
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
}
|
||||
<TableRow>
|
||||
<TableHead>* 연계 명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 연계 분류</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_clsf_cd"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select value={field.value ?? "none"} onValueChange={(value) => field.onChange(value)}>
|
||||
<SelectTrigger id="link_clsf_cd" className="w-[20rem]">
|
||||
<SelectValue placeholder="전체"></SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">선택</SelectItem>
|
||||
<SelectItem value="link0001">TIBERO 데이터베이스</SelectItem>
|
||||
<SelectItem value="link0002">ORACLE 데이터베이스</SelectItem>
|
||||
<SelectItem value="link0003">공공데이터 포털 API</SelectItem>
|
||||
<SelectItem value="link0004">한강홍수통제소 API</SelectItem>
|
||||
<SelectItem value="link0005">기상청 API</SelectItem>
|
||||
<SelectItem value="link0006">국가지하수정보센터 API</SelectItem>
|
||||
<SelectItem value="link0007">행정정보공동이용센터 연계</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_clsf_cd && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_clsf_cd.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 연계 URL</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_url"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_url && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_url.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계서버 IP</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_srvr_ip"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 연계 KEY</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_key && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_key.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계 ID</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_id"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계 비밀번호</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_pswd"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>서비스 용도</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="srvc_usg"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계 주기</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_batch_time"
|
||||
render={({ field }) => {
|
||||
const timeParts = convertMinutesToPeriod(Number(field.value));
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
value={timeParts.year}
|
||||
className="w-20 !text-[1.3rem]"
|
||||
type="number"
|
||||
min={0}
|
||||
onChange={(e) => {
|
||||
const total = (Number(e.target.value) * 525600) +
|
||||
(timeParts.month * 43200) +
|
||||
(timeParts.day * 1440) +
|
||||
(timeParts.hour * 60) +
|
||||
timeParts.minute;
|
||||
field.onChange(total);
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">년</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
value={timeParts.month}
|
||||
className="w-20 !text-[1.3rem]"
|
||||
type="number"
|
||||
min={0}
|
||||
max={11}
|
||||
onChange={(e) => {
|
||||
const total = (timeParts.year * 525600) +
|
||||
(Number(e.target.value) * 43200) +
|
||||
(timeParts.day * 1440) +
|
||||
(timeParts.hour * 60) +
|
||||
timeParts.minute;
|
||||
field.onChange(total);
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">월</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
value={timeParts.day}
|
||||
className="w-20 !text-[1.3rem]"
|
||||
type="number"
|
||||
min={0}
|
||||
max={30}
|
||||
onChange={(e) => {
|
||||
const total = (timeParts.year * 525600) +
|
||||
(timeParts.month * 43200) +
|
||||
(Number(e.target.value) * 1440) +
|
||||
(timeParts.hour * 60) +
|
||||
timeParts.minute;
|
||||
field.onChange(total);
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">일</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
value={timeParts.hour}
|
||||
className="w-20 !text-[1.3rem]"
|
||||
type="number"
|
||||
min={0}
|
||||
max={23}
|
||||
onChange={(e) => {
|
||||
const total = (timeParts.year * 525600) +
|
||||
(timeParts.month * 43200) +
|
||||
(timeParts.day * 1440) +
|
||||
(Number(e.target.value) * 60) +
|
||||
timeParts.minute;
|
||||
field.onChange(total);
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">시간</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Input
|
||||
value={timeParts.minute}
|
||||
className="w-20 !text-[1.3rem]"
|
||||
type="number"
|
||||
min={0}
|
||||
max={59}
|
||||
onChange={(e) => {
|
||||
const total = (timeParts.year * 525600) +
|
||||
(timeParts.month * 43200) +
|
||||
(timeParts.day * 1440) +
|
||||
(timeParts.hour * 60) +
|
||||
Number(e.target.value);
|
||||
field.onChange(total);
|
||||
}}
|
||||
/>
|
||||
<span className="ml-2">분</span>
|
||||
</div>
|
||||
</div>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_batch_time && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_batch_time.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isModifyMode &&
|
||||
<>
|
||||
<TableRow>
|
||||
<TableHead>연계 상태</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="last_link_rslt_cd"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-6">
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? (field.value === 'SUCC' ? '성공' : (field.value === 'FAIL' ? '실패' : '대기')) : ''}
|
||||
className="!text-[1.3rem] w-[15rem]"
|
||||
disabled
|
||||
/>
|
||||
<Button color="green" type="button" onClick={() => updateLinkResultCode?.()}>대기로 전환</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>최종 연계 실행일</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="last_link_excn_dt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? dayjs(field.value).format('YYYY-MM-DD HH:mm:ss') : ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>다음 연계 실행일</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="next_link_excn_dt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? dayjs(field.value).format('YYYY-MM-DD HH:mm:ss') : ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>수정자</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfr"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>수정일시</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfcn_dt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? dayjs(field.value).format('YYYY-MM-DD') : ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex justify-center gap-4 pb-12 pt-10">
|
||||
<Button
|
||||
size="lg"
|
||||
color="white"
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
>
|
||||
목록
|
||||
</Button>
|
||||
<Button size="lg" type="submit">
|
||||
저장
|
||||
</Button>
|
||||
{isModifyMode && (
|
||||
<Button size="lg" color="red" type="button" onClick={handleDelete}>
|
||||
삭제
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
<ConfirmDialog isOpen={onConfirm} />
|
||||
</>
|
||||
)
|
||||
};
|
||||
|
||||
export default LinkForm;
|
||||
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
|
||||
import "@common/assets/styles/grid.css";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button, Input, Label, LoadingWithAnimation } from "@common/components";
|
||||
import { getCoreRowModel } from "@tanstack/react-table";
|
||||
import { LinkApi, type Link, type LinkSearchParam } from "@/entities/link";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { columns } from "../model/table-columns";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
|
||||
function LinkPage() {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [linkSn, setLinkSn] = useState<string>('');
|
||||
const [linkNm, setLinkNm] = useState<string>('');
|
||||
|
||||
const [linkSearchParam, setLinkSearchParam] = useState<LinkSearchParam>({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`, linkSearchParam],
|
||||
queryFn: () => LinkApi.getLinkList(linkSearchParam),
|
||||
});
|
||||
|
||||
const handleResetSearchParam = () => {
|
||||
setLinkSn('');
|
||||
setLinkNm('');
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn,
|
||||
linkNm
|
||||
});
|
||||
};
|
||||
|
||||
const handleRowClick = (link: Link) => {
|
||||
queryClient.removeQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`],
|
||||
});
|
||||
queryClient.removeQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkInfo`],
|
||||
});
|
||||
router.push(`link/register/${link.link_sn}`);
|
||||
};
|
||||
|
||||
const handleClickRegist = () => {
|
||||
router.push('link/register');
|
||||
};
|
||||
|
||||
const { tableComponent } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
manualPagination: true,
|
||||
enableMultiRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
},
|
||||
dataTableOptions: {
|
||||
onRowClick: handleRowClick,
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form className="flex items-center gap-2 p-2" onSubmit={handleSubmit}>
|
||||
<Label htmlFor="liSnSearchInput" className="mr-2 text-label">
|
||||
일련번호
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-4"
|
||||
id="liSnSearchInput"
|
||||
value={linkSn}
|
||||
onChange={(e) => setLinkSn(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Label htmlFor="liNmSearchInput" className="mr-2 text-label">
|
||||
연계 명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-2"
|
||||
id="liNmSearchInput"
|
||||
value={linkNm}
|
||||
onChange={(e) => setLinkNm(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Button color="green" className="h-[3.2rem]" type="button" onClick={handleResetSearchParam}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border flex flex-col h-[calc(100vh-28rem)]">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 목록</h3>
|
||||
<div>
|
||||
<Button onClick={handleClickRegist}>등록</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] overflow-auto h-full !p-0">
|
||||
{!query.isLoading ?
|
||||
<div>{tableComponent}</div>
|
||||
:
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center h-full">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { LinkPage };
|
||||
@@ -0,0 +1,111 @@
|
||||
"use client";
|
||||
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { LinkApi, linkFormSchema, type LinkFormData } from "@/entities/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import LinkForm from "./link-form";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { useEffect } from "react";
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import { AUTH_USER_ID } from "@/shared/config";
|
||||
import { getCookie } from "@common/components";
|
||||
|
||||
function ModifyLinkPage({ linkSn }: { linkSn: string }) {
|
||||
const router = useRouter();
|
||||
const queryClient = useQueryClient();
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
|
||||
// 컴포넌트 마운트 시 캐시 초기화
|
||||
useEffect(() => {
|
||||
queryClient.removeQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkInfo`],
|
||||
});
|
||||
}, [queryClient]);
|
||||
|
||||
const { data: linkInfo, refetch } = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkInfo`],
|
||||
queryFn: () => LinkApi.getLinkInfo(linkSn),
|
||||
enabled: !!linkSn,
|
||||
});
|
||||
|
||||
const { mutateAsync: updateLinkInfo } = useMutation({
|
||||
mutationFn: (data: LinkFormData) => LinkApi.updateLinkInfo({
|
||||
...data,
|
||||
link_clsf_cd: data.link_clsf_cd !== 'none' ? data.link_clsf_cd : null,
|
||||
mdfr: userUniqId
|
||||
}),
|
||||
onSuccess: () => {
|
||||
alert('수정되었습니다.');
|
||||
router.push('/link');
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteLink } = useMutation({
|
||||
mutationFn: () => LinkApi.deleteLink(linkSn),
|
||||
onSuccess: () => {
|
||||
alert('삭제되었습니다.', router.back);
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: updateLinkResultCode } = useMutation({
|
||||
mutationFn: () => LinkApi.updateLinkResultCode(linkSn),
|
||||
onSuccess: () => {
|
||||
alert('대기로 전환되었습니다.');
|
||||
refetch();
|
||||
}
|
||||
});
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
const userData = await getUserInfo();
|
||||
return userData.userName;
|
||||
};
|
||||
|
||||
const form = useForm<LinkFormData>({
|
||||
resolver: zodResolver(linkFormSchema),
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const applyFormData = async () => {
|
||||
if (linkInfo) {
|
||||
let newData = { ...linkInfo };
|
||||
|
||||
if (linkInfo.mdfr) {
|
||||
const userName = await fetchUserInfo();
|
||||
newData = {
|
||||
...linkInfo,
|
||||
mdfr: userName,
|
||||
};
|
||||
}
|
||||
|
||||
form.reset(newData);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
applyFormData();
|
||||
}, [linkInfo]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계정보 수정</h3>
|
||||
</div>
|
||||
<LinkForm
|
||||
form={form}
|
||||
linkSn={linkSn}
|
||||
updateLinkInfo={updateLinkInfo}
|
||||
deleteLink={deleteLink}
|
||||
updateLinkResultCode={updateLinkResultCode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export { ModifyLinkPage };
|
||||
@@ -0,0 +1,64 @@
|
||||
"use client";
|
||||
|
||||
import { LinkApi, type LinkFormData, linkFormSchema } from "@/entities/link";
|
||||
import LinkForm from "./link-form";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import dayjs from "dayjs";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { AUTH_USER_ID } from "@/shared/config";
|
||||
import { getCookie } from "@common/components";
|
||||
|
||||
function RegistLinkPage() {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
const router = useRouter();
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
|
||||
const form = useForm<LinkFormData>({
|
||||
resolver: zodResolver(linkFormSchema),
|
||||
defaultValues: {
|
||||
link_nm: '',
|
||||
link_url: '',
|
||||
link_srvr_ip: '',
|
||||
link_db_cd: '',
|
||||
link_key: '',
|
||||
link_id: '',
|
||||
link_pswd: '',
|
||||
srvc_usg: '',
|
||||
link_batch_time: 5,
|
||||
link_clsf_cd: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: insertLinkInfo } = useMutation({
|
||||
mutationFn: (data: LinkFormData) => LinkApi.insertLinkInfo(data),
|
||||
onSuccess: () => {
|
||||
alert('저장되었습니다.');
|
||||
router.push('/link');
|
||||
}
|
||||
});
|
||||
|
||||
const handleInsertLink = (data: LinkFormData) => {
|
||||
const insertData = {
|
||||
...data,
|
||||
link_clsf_cd: data.link_clsf_cd !== 'none' ? data.link_clsf_cd : null,
|
||||
next_link_excn_dt: dayjs().add(Number(data.link_batch_time), 'minute').format('YYYY-MM-DD HH:mm:ss'),
|
||||
mdfr: userUniqId,
|
||||
};
|
||||
|
||||
insertLinkInfo(insertData);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card card-border !mb-0 !mt-6">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 등록</h3>
|
||||
</div>
|
||||
<LinkForm form={form} handleInsertLink={handleInsertLink} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export { RegistLinkPage };
|
||||
@@ -0,0 +1 @@
|
||||
export { LinkLogPage } from './ui/link-log-page';
|
||||
@@ -0,0 +1,55 @@
|
||||
import dayjs from "dayjs";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type LinkLog } from "@/entities/link";
|
||||
|
||||
const columnHelper = createColumnHelper<LinkLog>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor('rownum', {
|
||||
id: 'rownum',
|
||||
header: 'NO',
|
||||
}),
|
||||
columnHelper.accessor('link_sn', {
|
||||
id: 'link_sn',
|
||||
header: '연계일련번호',
|
||||
}),
|
||||
columnHelper.accessor('link_nm', {
|
||||
id: 'link_nm',
|
||||
header: '연계 명',
|
||||
}),
|
||||
columnHelper.display({
|
||||
id: 'link_rslt_cd',
|
||||
header: '연계실행결과',
|
||||
cell: (info) => {
|
||||
const isSucc = info.row.original.link_rslt_cd.trim() === 'SUCC';
|
||||
return <span className={`${isSucc ? 'text-[rgb(53,162,235)]' : 'text-[rgb(255,99,132)]'}`}>{isSucc ? '성공' : '실패'}</span>
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor('link_err_cd', {
|
||||
id: 'link_err_cd',
|
||||
header: '연계오류코드',
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
columnHelper.accessor('err_cn', {
|
||||
id: 'err_cn',
|
||||
header: '연계오류메세지',
|
||||
cell: (info) => (
|
||||
<div
|
||||
className="max-w-[500px] truncate"
|
||||
title={info.getValue() ?? ''}
|
||||
>
|
||||
{info.getValue() || '-'}
|
||||
</div>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor('log_crt_dt', {
|
||||
id: 'log_crt_dt',
|
||||
header: '연계실행일자',
|
||||
cell: (info) => {
|
||||
const date = dayjs(info.getValue());
|
||||
return date.format('YYYY-MM-DD HH:mm');
|
||||
}
|
||||
}),
|
||||
];
|
||||
|
||||
export { columns };
|
||||
@@ -0,0 +1,189 @@
|
||||
"use client";
|
||||
|
||||
import "@common/assets/styles/grid.css";
|
||||
|
||||
import { useState } from "react";
|
||||
import { Button, Input, Label, DatePicker, Select, SelectTrigger, SelectContent, SelectItem, SelectValue, LoadingWithAnimation } from "@common/components";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { getCoreRowModel, type PaginationState, type RowSelectionState, } from "@tanstack/react-table";
|
||||
import { LinkApi, type LinkSearchParam } from "@/entities/link";
|
||||
import { columns } from "../model/table-columns";
|
||||
import dayjs from "dayjs";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
|
||||
function LinkLogPage() {
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const [linkSn, setLinkSn] = useState<string>('');
|
||||
const [linkNm, setLinkNm] = useState<string>('');
|
||||
const [isSucc, setIsSucc] = useState<string>('ALL');
|
||||
const [startDate, setStartDate] = useState<string>(dayjs().subtract(7, "day").format('YYYY-MM-DD'));
|
||||
const [endDate, setEndDate] = useState<string>(dayjs().format('YYYY-MM-DD'));
|
||||
|
||||
const [linkSearchParam, setLinkSearchParam] = useState<LinkSearchParam>({
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
isSucc: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
});
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`, linkSearchParam, pagination],
|
||||
queryFn: () =>
|
||||
LinkApi.getLinkLogList({
|
||||
...linkSearchParam,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
isSucc,
|
||||
startDate,
|
||||
endDate,
|
||||
}),
|
||||
});
|
||||
|
||||
const { tableComponent } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
state: {
|
||||
pagination,
|
||||
rowSelection,
|
||||
},
|
||||
manualPagination: true,
|
||||
enableMultiRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
},
|
||||
dataTableOptions: {
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
const handleResetSearchParam = () => {
|
||||
setIsSucc('ALL');
|
||||
setLinkNm('');
|
||||
setLinkSn('');
|
||||
setStartDate(dayjs().subtract(7, "day").format('YYYY-MM-DD'));
|
||||
setEndDate(dayjs().format('YYYY-MM-DD'));
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
isSucc: 'ALL',
|
||||
startDate: dayjs().subtract(7, "day").format('YYYY-MM-DD'),
|
||||
endDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e?: React.FormEvent<HTMLFormElement>) => {
|
||||
e?.preventDefault();
|
||||
|
||||
if (startDate && endDate) {
|
||||
|
||||
if (startDate > endDate) {
|
||||
alert('종료일이 시작일보다 이전일 수 없습니다.');
|
||||
return;
|
||||
} else {
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
});
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn,
|
||||
linkNm,
|
||||
isSucc,
|
||||
startDate,
|
||||
endDate,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alert('조회 날짜를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form className="flex items-center gap-2 p-2" onSubmit={handleSubmit}>
|
||||
<Label htmlFor="liSnSearchInput" className="mr-2 text-label">
|
||||
일련번호
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-4"
|
||||
id="liSnSearchInput"
|
||||
value={linkSn}
|
||||
onChange={(e) => setLinkSn(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Label htmlFor="liNmSearchInput" className="mr-2 text-label">
|
||||
연계 명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-4"
|
||||
id="liNmSearchInput"
|
||||
value={linkNm}
|
||||
onChange={(e) => setLinkNm(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Label htmlFor="liResultCode" className="mr-2 text-label">
|
||||
실행결과
|
||||
</Label>
|
||||
<Select onValueChange={(e) => setIsSucc(e)} value={isSucc}>
|
||||
<SelectTrigger id="liResultCode" className="w-[10rem] mr-4">
|
||||
<SelectValue placeholder="전체"></SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ALL">전체</SelectItem>
|
||||
<SelectItem value="SUCC">성공</SelectItem>
|
||||
<SelectItem value="FAIL">실패</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Label htmlFor="sDateSearchInput" className="mx-2 text-label">
|
||||
시작일
|
||||
</Label>
|
||||
<DatePicker id="sDateSearchInput" mode={"single"} selected={dayjs(startDate).toDate()} onSelect={(e) => setStartDate(dayjs(e).format('YYYY-MM-DD'))} />
|
||||
<Label htmlFor="eDateSearchInput" className="mx-2 text-label">
|
||||
종료일
|
||||
</Label>
|
||||
<DatePicker id="eDateSearchInput" mode={"single"} className="mr-2" selected={dayjs(endDate).toDate()} onSelect={(e) => setEndDate(dayjs(e).format('YYYY-MM-DD'))} />
|
||||
<Button color="green" className="h-[3.2rem]" type="button" onClick={handleResetSearchParam}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border flex flex-col h-[calc(100vh-28rem)]">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">로그 목록 (검색 조건에 따른 건수 : {query.data?.totalElements.toLocaleString('ko-KR') ?? 0} )</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] overflow-auto h-full !p-0">
|
||||
{query.isLoading ?
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 h-full flex justify-center items-center">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div>{tableComponent}</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export { LinkLogPage };
|
||||
@@ -0,0 +1 @@
|
||||
export { LinkParamsPage } from './ui/link-params-page';
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type Link } from "@/entities/link";
|
||||
|
||||
const columnHelper = createColumnHelper<Link>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor((_, index) => index + 1, {
|
||||
id: 'no',
|
||||
header: 'NO',
|
||||
}),
|
||||
columnHelper.accessor('link_sn', {
|
||||
id: 'link_sn',
|
||||
header: '연계 일련번호',
|
||||
}),
|
||||
columnHelper.accessor('link_nm', {
|
||||
id: 'link_nm',
|
||||
header: '연계 명',
|
||||
}),
|
||||
columnHelper.accessor('srvc_usg', {
|
||||
id: 'srvc_usg',
|
||||
header: '서비스용도',
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
];
|
||||
|
||||
export { columns };
|
||||
@@ -0,0 +1,98 @@
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type LinkVrbl } from "@/entities/link";
|
||||
import { Checkbox } from "@common/components";
|
||||
|
||||
const columnHelper = createColumnHelper<LinkVrbl>();
|
||||
|
||||
function vrblColumns({
|
||||
selectedVrblIds,
|
||||
setSelectedVrblIds,
|
||||
}: {
|
||||
selectedVrblIds: string[];
|
||||
setSelectedVrblIds: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}) {
|
||||
return [
|
||||
columnHelper.display({
|
||||
id: "select",
|
||||
header: ({ table }) => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
const allSelected =
|
||||
allRows.length > 0 &&
|
||||
allRows.every((row) =>
|
||||
selectedVrblIds.includes(row.original.link_vrbl_sn)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<Checkbox
|
||||
className="bg-white"
|
||||
checked={
|
||||
allSelected
|
||||
? true
|
||||
: false
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setSelectedVrblIds(allRows.map((row) => row.original.link_vrbl_sn));
|
||||
} else {
|
||||
setSelectedVrblIds([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const id = row.original.link_vrbl_sn;
|
||||
const checked = selectedVrblIds.includes(id);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onCheckedChange={(checked) => {
|
||||
setSelectedVrblIds((prev) =>
|
||||
checked ? [...prev, id] : prev.filter((x) => x !== id)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
|
||||
columnHelper.accessor("link_vrbl_sn", {
|
||||
id: "link_vrbl_sn",
|
||||
header: "연계 변수 일련번호",
|
||||
}),
|
||||
columnHelper.accessor("link_vrbl_nm", {
|
||||
id: "link_vrbl_nm",
|
||||
header: "변수 명",
|
||||
}),
|
||||
columnHelper.accessor("link_vrbl_cd", {
|
||||
id: "link_vrbl_cd",
|
||||
header: "변수코드",
|
||||
cell: (info) => (info.getValue() ? info.getValue() : '-'),
|
||||
}),
|
||||
columnHelper.accessor("link_vrbl_vl", {
|
||||
id: "link_vrbl_vl",
|
||||
header: "변수 값",
|
||||
cell: (info) => (info.getValue() === 'now' ? '요청일자' : info.getValue() === 'nowMonth' ? '요청월' : info.getValue()),
|
||||
}),
|
||||
columnHelper.accessor("link_vrbl_expln", {
|
||||
id: "link_vrbl_expln",
|
||||
header: "설명",
|
||||
cell: (info) => (
|
||||
<div
|
||||
className="max-w-72 truncate"
|
||||
title={info.getValue() ?? ''}
|
||||
>
|
||||
{info.getValue() || '-'}
|
||||
</div>
|
||||
),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
export { vrblColumns };
|
||||
@@ -0,0 +1,351 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
Button,
|
||||
Input,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Checkbox,
|
||||
RadioGroup,
|
||||
RadioGroupItem,
|
||||
Label
|
||||
} from "@common/components";
|
||||
import { FormItem, FormControl, FormField, Form } from "@/shared/ui/form";
|
||||
import { type LinkVrblFormData } from "@/entities/link";
|
||||
import dayjs from "dayjs";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useState, useEffect } from "react";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
|
||||
type LinkParamsDialogProps = {
|
||||
selectedLink: {
|
||||
linkSn: string;
|
||||
linkNm: string;
|
||||
};
|
||||
form: UseFormReturn<LinkVrblFormData>;
|
||||
insertLinkVrbl: (data: LinkVrblFormData) => void;
|
||||
dialogOpen: boolean;
|
||||
onOpenChange: (val: boolean) => void;
|
||||
updateLinkVrbl: (data: LinkVrblFormData) => void;
|
||||
}
|
||||
|
||||
function LinkParamsDialog({ selectedLink, form, insertLinkVrbl, dialogOpen, onOpenChange, updateLinkVrbl }: LinkParamsDialogProps) {
|
||||
|
||||
const isModifyMode = !!form.getValues('link_vrbl_sn');
|
||||
|
||||
const { confirm } = useConfirm();
|
||||
const [onConfirm, setOnConfirm] = useState<boolean>(false);
|
||||
const [isToday, setIsToday] = useState<boolean>(false);
|
||||
const [checkNowMonth, setCheckNowMonth] = useState<string>('now');
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
function replaceEmptyWithNull<T extends Record<string, unknown>>(data: T): T {
|
||||
const result = {} as T;
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
(result as Record<string, unknown>)[key] = value === '' ? null : value;
|
||||
}
|
||||
return result as T;
|
||||
};
|
||||
|
||||
const handleSubmit = async (data: LinkVrblFormData) => {
|
||||
const confirmMsg = isModifyMode ? '수정 하시겠습니까?' : '저장 하시겠습니까?';
|
||||
|
||||
await handleConfirm(async () => {
|
||||
const cleanData = replaceEmptyWithNull(data);
|
||||
|
||||
if (await confirm(confirmMsg)) {
|
||||
if (isModifyMode) {
|
||||
updateLinkVrbl(isToday ? { ...cleanData, link_vrbl_vl: checkNowMonth } : cleanData);
|
||||
} else {
|
||||
insertLinkVrbl(isToday ? { ...cleanData, link_vrbl_vl: checkNowMonth } : cleanData);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckTodayValue = (check: boolean | string) => {
|
||||
if (check) {
|
||||
setIsToday(true);
|
||||
form.setValue('link_vrbl_vl', (checkNowMonth === 'now' ? '요청일자' : '요청월'));
|
||||
} else {
|
||||
setIsToday(false);
|
||||
form.setValue('link_vrbl_vl', '');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!dialogOpen) {
|
||||
setIsToday(false);
|
||||
setCheckNowMonth('now');
|
||||
form.reset({
|
||||
link_vrbl_nm: '',
|
||||
link_vrbl_expln: '',
|
||||
link_vrbl_cd: '',
|
||||
link_vrbl_vl: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (isModifyMode && (form.getValues('link_vrbl_vl') === 'now' || form.getValues('link_vrbl_vl') === 'nowMonth')) {
|
||||
setIsToday(true);
|
||||
setCheckNowMonth(form.getValues('link_vrbl_vl'));
|
||||
}
|
||||
|
||||
}, [dialogOpen]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
onOpenChange(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button color="green" className="h-[3.2rem]" onClick={() => onOpenChange(true)}>행 추가</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[55rem]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{selectedLink.linkNm} ({selectedLink.linkSn})
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
<col width="30%" />
|
||||
<col width="70%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
{isModifyMode &&
|
||||
<TableRow>
|
||||
<TableHead>변수 일련번호</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_vrbl_sn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
}
|
||||
<TableRow>
|
||||
<TableHead>* 변수 명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_vrbl_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_vrbl_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_vrbl_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>변수 설명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_vrbl_expln"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_vrbl_expln && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_vrbl_expln.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>변수 코드</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_vrbl_cd"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_vrbl_cd && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_vrbl_cd.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변수 값</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="link_vrbl_vl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center gap-x-2 mb-4">
|
||||
<Checkbox id="isToday" className="!border-gray-600" checked={isToday} onCheckedChange={(check) => handleCheckTodayValue(check)} />
|
||||
<label htmlFor="isToday" className="text-gray-600">변수 값이 오늘 날짜 일시 체크해 주세요.</label>
|
||||
</div>
|
||||
{isToday &&
|
||||
<div className="flex items-center gap-x-2 mb-4">
|
||||
<RadioGroup
|
||||
name="checkNowMonth"
|
||||
onValueChange={(value) => {
|
||||
setCheckNowMonth(value);
|
||||
form.setValue('link_vrbl_vl', (value === 'now' ? '요청일자' : '요청월'));
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="now" id="now" checked={checkNowMonth === 'now'} />
|
||||
<Label htmlFor="now">요청일자(YYYY-MM-DD)</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="nowMonth" id="nowMonth" checked={checkNowMonth === 'nowMonth'} />
|
||||
<Label htmlFor="nowMonth">요청월(YYYY-MM)</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
}
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? (field.value === 'now' ? '요청일자' : field.value === 'nowMonth' ? '요청월' : field.value) : ''}
|
||||
className={`!text-[1.3rem] ${isToday ? 'bg-gray-100' : ''}`}
|
||||
autoComplete="off"
|
||||
disabled={isToday}
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.link_vrbl_vl && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.link_vrbl_vl.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isModifyMode &&
|
||||
<>
|
||||
<TableRow>
|
||||
<TableHead>수정자</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfr"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>수정일시</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfcn_dt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? dayjs(field.value).format('YYYY-MM-DD') : ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="submit">저장</Button>
|
||||
<DialogClose asChild>
|
||||
<Button color="white">취소</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
<ConfirmDialog isOpen={onConfirm} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
};
|
||||
|
||||
export { LinkParamsDialog };
|
||||
@@ -0,0 +1,405 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
Input,
|
||||
Label,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
LoadingWithAnimation,
|
||||
} from "@common/components";
|
||||
import { getCoreRowModel, useReactTable, type PaginationState, type RowSelectionState, } from "@tanstack/react-table";
|
||||
import { LinkApi, type Link, type LinkSearchParam, type LinkVrblFormData, linkVrblFormSchema } from "@/entities/link";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { columns } from "../model/table-columns";
|
||||
import { vrblColumns } from "../model/vrbl-table-columns";
|
||||
import { type LinkVrbl } from "@/entities/link";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { LinkParamsDialog } from "./link-params-dialog";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import { AUTH_USER_ID } from "@/shared/config";
|
||||
import { getCookie } from "@common/components";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
|
||||
function LinkParamsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const [linkSn, setLinkSn] = useState<string>('');
|
||||
const [linkNm, setLinkNm] = useState<string>('');
|
||||
|
||||
const [linkSearchParam, setLinkSearchParam] = useState<LinkSearchParam>({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
const [selectedLink, setSelectedLink] = useState<{
|
||||
linkSn: string;
|
||||
linkNm: string;
|
||||
}>({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
|
||||
const { confirm } = useConfirm();
|
||||
|
||||
const [selectedVrblIds, setSelectedVrblIds] = useState<string[]>([]);
|
||||
const [isDBdata, setIsDBData] = useState<boolean>(false);
|
||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||
const [onConfirm, setOnConfirm] = useState<boolean>(false);
|
||||
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const dbIdList = ['link0001', 'link0002'];
|
||||
|
||||
// Link 목록 쿼리
|
||||
const query = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`, linkSearchParam],
|
||||
queryFn: () => LinkApi.getLinkList({
|
||||
...linkSearchParam,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
});
|
||||
|
||||
// LinkVrbl 목록 쿼리 - 별도 관리
|
||||
const linkVrblQuery = useQuery({
|
||||
queryKey: ['linkVrblList', selectedLink.linkSn],
|
||||
queryFn: () => LinkApi.getLinkVrblList(selectedLink.linkSn),
|
||||
enabled: !!selectedLink.linkSn && !dbIdList.includes(selectedLink.linkSn),
|
||||
staleTime: 0, // 항상 최신 데이터를 가져오도록 설정
|
||||
});
|
||||
|
||||
const handleResetSearchParam = () => {
|
||||
setSelectedLink({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
setLinkSn('');
|
||||
setLinkNm('');
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
};
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
const { mutateAsync: insertLinkVrbl } = useMutation({
|
||||
mutationFn: async (data: LinkVrblFormData) =>
|
||||
await LinkApi.insertLinkVrbl({
|
||||
...data,
|
||||
link_sn: selectedLink.linkSn,
|
||||
mdfr: userUniqId,
|
||||
}),
|
||||
onSuccess: async () => {
|
||||
// 쿼리 무효화를 통한 최신 데이터 가져오기
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['linkVrblList', selectedLink.linkSn]
|
||||
});
|
||||
setDialogOpen(false);
|
||||
alert('저장되었습니다.');
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: updateLinkVrbl } = useMutation({
|
||||
mutationFn: async (data: LinkVrblFormData) => await LinkApi.updateLinkVrblInfo({
|
||||
...data,
|
||||
link_sn: selectedLink.linkSn,
|
||||
mdfr: userUniqId,
|
||||
}),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['linkVrblList', selectedLink.linkSn]
|
||||
});
|
||||
setDialogOpen(false);
|
||||
alert('저장되었습니다.');
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteLinkVrbl } = useMutation({
|
||||
mutationFn: async (data: string[]) => await LinkApi.deleteLinkVrbl(data),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: ['linkVrblList', selectedLink.linkSn]
|
||||
});
|
||||
setSelectedVrblIds([]);
|
||||
alert('삭제되었습니다.');
|
||||
}
|
||||
});
|
||||
|
||||
// linkVrblQuery의 데이터를 사용
|
||||
const tableData = useMemo(() => {
|
||||
return linkVrblQuery.data?.content ?? [];
|
||||
}, [linkVrblQuery.data]);
|
||||
|
||||
const vrblCols = useMemo(
|
||||
() =>
|
||||
vrblColumns({
|
||||
selectedVrblIds,
|
||||
setSelectedVrblIds,
|
||||
}),
|
||||
[selectedVrblIds]
|
||||
);
|
||||
|
||||
const vrblTable = useReactTable({
|
||||
data: tableData,
|
||||
columns: vrblCols,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
const form = useForm<LinkVrblFormData>({
|
||||
resolver: zodResolver(linkVrblFormSchema),
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
const handleRowClick = async (link: Link) => {
|
||||
setSelectedLink({
|
||||
linkSn: link.link_sn,
|
||||
linkNm: link.link_nm,
|
||||
});
|
||||
|
||||
setSelectedVrblIds([]);
|
||||
|
||||
if (dbIdList.includes(link.link_clsf_cd)) {
|
||||
setIsDBData(true);
|
||||
} else {
|
||||
setIsDBData(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
|
||||
setSelectedLink({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn,
|
||||
linkNm
|
||||
});
|
||||
};
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
const userData = await getUserInfo();
|
||||
return userData.userName;
|
||||
};
|
||||
|
||||
const handleDeleteParam = async () => {
|
||||
if (selectedVrblIds.length === 0) {
|
||||
alert('삭제할 파라미터를 선택해 주세요.');
|
||||
} else {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm('선택한 파라미터 정보를 삭제하시겠습니까?')) {
|
||||
deleteLinkVrbl(selectedVrblIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleVrblRowClick = async (data: LinkVrbl) => {
|
||||
const linkVrblInfo = await LinkApi.getLinkVrblInfo(data.link_vrbl_sn);
|
||||
|
||||
if (linkVrblInfo) {
|
||||
let newData = { ...linkVrblInfo };
|
||||
|
||||
if (linkVrblInfo.mdfr) {
|
||||
const userName = await fetchUserInfo();
|
||||
newData = {
|
||||
...linkVrblInfo,
|
||||
mdfr: userName,
|
||||
};
|
||||
}
|
||||
|
||||
form.reset(newData);
|
||||
}
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const { tableComponent } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
state: {
|
||||
pagination,
|
||||
rowSelection,
|
||||
},
|
||||
manualPagination: true,
|
||||
enableMultiRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
},
|
||||
dataTableOptions: {
|
||||
onRowClick: handleRowClick,
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
// LinkVrbl 데이터 표시 로직
|
||||
const renderLinkVrblContent = () => {
|
||||
if (isDBdata) {
|
||||
return (
|
||||
<div className="flex items-center justify-center mt-14 text-gray-500 text-3xl flex-1">
|
||||
변수를 지원하지 않는 연계 데이터입니다.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!selectedLink.linkSn) {
|
||||
return (
|
||||
<div className="flex items-center justify-center mt-14 text-gray-500 text-3xl flex-1">
|
||||
연계 데이터를 선택해주세요.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (linkVrblQuery.isLoading) {
|
||||
return (
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center h-full">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-2 mt-14 flex justify-end items-center flex-none">
|
||||
{tableData.length > 0 && (
|
||||
<Button color="red" className="h-[3.2rem] mr-2" type="button" onClick={handleDeleteParam}>
|
||||
행 삭제
|
||||
</Button>
|
||||
)}
|
||||
<LinkParamsDialog
|
||||
selectedLink={selectedLink}
|
||||
form={form}
|
||||
insertLinkVrbl={insertLinkVrbl}
|
||||
dialogOpen={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
updateLinkVrbl={updateLinkVrbl}
|
||||
/>
|
||||
</div>
|
||||
{tableData.length > 0 ? (
|
||||
<div className="overflow-auto flex-1 min-h-[300px]">
|
||||
<DataTable table={vrblTable} onRowClick={handleVrblRowClick} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center justify-center h-20 text-gray-500 text-3xl">
|
||||
등록된 연계 변수가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form className="flex items-center gap-2 p-2" onSubmit={handleSubmit}>
|
||||
<Label htmlFor="liSnSearchInput" className="mr-2 text-label">
|
||||
일련번호
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-4"
|
||||
id="liSnSearchInput"
|
||||
value={linkSn}
|
||||
onChange={(e) => setLinkSn(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Label htmlFor="liNmSearchInput" className="mr-2 text-label">
|
||||
연계 명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-2"
|
||||
id="liNmSearchInput"
|
||||
value={linkNm}
|
||||
onChange={(e) => setLinkNm(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Button color="green" className="h-[3.2rem]" type="button" onClick={handleResetSearchParam}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card !mb-0 flex h-[calc(100vh-28rem)] gap-5 !p-0">
|
||||
<div className="card card-border w-1/2 !mx-0 flex flex-col">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 목록</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] overflow-auto !p-0 h-full">
|
||||
{!query.isLoading ? (
|
||||
<div>{tableComponent}</div>
|
||||
) : (
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center h-full">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card card-border w-1/2 !mx-0 flex flex-col">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 변수 정보</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] !p-0 overflow-auto h-full flex flex-col">
|
||||
<Table variant="secondary" className="flex-none">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="80%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableHead>연계 일련번호</TableHead>
|
||||
<TableCell>
|
||||
{selectedLink.linkSn ? selectedLink.linkSn : '-'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계 명</TableHead>
|
||||
<TableCell>
|
||||
{selectedLink.linkNm ? selectedLink.linkNm : '-'}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
{renderLinkVrblContent()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmDialog isOpen={onConfirm} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { LinkParamsPage };
|
||||
@@ -0,0 +1 @@
|
||||
export { LinkStatsPage } from './ui/link-stats-page';
|
||||
@@ -0,0 +1,378 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import dayjs from "dayjs";
|
||||
import { LinkApi, type LinkStats, type LinkSearchParam } from "@/entities/link";
|
||||
import { Button, Label, DatePicker, LoadingWithAnimation } from "@common/components";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
Tooltip,
|
||||
Legend,
|
||||
type ChartOptions,
|
||||
type Chart,
|
||||
} from "chart.js";
|
||||
import { Line } from "react-chartjs-2";
|
||||
import { getCoreRowModel, type PaginationState, type RowSelectionState, } from "@tanstack/react-table";
|
||||
import { columns } from "@/pages/linkLog/model/table-columns";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
interface LinkStatsListObject {
|
||||
linkSuccStats: LinkStats[] | null;
|
||||
linkFailStats: LinkStats[] | null;
|
||||
}
|
||||
|
||||
function LinkStatsPage() {
|
||||
|
||||
const succChartRef = useRef<Chart<"line"> | null>(null);
|
||||
const failChartRef = useRef<Chart<"line"> | null>(null);
|
||||
|
||||
const [startDate, setStartDate] = useState<string>(dayjs().subtract(7, "day").format('YYYY-MM-DD'));
|
||||
const [endDate, setEndDate] = useState<string>(dayjs().format('YYYY-MM-DD'));
|
||||
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
|
||||
const [linkStatsList, setLinkStatsList] = useState<LinkStatsListObject | null>(null);
|
||||
const [logQueryParam, setLogQueryParam] = useState<{
|
||||
isSucc: 'SUCC' | 'FAIL';
|
||||
linkSn?: string;
|
||||
linkNm?: string;
|
||||
} | null>(null);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`, logQueryParam, pagination],
|
||||
queryFn: () =>
|
||||
LinkApi.getLinkLogList({
|
||||
...logQueryParam,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
startDate,
|
||||
endDate,
|
||||
}),
|
||||
enabled: !!logQueryParam,
|
||||
});
|
||||
|
||||
|
||||
const { tableComponent } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
state: {
|
||||
pagination,
|
||||
rowSelection,
|
||||
},
|
||||
manualPagination: true,
|
||||
enableMultiRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
},
|
||||
dataTableOptions: {
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
const [linkSearchParam, setLinkSearchParam] = useState<LinkSearchParam>({
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
});
|
||||
|
||||
ChartJS.register(
|
||||
LineElement,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
Tooltip,
|
||||
Legend
|
||||
);
|
||||
|
||||
const succData = {
|
||||
labels: linkStatsList?.linkSuccStats?.map((e) => e.link_nm),
|
||||
datasets: [
|
||||
{
|
||||
label: '연계 성공',
|
||||
data: linkStatsList?.linkSuccStats?.map((e) => e.count),
|
||||
fill: false,
|
||||
borderColor: 'rgb(53, 162, 235)',
|
||||
backgroundColor: 'rgb(53, 162, 235)',
|
||||
tension: 0.1,
|
||||
pointRadius: 6,
|
||||
pointHoverRadius: 8,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const succOptions: ChartOptions<'line'> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const failData = {
|
||||
labels: linkStatsList?.linkFailStats?.map((e) => e.link_nm),
|
||||
datasets: [
|
||||
{
|
||||
label: '연계 실패',
|
||||
data: linkStatsList?.linkFailStats?.map((e) => e.count),
|
||||
fill: false,
|
||||
borderColor: 'rgb(255, 99, 132)',
|
||||
backgroundColor: 'rgb(255, 99, 132)',
|
||||
tension: 0.1,
|
||||
pointRadius: 6,
|
||||
pointHoverRadius: 8,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const failOptions: ChartOptions<'line'> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const handleResetSearchParam = async () => {
|
||||
setLinkSearchParam({
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
startDate: dayjs().subtract(7, "day").format('YYYY-MM-DD'),
|
||||
endDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
|
||||
setLogQueryParam({
|
||||
isSucc: 'SUCC',
|
||||
linkNm: '',
|
||||
linkSn: '',
|
||||
});
|
||||
|
||||
setStartDate(dayjs().subtract(7, "day").format('YYYY-MM-DD'));
|
||||
setEndDate(dayjs().format('YYYY-MM-DD'));
|
||||
|
||||
const data = await LinkApi.getLinkStatsList({
|
||||
...linkSearchParam,
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
startDate: dayjs().subtract(7, "day").format('YYYY-MM-DD'),
|
||||
endDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
setLinkStatsList(data);
|
||||
};
|
||||
|
||||
const handleSubmit = async (e?: React.FormEvent<HTMLFormElement>) => {
|
||||
e?.preventDefault();
|
||||
|
||||
if (startDate && endDate) {
|
||||
|
||||
if (startDate > endDate) {
|
||||
alert('종료일이 시작일보다 이전일 수 없습니다.');
|
||||
return;
|
||||
} else {
|
||||
const data = await LinkApi.getLinkStatsList({
|
||||
...linkSearchParam,
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
setLinkStatsList(data);
|
||||
setLogQueryParam({
|
||||
isSucc: 'SUCC',
|
||||
linkNm: '',
|
||||
linkSn: '',
|
||||
});
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
}
|
||||
} else {
|
||||
alert('조회 날짜를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuccChartClick = async (event: React.MouseEvent<HTMLCanvasElement>) => {
|
||||
if (!succChartRef.current) return;
|
||||
|
||||
const chart = succChartRef.current;
|
||||
|
||||
const points = chart.getElementsAtEventForMode(
|
||||
event.nativeEvent,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
false
|
||||
);
|
||||
|
||||
if (points[0] && linkStatsList?.linkSuccStats) {
|
||||
const index = points[0].index;
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
setLogQueryParam({
|
||||
isSucc: 'SUCC',
|
||||
linkNm: linkStatsList.linkSuccStats[index]?.link_nm,
|
||||
linkSn: linkStatsList.linkSuccStats[index]?.link_sn,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleFailChartClick = async (event: React.MouseEvent<HTMLCanvasElement>) => {
|
||||
if (!failChartRef.current) return;
|
||||
|
||||
const chart = failChartRef.current;
|
||||
|
||||
const points = chart.getElementsAtEventForMode(
|
||||
event.nativeEvent,
|
||||
"nearest",
|
||||
{ intersect: true },
|
||||
false
|
||||
);
|
||||
|
||||
if (points[0] && linkStatsList?.linkFailStats) {
|
||||
const index = points[0].index;
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
setLogQueryParam({
|
||||
isSucc: 'FAIL',
|
||||
linkNm: linkStatsList.linkFailStats[index]?.link_nm,
|
||||
linkSn: linkStatsList.linkFailStats[index]?.link_sn,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmitToday = async () => {
|
||||
const data = await LinkApi.getLinkStatsList({
|
||||
...linkSearchParam,
|
||||
startDate: dayjs().format('YYYY-MM-DD'),
|
||||
endDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
setEndDate(dayjs().format('YYYY-MM-DD'));
|
||||
setStartDate(dayjs().format('YYYY-MM-DD'));
|
||||
setLinkStatsList(data);
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
startDate: dayjs().format('YYYY-MM-DD'),
|
||||
endDate: dayjs().format('YYYY-MM-DD'),
|
||||
});
|
||||
setLogQueryParam({
|
||||
isSucc: 'SUCC',
|
||||
linkNm: '',
|
||||
linkSn: '',
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
const data = await LinkApi.getLinkStatsList({
|
||||
...linkSearchParam,
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
setLinkStatsList(data);
|
||||
};
|
||||
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="card !mb-0 !min-h-[calc(100vh-16.8rem)]">
|
||||
<div className="card card-border">
|
||||
<form className="flex items-center gap-2 p-2" onSubmit={handleSubmit}>
|
||||
<Label htmlFor="sDateSearchInput" className="mx-2 text-label">
|
||||
시작일
|
||||
</Label>
|
||||
<DatePicker id="sDateSearchInput" mode={"single"} selected={dayjs(startDate).toDate()} onSelect={(e) => setStartDate(dayjs(e).format('YYYY-MM-DD'))} />
|
||||
<Label htmlFor="eDateSearchInput" className="mx-2 text-label">
|
||||
종료일
|
||||
</Label>
|
||||
<DatePicker id="eDateSearchInput" mode={"single"} className="mr-2" selected={dayjs(endDate).toDate()} onSelect={(e) => setEndDate(dayjs(e).format('YYYY-MM-DD'))} />
|
||||
<Button color="green" className="h-[3.2rem]" type="button" onClick={handleResetSearchParam}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="button" onClick={handleSubmitToday}>
|
||||
오늘 날짜로 조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="card !m-[1.2rem] !p-0 flex flex-col gap-y-8">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">조회 일자 : {`${linkSearchParam.startDate} ~ ${linkSearchParam.endDate}`}</h3>
|
||||
</div>
|
||||
<div className="h-[32rem] w-full">
|
||||
<Line ref={succChartRef} data={succData} options={succOptions} onClick={handleSuccChartClick} />
|
||||
</div>
|
||||
<div className="h-[32rem]">
|
||||
<Line ref={failChartRef} data={failData} options={failOptions} onClick={handleFailChartClick} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] !p-0">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">
|
||||
{logQueryParam?.linkNm ? `${logQueryParam.linkNm} 연계 ${logQueryParam.isSucc === 'FAIL' ? '실패' : '성공'} 로그 목록 (${linkSearchParam.startDate} ~ ${linkSearchParam.endDate}) - ${query.data?.totalElements.toLocaleString('ko-KR') ?? 0}건` : '로그 목록'}
|
||||
</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] h-[40rem] overflow-auto !p-0">
|
||||
{logQueryParam?.linkNm ?
|
||||
query.isLoading ?
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 h-full flex justify-center items-center">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
:
|
||||
<div>{tableComponent}</div>
|
||||
:
|
||||
<div className="flex items-center justify-center h-full text-gray-500 text-3xl">
|
||||
차트 데이터를 선택해주세요.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export { LinkStatsPage };
|
||||
@@ -0,0 +1 @@
|
||||
export { LinkTablePage} from "./ui/link-table-page";
|
||||
@@ -0,0 +1,103 @@
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type LinkTableColumns } from "@/entities/link";
|
||||
import { Checkbox } from "@common/components";
|
||||
|
||||
const columnHelper = createColumnHelper<LinkTableColumns>();
|
||||
|
||||
function tableColumns({ selectedColIds, setSelectedColIds }: {
|
||||
selectedColIds: string[];
|
||||
setSelectedColIds: React.Dispatch<React.SetStateAction<string[]>>;
|
||||
}) {
|
||||
return [
|
||||
columnHelper.display({
|
||||
id: "select",
|
||||
header: ({ table }) => {
|
||||
const allRows = table.getRowModel().rows;
|
||||
const allSelected =
|
||||
allRows.length > 0 &&
|
||||
allRows.every((row) =>
|
||||
selectedColIds.includes(row.original.col_sn)
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<Checkbox
|
||||
className="bg-white"
|
||||
checked={
|
||||
allSelected
|
||||
? true
|
||||
: false
|
||||
}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
setSelectedColIds(allRows.map((row) => row.original.col_sn));
|
||||
} else {
|
||||
setSelectedColIds([]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const id = row.original.col_sn;
|
||||
const checked = selectedColIds.includes(id);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center items-center">
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onCheckedChange={(checked) => {
|
||||
setSelectedColIds((prev) =>
|
||||
checked ? [...prev, id] : prev.filter((x) => x !== id)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor('col_sn', {
|
||||
id: 'col_sn',
|
||||
header: '컬럼 일련번호'
|
||||
}),
|
||||
columnHelper.accessor('sou_col_nm', {
|
||||
id: 'sou_col_nm',
|
||||
header: '원천컬럼명',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('sou_col_korn_nm', {
|
||||
id: 'sou_col_korn_nm',
|
||||
header: '원천컬럼 한글명',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('sou_col_sz', {
|
||||
id: 'sou_col_sz',
|
||||
header: '원천컬럼 크기',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('trsf_col_nm', {
|
||||
id: 'trsf_col_nm',
|
||||
header: '변환컬럼명',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('trsf_col_korn_nm', {
|
||||
id: 'trsf_col_korn_nm',
|
||||
header: '변환컬럼 한글명',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('trsf_col_sz', {
|
||||
id: 'trsf_col_sz',
|
||||
header: '변환컬럼 크기',
|
||||
cell: (info) => info.getValue() ? info.getValue() : '-',
|
||||
}),
|
||||
columnHelper.accessor('pk_yn', {
|
||||
id: 'pk_yn',
|
||||
header: 'PK 여부',
|
||||
cell: (info) => info.getValue() === 'Y' ? '예' : '아니오',
|
||||
}),
|
||||
]
|
||||
}
|
||||
|
||||
export { tableColumns };
|
||||
@@ -0,0 +1,503 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
Button,
|
||||
Input,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectValue,
|
||||
Checkbox
|
||||
} from "@common/components";
|
||||
import { FormItem, FormControl, FormField, Form } from "@/shared/ui/form";
|
||||
import { type LinkTableColData, type LinkTable } from "@/entities/link";
|
||||
import dayjs from "dayjs";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useState, useEffect } from "react";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
|
||||
interface LinkTableDialogProps {
|
||||
dialogOpen: boolean;
|
||||
onOpenChange: (val: boolean) => void;
|
||||
form: UseFormReturn<LinkTableColData>;
|
||||
tableInfo: LinkTable;
|
||||
insertLinkTableCol: (data: LinkTableColData) => void;
|
||||
updateLinkTableCol: (data: LinkTableColData) => void;
|
||||
}
|
||||
|
||||
function LinkTableDialog({ dialogOpen, onOpenChange, form, tableInfo, insertLinkTableCol, updateLinkTableCol }: LinkTableDialogProps) {
|
||||
|
||||
const isModifyMode = !!form.getValues('col_sn');
|
||||
|
||||
const { confirm } = useConfirm();
|
||||
const [onConfirm, setOnConfirm] = useState<boolean>(false);
|
||||
const [useColSeq, setUseColSeq] = useState<boolean>(false);
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
const handleSubmit = async (data: LinkTableColData) => {
|
||||
const confirmMsg = isModifyMode ? '수정 하시겠습니까?' : '저장 하시겠습니까?';
|
||||
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm(confirmMsg)) {
|
||||
if (!isModifyMode) {
|
||||
insertLinkTableCol({
|
||||
...data,
|
||||
link_sn: tableInfo.link_sn,
|
||||
tbl_sn: tableInfo.tbl_sn,
|
||||
});
|
||||
} else {
|
||||
updateLinkTableCol({
|
||||
...data,
|
||||
link_sn: tableInfo.link_sn,
|
||||
tbl_sn: tableInfo.tbl_sn,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleCheckColSeqValue = (check: boolean | string) => {
|
||||
if (check) {
|
||||
setUseColSeq(false);
|
||||
form.setValue('col_seq', null);
|
||||
} else {
|
||||
setUseColSeq(true);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!dialogOpen) {
|
||||
setUseColSeq(false);
|
||||
form.reset({
|
||||
tbl_sn: '',
|
||||
col_sn: '',
|
||||
sou_col_nm: '',
|
||||
sou_col_korn_nm: '',
|
||||
sou_col_data_frm: '',
|
||||
sou_col_sz: null,
|
||||
trsf_col_nm: '',
|
||||
trsf_col_korn_nm: '',
|
||||
trsf_col_data_frm: '',
|
||||
trsf_col_sz: null,
|
||||
col_seq: null,
|
||||
pk_yn: 'N',
|
||||
mdfcn_dt: '',
|
||||
mdfr: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (isModifyMode && form.getValues('col_seq')) {
|
||||
setUseColSeq(true);
|
||||
}
|
||||
|
||||
}, [dialogOpen]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={dialogOpen}
|
||||
onOpenChange={(open) => {
|
||||
onOpenChange(open);
|
||||
}}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button color="green" className="h-[3.2rem]" onClick={() => onOpenChange(true)}>행 추가</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[55rem]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{tableInfo.trsf_tbl_korn_nm} ({tableInfo.tbl_sn})
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||
<div className="card">
|
||||
<Table variant="secondary">
|
||||
<colgroup>
|
||||
<col width="30%" />
|
||||
<col width="70%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
{isModifyMode &&
|
||||
<TableRow>
|
||||
<TableHead>컬럼 일련번호</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="col_sn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
}
|
||||
<TableRow>
|
||||
<TableHead>* 원천컬럼명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sou_col_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.sou_col_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.sou_col_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 원천컬럼 한글명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sou_col_korn_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.sou_col_korn_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.sou_col_korn_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 원천컬럼 데이터 형식</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sou_col_data_frm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.sou_col_data_frm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.sou_col_data_frm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>원천컬럼 크기</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="sou_col_sz"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
min={0}
|
||||
max={99999999999999}
|
||||
onChange={(e) => {
|
||||
const onlyDigits = e.target.value.replace(/\D/g, "").slice(0, 14);
|
||||
field.onChange(onlyDigits); // react-hook-form에 반영
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<p className="text-gray-500 text-base pt-2">
|
||||
※ 14자리까지 입력 가능합니다.
|
||||
</p>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>PK 여부</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="pk_yn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Select value={field.value ?? "N"} onValueChange={field.onChange}>
|
||||
<SelectTrigger id="liResultCode" className="w-[10rem] mr-4">
|
||||
<SelectValue placeholder="아니오"></SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Y">예</SelectItem>
|
||||
<SelectItem value="N">아니오</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변환컬럼명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="trsf_col_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.trsf_col_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.trsf_col_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변환컬럼 한글명</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="trsf_col_korn_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.trsf_col_korn_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.trsf_col_korn_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변환컬럼 데이터 형식</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="trsf_col_data_frm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{form.formState.errors.trsf_col_data_frm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{form.formState.errors.trsf_col_data_frm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>변환컬럼 크기</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="trsf_col_sz"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
min={0}
|
||||
max={99999999999999}
|
||||
onChange={(e) => {
|
||||
const onlyDigits = e.target.value.replace(/\D/g, "").slice(0, 14);
|
||||
field.onChange(onlyDigits); // react-hook-form에 반영
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<p className="text-gray-500 text-base pt-2">
|
||||
※ 14자리까지 입력 가능합니다.
|
||||
</p>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>컬럼 매칭 순서</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="col_seq"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="flex items-center gap-x-2 mb-4">
|
||||
<Checkbox id="useColSeq" className="!border-gray-600" checked={!useColSeq} onCheckedChange={(check) => handleCheckColSeqValue(check)} />
|
||||
<label htmlFor="useColSeq" className="text-gray-600">사용안함</label>
|
||||
</div>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
autoComplete="off"
|
||||
type="number"
|
||||
min={0}
|
||||
disabled={!useColSeq}
|
||||
max={999}
|
||||
onChange={(e) => {
|
||||
const onlyDigits = e.target.value.replace(/\D/g, "").slice(0, 3);
|
||||
field.onChange(onlyDigits); // react-hook-form에 반영
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<p className="text-gray-500 text-base pt-2">
|
||||
※ 3자리까지 입력 가능합니다.
|
||||
</p>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{isModifyMode &&
|
||||
<>
|
||||
<TableRow>
|
||||
<TableHead>수정자</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfr"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>수정일시</TableHead>
|
||||
<TableCell>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="mdfcn_dt"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ? dayjs(field.value).format('YYYY-MM-DD') : ""}
|
||||
className="!text-[1.3rem]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button type="submit">저장</Button>
|
||||
<DialogClose asChild>
|
||||
<Button color="white">취소</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</Form>
|
||||
<ConfirmDialog isOpen={onConfirm} />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export { LinkTableDialog };
|
||||
@@ -0,0 +1,710 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useMemo } from "react";
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
Input,
|
||||
Label,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableRow,
|
||||
LoadingWithAnimation,
|
||||
} from "@common/components";
|
||||
import { getCoreRowModel, type PaginationState, type RowSelectionState, useReactTable } from "@tanstack/react-table";
|
||||
import { LinkApi, type Link, type LinkSearchParam, type LinkTable, type LinkTableColumns, type LinkTableColData, type LinkTableFormData, LinkTableColumnsSchema, LinkTableSchema } from "@/entities/link";
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { columns } from "../../linkParams/model/table-columns";
|
||||
import { tableColumns } from "../model/table-columns";
|
||||
import { LinkTableDialog } from "./link-table-dialog";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
|
||||
import { AUTH_USER_ID } from "@/shared/config";
|
||||
import { getCookie } from "@common/components";
|
||||
import { useAlertStore } from "@/shared/lib/use-alert-store";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
|
||||
import { FormItem, FormControl, FormField, Form } from "@/shared/ui/form";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
|
||||
function LinkTablePage() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const userUniqId = getCookie(AUTH_USER_ID) ?? "";
|
||||
|
||||
const [linkSn, setLinkSn] = useState<string>('');
|
||||
const [linkNm, setLinkNm] = useState<string>('');
|
||||
|
||||
const [linkSearchParam, setLinkSearchParam] = useState<LinkSearchParam>({
|
||||
linkSn: '',
|
||||
linkNm: ''
|
||||
});
|
||||
|
||||
const [selectedTableInfo, setSelectedTableInfo] = useState<LinkTable | null>(null);
|
||||
const [tableColList, setTableColList] = useState<LinkTableColumns[]>([]);
|
||||
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
|
||||
const [selectedColIds, setSelectedColIds] = useState<string[]>([]);
|
||||
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 100,
|
||||
});
|
||||
|
||||
const { setMessage: alert } = useAlertStore((state) => state);
|
||||
|
||||
const { confirm } = useConfirm();
|
||||
const [onConfirm, setOnConfirm] = useState<boolean>(false);
|
||||
|
||||
const handleConfirm = async (callback: () => Promise<void>) => {
|
||||
setOnConfirm(true);
|
||||
await callback();
|
||||
setOnConfirm(false);
|
||||
};
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkList`, linkSearchParam],
|
||||
queryFn: () => LinkApi.getLinkList({
|
||||
...linkSearchParam,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
});
|
||||
|
||||
const { mutateAsync: insertLinkTableCol } = useMutation({
|
||||
mutationFn: async (data: LinkTableColData) => await LinkApi.insertLinkTableCol({
|
||||
...data,
|
||||
mdfr: userUniqId,
|
||||
}),
|
||||
onSuccess: async () => {
|
||||
if (selectedTableInfo) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo`]
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList`]
|
||||
});
|
||||
|
||||
const tableData = await LinkApi.getLinkTableInfo(selectedTableInfo.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(selectedTableInfo.link_sn);
|
||||
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
setDialogOpen(false);
|
||||
alert('저장되었습니다.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: updateLinkTableCol } = useMutation({
|
||||
mutationFn: async (data: LinkTableColData) => await LinkApi.updateLinkTableCol({
|
||||
...data,
|
||||
mdfr: userUniqId,
|
||||
}),
|
||||
onSuccess: async () => {
|
||||
if (selectedTableInfo) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo`]
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList`]
|
||||
});
|
||||
|
||||
const tableData = await LinkApi.getLinkTableInfo(selectedTableInfo.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(selectedTableInfo.link_sn);
|
||||
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
setDialogOpen(false);
|
||||
alert('수정되었습니다.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: deleteLinkTableCol } = useMutation({
|
||||
mutationFn: async (data: string[]) => await LinkApi.deleteLinkTableCol(data),
|
||||
onSuccess: async () => {
|
||||
if (selectedTableInfo) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo`]
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList`]
|
||||
});
|
||||
|
||||
const tableData = await LinkApi.getLinkTableInfo(selectedTableInfo.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(selectedTableInfo.link_sn);
|
||||
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
setDialogOpen(false);
|
||||
alert('삭제되었습니다.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: insertLinkTableInfo } = useMutation({
|
||||
mutationFn: async (data: LinkTableFormData) => await LinkApi.insertLinkTableInfo(data),
|
||||
onSuccess: async () => {
|
||||
if (selectedTableInfo) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo`]
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList`]
|
||||
});
|
||||
|
||||
const tableData = await LinkApi.getLinkTableInfo(selectedTableInfo.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(selectedTableInfo.link_sn);
|
||||
|
||||
tableForm.reset({
|
||||
link_sn: tableData.link_sn,
|
||||
link_nm: tableData.link_nm,
|
||||
tbl_sn: tableData.tbl_sn ?? "",
|
||||
sou_tbl_nm: tableData.sou_tbl_nm ?? "",
|
||||
sou_tbl_korn_nm: tableData.sou_tbl_korn_nm ?? "",
|
||||
sou_tbl_expln: tableData.sou_tbl_expln ?? "",
|
||||
trsf_tbl_expln: tableData.trsf_tbl_expln ?? "",
|
||||
trsf_tbl_nm: tableData.trsf_tbl_nm ?? "",
|
||||
trsf_tbl_korn_nm: tableData.trsf_tbl_korn_nm ?? "",
|
||||
});
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
alert('저장되었습니다.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: updateLinkTableInfo } = useMutation({
|
||||
mutationFn: async (data: LinkTableFormData) => await LinkApi.updateLinkTableInfo(data),
|
||||
onSuccess: async () => {
|
||||
if (selectedTableInfo) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableInfo`]
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: [`${process.env.NEXT_PUBLIC_LINK_BACKEND_API_HOST}/getLinkTableColList`]
|
||||
});
|
||||
|
||||
const tableData = await LinkApi.getLinkTableInfo(selectedTableInfo.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(selectedTableInfo.link_sn);
|
||||
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
alert('수정되었습니다.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const tableCol = useMemo(
|
||||
() =>
|
||||
tableColumns({ selectedColIds, setSelectedColIds }),
|
||||
[selectedColIds]
|
||||
);
|
||||
|
||||
const colTable = useReactTable({
|
||||
data: tableColList,
|
||||
columns: tableCol,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
const form = useForm<LinkTableColData>({
|
||||
resolver: zodResolver(LinkTableColumnsSchema),
|
||||
defaultValues: {
|
||||
pk_yn: "N"
|
||||
}
|
||||
});
|
||||
|
||||
const tableForm = useForm<LinkTableFormData>({
|
||||
resolver: zodResolver(LinkTableSchema),
|
||||
defaultValues: {},
|
||||
});
|
||||
|
||||
const handleResetSearchParam = async () => {
|
||||
setSelectedTableInfo(null);
|
||||
setTableColList([]);
|
||||
setLinkSn('');
|
||||
setLinkNm('');
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn: '',
|
||||
linkNm: '',
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setSelectedTableInfo(null);
|
||||
setTableColList([]);
|
||||
|
||||
setLinkSearchParam({
|
||||
...linkSearchParam,
|
||||
linkSn,
|
||||
linkNm
|
||||
});
|
||||
};
|
||||
|
||||
const handleRowClick = async (data: Link) => {
|
||||
const tableData = await LinkApi.getLinkTableInfo(data.link_sn);
|
||||
const tableColList = await LinkApi.getLinkTableColList(data.link_sn);
|
||||
|
||||
setSelectedTableInfo(tableData);
|
||||
setTableColList(tableColList.content);
|
||||
setSelectedColIds([]);
|
||||
|
||||
tableForm.reset({
|
||||
link_sn: tableData.link_sn,
|
||||
link_nm: tableData.link_nm,
|
||||
tbl_sn: tableData.tbl_sn ?? "",
|
||||
sou_tbl_nm: tableData.sou_tbl_nm ?? "",
|
||||
sou_tbl_korn_nm: tableData.sou_tbl_korn_nm ?? "",
|
||||
sou_tbl_expln: tableData.sou_tbl_expln ?? "",
|
||||
trsf_tbl_expln: tableData.trsf_tbl_expln ?? "",
|
||||
trsf_tbl_nm: tableData.trsf_tbl_nm ?? "",
|
||||
trsf_tbl_korn_nm: tableData.trsf_tbl_korn_nm ?? "",
|
||||
});
|
||||
};
|
||||
|
||||
const handleColumnRowClick = async (data: LinkTableColumns) => {
|
||||
const colInfo = await LinkApi.getLinkTableColInfo({ colSn: data.col_sn, tblSn: data.tbl_sn });
|
||||
|
||||
if (colInfo) {
|
||||
let newData = { ...colInfo };
|
||||
|
||||
if (colInfo.mdfr) {
|
||||
const userName = await fetchUserInfo();
|
||||
newData = {
|
||||
...colInfo,
|
||||
mdfr: userName,
|
||||
};
|
||||
}
|
||||
|
||||
form.reset(newData);
|
||||
|
||||
}
|
||||
setDialogOpen(true)
|
||||
};
|
||||
|
||||
const fetchUserInfo = async () => {
|
||||
const userData = await getUserInfo();
|
||||
return userData.userName;
|
||||
};
|
||||
|
||||
const handleDeleteParam = async () => {
|
||||
if (selectedColIds.length === 0) {
|
||||
alert('삭제할 컬럼을 선택해 주세요.');
|
||||
} else {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm('선택한 컬럼 정보를 삭제하시겠습니까?')) {
|
||||
deleteLinkTableCol(selectedColIds);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleTableSubmit = async (data: LinkTableFormData) => {
|
||||
const regTblInfo = await LinkApi.countLinkTblNm(data.trsf_tbl_nm);
|
||||
const confirmMsg = (regTblInfo.count > 0 && data.trsf_tbl_nm !== selectedTableInfo?.trsf_tbl_nm) ? `이미 사용중인 테이블 명입니다. 해당 테이블 명으로 ${!data.tbl_sn ? '저장' : '수정'}하시겠습니까?` : `${!data.tbl_sn ? '저장' : '수정'}하시겠습니까?`;
|
||||
if (!data.tbl_sn) {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm(confirmMsg)) {
|
||||
insertLinkTableInfo({
|
||||
...data,
|
||||
mdfr: userUniqId,
|
||||
tbl_sn: (regTblInfo.count > 0) ? regTblInfo.tbl_sn : "",
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await handleConfirm(async () => {
|
||||
if (await confirm(confirmMsg)) {
|
||||
updateLinkTableInfo({
|
||||
...data,
|
||||
tbl_sn: (regTblInfo.count > 0) ? regTblInfo.tbl_sn : "",
|
||||
mdfr: userUniqId,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const { tableComponent } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
state: {
|
||||
pagination,
|
||||
rowSelection,
|
||||
},
|
||||
manualPagination: true,
|
||||
enableMultiRowSelection: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
},
|
||||
dataTableOptions: {
|
||||
onRowClick: handleRowClick,
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form className="flex items-center gap-2 p-2" onSubmit={handleSubmit}>
|
||||
<Label htmlFor="liSnSearchInput" className="mr-2 text-label">
|
||||
일련번호
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-4"
|
||||
id="liSnSearchInput"
|
||||
value={linkSn}
|
||||
onChange={(e) => setLinkSn(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Label htmlFor="liNmSearchInput" className="mr-2 text-label">
|
||||
연계 명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem] mr-2"
|
||||
id="liNmSearchInput"
|
||||
value={linkNm}
|
||||
onChange={(e) => setLinkNm(e.target.value)}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<Button color="green" className="h-[3.2rem]" type="button" onClick={handleResetSearchParam}>
|
||||
초기화
|
||||
</Button>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card !mb-0 flex h-[calc(100vh-28rem)] gap-5 !p-0">
|
||||
<div className="card card-border w-1/2 !mx-0 flex flex-col">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 목록</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] overflow-auto !p-0 h-full">
|
||||
{!query.isLoading ?
|
||||
<div>{tableComponent}</div>
|
||||
:
|
||||
<div className="[data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center h-full">
|
||||
<div className="relative size-[5rem]">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card card-border w-1/2 !mx-0 flex flex-col">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">연계 테이블 정보</h3>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] !p-0 h-full overflow-auto flex flex-col">
|
||||
<Form {...tableForm}>
|
||||
<form onSubmit={tableForm.handleSubmit(handleTableSubmit)}>
|
||||
{selectedTableInfo &&
|
||||
<div className="flex justify-end mb-2">
|
||||
<Button className="h-[3.2rem]" type="submit" color={selectedTableInfo.tbl_sn ? "green" : "primary"}>
|
||||
{selectedTableInfo.tbl_sn ? "수정" : "저장"}
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
<Table variant="secondary" className="flex-none">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="80%" />
|
||||
</colgroup>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableHead>연계 일련번호</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="link_sn"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>연계 명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="link_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
disabled
|
||||
/>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>원천 테이블 명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="sou_tbl_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.sou_tbl_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.sou_tbl_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>원천 테이블 한글명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="sou_tbl_korn_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.sou_tbl_korn_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.sou_tbl_korn_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>원천 테이블 설명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="sou_tbl_expln"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.sou_tbl_expln && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.sou_tbl_expln.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변환 테이블 명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="trsf_tbl_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.trsf_tbl_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.trsf_tbl_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>* 변환 테이블 한글명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="trsf_tbl_korn_nm"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.trsf_tbl_korn_nm && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.trsf_tbl_korn_nm.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableHead>변환 테이블 설명</TableHead>
|
||||
<TableCell className="!p-2">
|
||||
{selectedTableInfo ?
|
||||
<FormField
|
||||
control={tableForm.control}
|
||||
name="trsf_tbl_expln"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
value={field.value ?? ""}
|
||||
className="!text-[1.3rem] w-[400px]"
|
||||
placeholder={!field.value ? "-" : ""}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</FormControl>
|
||||
{tableForm.formState.errors.trsf_tbl_expln && (
|
||||
<p className="text-red-500 text-base pt-2">
|
||||
{tableForm.formState.errors.trsf_tbl_expln.message}
|
||||
</p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
:
|
||||
<span className="px-2 py-[1.2rem]">-</span>
|
||||
}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</form>
|
||||
</Form>
|
||||
{selectedTableInfo ?
|
||||
<>
|
||||
<div className="mb-2 mt-14 flex justify-end items-center flex-none">
|
||||
{tableColList.length > 0 &&
|
||||
<Button color="red" className="h-[3.2rem] mr-2" type="button" onClick={handleDeleteParam}>
|
||||
행 삭제
|
||||
</Button>
|
||||
}
|
||||
{selectedTableInfo.tbl_sn &&
|
||||
<LinkTableDialog dialogOpen={dialogOpen} onOpenChange={setDialogOpen} form={form} tableInfo={selectedTableInfo} insertLinkTableCol={insertLinkTableCol} updateLinkTableCol={updateLinkTableCol} />
|
||||
}
|
||||
</div>
|
||||
{selectedTableInfo.tbl_sn ?
|
||||
(tableColList.length > 0 ?
|
||||
<div className="flex-1 overflow-auto min-h-[300px]">
|
||||
<DataTable table={colTable} onRowClick={handleColumnRowClick} />
|
||||
</div>
|
||||
:
|
||||
<div className="flex items-center justify-center h-20 text-gray-500 text-3xl">
|
||||
등록된 테이블 컬럼이 없습니다.
|
||||
</div>)
|
||||
:
|
||||
<div className="flex items-center justify-center h-20 text-gray-500 text-3xl">
|
||||
테이블 정보를 등록해주세요.
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
:
|
||||
<div className="flex items-center justify-center text-gray-500 text-3xl mt-14 flex-1">
|
||||
연계 데이터를 선택해주세요.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ConfirmDialog isOpen={onConfirm} />
|
||||
</div >
|
||||
)
|
||||
};
|
||||
|
||||
export { LinkTablePage };
|
||||
@@ -7,7 +7,6 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -16,10 +15,11 @@ import {
|
||||
SelectValue,
|
||||
LoadingWithImage,
|
||||
} from "@common/components";
|
||||
import { columns } from "@/pages/loginlog/model/table-columns";
|
||||
import { LoginlogApi, type LoginlogParams } from "@/entities/loginlog";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/loginlog/model/table-columns";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
|
||||
type DateRange = {
|
||||
from?: Date | undefined;
|
||||
@@ -141,6 +141,11 @@ function LoginlogPage() {
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="card card-border">
|
||||
@@ -156,20 +161,9 @@ function LoginlogPage() {
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">사용자ID</Label>
|
||||
<Input
|
||||
|
||||
@@ -7,7 +7,6 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -15,13 +14,14 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@common/components";
|
||||
import { columns } from "@/pages/privacylog/model/table-columns";
|
||||
import {
|
||||
PrivacyLogPrivacy,
|
||||
type PrivacyLogParams,
|
||||
} from "@/entities/privacylog";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/privacylog/model/table-columns";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
|
||||
type DateRange = {
|
||||
from?: Date | undefined;
|
||||
@@ -132,6 +132,11 @@ function PrivacyLogPage() {
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="card card-border">
|
||||
@@ -147,20 +152,9 @@ function PrivacyLogPage() {
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">조회정보</Label>
|
||||
<Input
|
||||
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -31,8 +30,9 @@ import {
|
||||
} from "@/entities/post";
|
||||
import { BoardApi } from "@/entities/board";
|
||||
import { AuthorityButton } from "@/entities/menu-authority";
|
||||
import { useAlertStore, useSelectPrompt, useDataTable } from "@/shared/lib";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
import { useAlertStore, useSelectPrompt, useDataTable } from "@/shared/lib";
|
||||
import { columns } from "../model/table-columns";
|
||||
import { MovePostDialog } from "./move-post-dialog";
|
||||
|
||||
@@ -58,7 +58,7 @@ function RecentPage() {
|
||||
});
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange | undefined>({
|
||||
const [dateRange, setDateRange] = useState<DateRange>({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
});
|
||||
@@ -169,7 +169,7 @@ function RecentPage() {
|
||||
};
|
||||
|
||||
const handlePresetDateRange = (value: string) => {
|
||||
let presetDateRange: DateRange | undefined;
|
||||
let presetDateRange: DateRange;
|
||||
switch (value) {
|
||||
case "1W":
|
||||
presetDateRange = {
|
||||
@@ -190,12 +190,20 @@ function RecentPage() {
|
||||
};
|
||||
break;
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
presetDateRange = {
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
};
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange);
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
const onSoftDeletePost = async () => {
|
||||
const { resolved, value: delRsn } =
|
||||
await prompt("삭제 사유를 입력해주세요.");
|
||||
@@ -260,20 +268,9 @@ function RecentPage() {
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange?.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange?.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">게시판</Label>
|
||||
<Select
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { UserStatisticsApi } from "./user-statistics-service";
|
||||
@@ -0,0 +1,138 @@
|
||||
import {
|
||||
type AccessLog,
|
||||
type AccessLogParams,
|
||||
type AccessLogService,
|
||||
type PageableList,
|
||||
} from "../model/user-statistics-interface";
|
||||
|
||||
// 목 데이터
|
||||
const mockAccessLogs: AccessLog[] = [
|
||||
{
|
||||
no: 1,
|
||||
accessTime: "2024-01-15 14:30:25",
|
||||
accessIp: "192.168.1.100",
|
||||
accessRegion: "서울특별시",
|
||||
urlPath: "/index",
|
||||
browser: "Chrome",
|
||||
site: "SITE1",
|
||||
accessType: "메인",
|
||||
},
|
||||
{
|
||||
no: 2,
|
||||
accessTime: "2024-01-15 14:28:15",
|
||||
accessIp: "203.241.50.123",
|
||||
accessRegion: "부산광역시",
|
||||
urlPath: "/board/list",
|
||||
browser: "Edge",
|
||||
site: "SITE2",
|
||||
accessType: "게시판",
|
||||
},
|
||||
{
|
||||
no: 3,
|
||||
accessTime: "2024-01-15 14:25:42",
|
||||
accessIp: "175.223.45.67",
|
||||
accessRegion: "대구광역시",
|
||||
urlPath: "/statistics",
|
||||
browser: "Safari",
|
||||
site: "SITE1",
|
||||
accessType: "통계",
|
||||
},
|
||||
{
|
||||
no: 4,
|
||||
accessTime: "2024-01-15 14:22:18",
|
||||
accessIp: "211.252.87.45",
|
||||
accessRegion: "인천광역시",
|
||||
urlPath: "/contents/view",
|
||||
browser: "Firefox",
|
||||
site: "SITE3",
|
||||
accessType: "컨텐츠",
|
||||
},
|
||||
{
|
||||
no: 5,
|
||||
accessTime: "2024-01-15 14:20:33",
|
||||
accessIp: "121.254.12.89",
|
||||
accessRegion: "광주광역시",
|
||||
urlPath: "/search",
|
||||
browser: "Chrome",
|
||||
site: "SITE1",
|
||||
accessType: "검색",
|
||||
},
|
||||
];
|
||||
|
||||
// 목 API 서비스
|
||||
class UserStatisticsApiImpl implements AccessLogService {
|
||||
async getAccessLogList(
|
||||
params: AccessLogParams,
|
||||
): Promise<PageableList<AccessLog>> {
|
||||
// 실제 API 호출 대신 목 데이터 반환
|
||||
const { page = 0, size = 10 } = params;
|
||||
|
||||
// 필터링 로직 (실제로는 서버에서 처리)
|
||||
let filteredLogs = [...mockAccessLogs];
|
||||
|
||||
if (params.urlPath) {
|
||||
filteredLogs = filteredLogs.filter((log) =>
|
||||
log.urlPath.toLowerCase().includes(params.urlPath!.toLowerCase()),
|
||||
);
|
||||
}
|
||||
|
||||
if (params.accessIp) {
|
||||
filteredLogs = filteredLogs.filter((log) =>
|
||||
log.accessIp.includes(params.accessIp!),
|
||||
);
|
||||
}
|
||||
|
||||
if (params.accessRegion) {
|
||||
filteredLogs = filteredLogs.filter((log) =>
|
||||
log.accessRegion.includes(params.accessRegion!),
|
||||
);
|
||||
}
|
||||
|
||||
if (params.browser) {
|
||||
filteredLogs = filteredLogs.filter(
|
||||
(log) => log.browser.toLowerCase() === params.browser!.toLowerCase(),
|
||||
);
|
||||
}
|
||||
|
||||
if (params.siteId) {
|
||||
filteredLogs = filteredLogs.filter((log) => log.site === params.siteId);
|
||||
}
|
||||
|
||||
// 페이지네이션
|
||||
const start = page * size;
|
||||
const end = start + size;
|
||||
const paginatedLogs = filteredLogs.slice(start, end);
|
||||
|
||||
// 목 응답 생성
|
||||
return {
|
||||
content: paginatedLogs,
|
||||
pageable: {
|
||||
pageNumber: page,
|
||||
pageSize: size,
|
||||
sort: {
|
||||
empty: true,
|
||||
sorted: false,
|
||||
unsorted: true,
|
||||
},
|
||||
offset: page * size,
|
||||
paged: true,
|
||||
unpaged: false,
|
||||
},
|
||||
last: (page + 1) * size >= filteredLogs.length,
|
||||
totalPages: Math.ceil(filteredLogs.length / size),
|
||||
totalElements: filteredLogs.length,
|
||||
size,
|
||||
number: page,
|
||||
sort: {
|
||||
empty: true,
|
||||
sorted: false,
|
||||
unsorted: true,
|
||||
},
|
||||
first: page === 0,
|
||||
numberOfElements: paginatedLogs.length,
|
||||
empty: paginatedLogs.length === 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const UserStatisticsApi = new UserStatisticsApiImpl();
|
||||
@@ -0,0 +1 @@
|
||||
export { UserStatisticsPage } from "./ui/user-statistics-page";
|
||||
@@ -0,0 +1,148 @@
|
||||
import { type ChartData, type ChartOptions } from "chart.js";
|
||||
|
||||
// 일별 접속 현황 차트 데이터
|
||||
export const monthlyStatisticsData = {
|
||||
labels: [
|
||||
"1일",
|
||||
"2일",
|
||||
"3일",
|
||||
"4일",
|
||||
"5일",
|
||||
"6일",
|
||||
"7일",
|
||||
"8일",
|
||||
"9일",
|
||||
"10일",
|
||||
"11일",
|
||||
"12일",
|
||||
"13일",
|
||||
"14일",
|
||||
"15일",
|
||||
],
|
||||
datasets: [
|
||||
{
|
||||
type: "bar" as const,
|
||||
label: "페이지뷰",
|
||||
data: [
|
||||
850, 1000, 750, 750, 700, 750, 780, 800, 850, 900, 1200, 1700, 2300,
|
||||
2100, 2600,
|
||||
],
|
||||
backgroundColor: "rgba(54, 162, 235, 0.8)",
|
||||
order: 2,
|
||||
},
|
||||
{
|
||||
type: "line" as const,
|
||||
label: "신규 접속자",
|
||||
data: [
|
||||
700, 600, 500, 550, 500, 550, 500, 550, 600, 850, 1200, 1500, 1700,
|
||||
2300, 2200,
|
||||
],
|
||||
borderColor: "rgba(255, 99, 132, 0.8)",
|
||||
borderWidth: 2,
|
||||
tension: 0.4,
|
||||
pointRadius: 3,
|
||||
order: 0,
|
||||
},
|
||||
{
|
||||
type: "line" as const,
|
||||
label: "총 접속자",
|
||||
data: [
|
||||
200, 300, 400, 500, 600, 500, 550, 500, 400, 500, 350, 800, 900, 1200,
|
||||
1300,
|
||||
],
|
||||
borderColor: "rgba(75, 192, 192, 0.8)",
|
||||
borderWidth: 2,
|
||||
tension: 0.4,
|
||||
pointRadius: 3,
|
||||
order: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const monthlyStatisticsOptions: ChartOptions<"bar"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "top" as const,
|
||||
},
|
||||
tooltip: {
|
||||
mode: "index" as const,
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 사이트별 접속자 차트 데이터
|
||||
export const siteVisitorsData: ChartData<"bar"> = {
|
||||
labels: ["SITE1", "SITE2", "SITE3", "SITE4", "SITE5"],
|
||||
datasets: [
|
||||
{
|
||||
label: "사이트별 접속자",
|
||||
data: [2500, 2000, 1500, 1200, 800],
|
||||
backgroundColor: [
|
||||
"rgba(54, 162, 235, 0.8)",
|
||||
"rgba(153, 102, 255, 0.8)",
|
||||
"rgba(255, 99, 132, 0.8)",
|
||||
"rgba(255, 159, 64, 0.8)",
|
||||
"rgba(255, 205, 86, 0.8)",
|
||||
],
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const siteVisitorsOptions: ChartOptions<"bar"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
indexAxis: "y" as const,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// 브라우저별 접속자 차트 데이터
|
||||
export const browserStatisticsData: ChartData<"doughnut"> = {
|
||||
labels: ["Chrome", "Edge", "Safari", "Firefox", "Internet Explorer"],
|
||||
datasets: [
|
||||
{
|
||||
data: [45, 25, 15, 10, 5],
|
||||
backgroundColor: [
|
||||
"rgba(54, 162, 235, 0.8)",
|
||||
"rgba(153, 102, 255, 0.8)",
|
||||
"rgba(255, 99, 132, 0.8)",
|
||||
"rgba(255, 159, 64, 0.8)",
|
||||
"rgba(255, 205, 86, 0.8)",
|
||||
],
|
||||
borderColor: [
|
||||
"rgba(54, 162, 235, 1)",
|
||||
"rgba(153, 102, 255, 1)",
|
||||
"rgba(255, 99, 132, 1)",
|
||||
"rgba(255, 159, 64, 1)",
|
||||
"rgba(255, 205, 86, 1)",
|
||||
],
|
||||
borderWidth: 1,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const browserStatisticsOptions: ChartOptions<"doughnut"> = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "bottom" as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const chartData = {
|
||||
monthlyStatisticsData,
|
||||
monthlyStatisticsOptions,
|
||||
siteVisitorsData,
|
||||
siteVisitorsOptions,
|
||||
browserStatisticsData,
|
||||
browserStatisticsOptions,
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { type AccessLog } from "./user-statistics-interface";
|
||||
|
||||
const columnHelper = createColumnHelper<AccessLog>();
|
||||
|
||||
export const columns = [
|
||||
columnHelper.accessor("no", {
|
||||
header: "No",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("accessTime", {
|
||||
header: "접속일시",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("accessIp", {
|
||||
header: "접속IP",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("accessRegion", {
|
||||
header: "접속지역",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("urlPath", {
|
||||
header: "URL경로",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("browser", {
|
||||
header: "브라우저",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("site", {
|
||||
header: "사이트",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
columnHelper.accessor("accessType", {
|
||||
header: "메뉴명",
|
||||
cell: (info) => info.getValue(),
|
||||
}),
|
||||
];
|
||||
@@ -0,0 +1,56 @@
|
||||
export interface AccessLog {
|
||||
no: number;
|
||||
accessTime: string;
|
||||
accessIp: string;
|
||||
accessRegion: string;
|
||||
urlPath: string;
|
||||
browser: string;
|
||||
site: string;
|
||||
accessType: string;
|
||||
}
|
||||
|
||||
export interface AccessLogParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
urlPath?: string;
|
||||
accessIp?: string;
|
||||
accessRegion?: string;
|
||||
browser?: string;
|
||||
accessDateStart?: string;
|
||||
accessDateEnd?: string;
|
||||
siteId?: string;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
export interface AccessLogService {
|
||||
getAccessLogList(params: AccessLogParams): Promise<PageableList<AccessLog>>;
|
||||
}
|
||||
|
||||
export interface PageableList<T> {
|
||||
content: T[];
|
||||
pageable: {
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
sort: {
|
||||
empty: boolean;
|
||||
sorted: boolean;
|
||||
unsorted: boolean;
|
||||
};
|
||||
offset: number;
|
||||
paged: boolean;
|
||||
unpaged: boolean;
|
||||
};
|
||||
last: boolean;
|
||||
totalPages: number;
|
||||
totalElements: number;
|
||||
size: number;
|
||||
number: number;
|
||||
sort: {
|
||||
empty: boolean;
|
||||
sorted: boolean;
|
||||
unsorted: boolean;
|
||||
};
|
||||
first: boolean;
|
||||
numberOfElements: number;
|
||||
empty: boolean;
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
"use client";
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import { useState } from "react";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
Filler,
|
||||
Legend,
|
||||
Tooltip,
|
||||
type ChartData,
|
||||
} from "chart.js";
|
||||
import { Bar, Doughnut } from "react-chartjs-2";
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@common/components";
|
||||
import { SiteSelect } from "@/entities/site";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
import { UserStatisticsApi } from "../api";
|
||||
import { chartData } from "../model/chart-data";
|
||||
import { columns } from "../model/table-columns";
|
||||
import { type AccessLogParams } from "../model/user-statistics-interface";
|
||||
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
ArcElement,
|
||||
Filler,
|
||||
Legend,
|
||||
Tooltip,
|
||||
);
|
||||
|
||||
type DateRange = {
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
function UserStatisticsPage() {
|
||||
const defaultDateRange: DateRange = {
|
||||
from: dayjs().subtract(7, "day").toDate(),
|
||||
to: dayjs().toDate(),
|
||||
};
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [keywordType, setKeywordType] = useState("");
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [presetDateRange, setPresetDateRange] = useState("1W");
|
||||
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
|
||||
const [searchParams, setSearchParams] = useState<AccessLogParams>({
|
||||
urlPath: "",
|
||||
accessIp: "",
|
||||
accessRegion: "",
|
||||
browser: "",
|
||||
accessDateStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
|
||||
accessDateEnd: dayjs().format("YYYY-MM-DD"),
|
||||
sort: "accessTime,desc",
|
||||
});
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: ["user-statistics", searchParams, pagination],
|
||||
queryFn: () =>
|
||||
UserStatisticsApi.getAccessLogList({
|
||||
...searchParams,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
const { tableComponent, tableInstance: table } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
columns,
|
||||
state: {
|
||||
pagination,
|
||||
},
|
||||
manualPagination: true,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
},
|
||||
dataTableOptions: {
|
||||
viewPagination: true,
|
||||
},
|
||||
});
|
||||
|
||||
const handlePresetDateRange = (value: string) => {
|
||||
let presetDateRange: DateRange;
|
||||
switch (value) {
|
||||
case "1W":
|
||||
presetDateRange = {
|
||||
from: dayjs().subtract(7, "day").toDate(),
|
||||
to: dayjs().toDate(),
|
||||
};
|
||||
break;
|
||||
case "1M":
|
||||
presetDateRange = {
|
||||
from: dayjs().subtract(30, "day").toDate(),
|
||||
to: dayjs().toDate(),
|
||||
};
|
||||
break;
|
||||
case "3M":
|
||||
presetDateRange = {
|
||||
from: dayjs().subtract(90, "day").toDate(),
|
||||
to: dayjs().toDate(),
|
||||
};
|
||||
break;
|
||||
default:
|
||||
presetDateRange = defaultDateRange;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange);
|
||||
};
|
||||
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
});
|
||||
|
||||
// 검색 조건에 따라 검색 파라미터 설정
|
||||
const params: AccessLogParams = {
|
||||
sort: "accessTime,desc",
|
||||
accessDateStart: dateRange.from
|
||||
? dayjs(dateRange.from).format("YYYY-MM-DD")
|
||||
: "",
|
||||
accessDateEnd: dateRange.to
|
||||
? dayjs(dateRange.to).format("YYYY-MM-DD")
|
||||
: "",
|
||||
};
|
||||
|
||||
// 검색어 타입에 따라 파라미터 설정
|
||||
switch (keywordType) {
|
||||
case "urlPath":
|
||||
params.urlPath = keyword;
|
||||
break;
|
||||
case "ip":
|
||||
params.accessIp = keyword;
|
||||
break;
|
||||
case "region":
|
||||
params.accessRegion = keyword;
|
||||
break;
|
||||
case "browser":
|
||||
params.browser = keyword;
|
||||
break;
|
||||
}
|
||||
|
||||
setSearchParams(params);
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setKeywordType("");
|
||||
setKeyword("");
|
||||
setDateRange(defaultDateRange);
|
||||
setPresetDateRange("1W");
|
||||
setSearchParams({
|
||||
urlPath: "",
|
||||
accessIp: "",
|
||||
accessRegion: "",
|
||||
browser: "",
|
||||
accessDateStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
|
||||
accessDateEnd: dayjs().format("YYYY-MM-DD"),
|
||||
sort: "accessTime,desc",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="card card-border !mt-6">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label className="mr-2">날짜</Label>
|
||||
<Select value={presetDateRange} onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">사이트</Label>
|
||||
<SiteSelect enabledAllSite className="w-[16rem]" />
|
||||
<Label className="ml-4 mr-2 text-label">검색</Label>
|
||||
<Select
|
||||
value={keywordType}
|
||||
onValueChange={(value) => setKeywordType(value)}
|
||||
>
|
||||
<SelectTrigger className="h-[3.2rem] w-[10rem]">
|
||||
<SelectValue placeholder="선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="urlPath">URL경로</SelectItem>
|
||||
<SelectItem value="ip">접속IP</SelectItem>
|
||||
<SelectItem value="region">접속지역</SelectItem>
|
||||
<SelectItem value="browser">브라우저</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Input
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
|
||||
/>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="white"
|
||||
className="h-[3.2rem]"
|
||||
onClick={handleReset}
|
||||
>
|
||||
초기화
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{/* 차트 영역 */}
|
||||
<div className="grid grid-cols-2">
|
||||
{/* 일별 접속 현황 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-xl font-medium">일별 접속 현황</h3>
|
||||
</div>
|
||||
<div className="h-96 p-4">
|
||||
<Bar
|
||||
data={chartData.monthlyStatisticsData as ChartData<"bar">}
|
||||
options={chartData.monthlyStatisticsOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2">
|
||||
{/* 사이트별 접속자 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-xl font-medium">사이트별 접속자</h3>
|
||||
</div>
|
||||
<div className="h-96 p-4">
|
||||
<Bar
|
||||
data={chartData.siteVisitorsData}
|
||||
options={chartData.siteVisitorsOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 브라우저별 접속자 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-xl font-medium">브라우저별 접속자</h3>
|
||||
</div>
|
||||
<div className="h-96 p-4">
|
||||
<Doughnut
|
||||
data={chartData.browserStatisticsData}
|
||||
options={chartData.browserStatisticsOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 접속 로그 상세 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<h3 className="text-2xl font-medium text-label">접속 로그 상세</h3>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={table.getState().pagination.pageSize.toString()}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(parseInt(value));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="h-[3.2rem] w-[12rem]">
|
||||
<SelectValue placeholder="페이지 크기" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={pageSize.toString()}>
|
||||
{pageSize}개씩 보기
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem] !p-0">{tableComponent}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { UserStatisticsPage };
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
} from "@/entities/user";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker";
|
||||
import { columns } from "../model/table-columns";
|
||||
|
||||
type DateRange = {
|
||||
@@ -40,7 +40,7 @@ function UsersPage() {
|
||||
pageSize: 10,
|
||||
});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange | undefined>({
|
||||
const [dateRange, setDateRange] = useState<DateRange>({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
});
|
||||
@@ -62,7 +62,7 @@ function UsersPage() {
|
||||
});
|
||||
|
||||
const { data: userSttsStats } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS, "userStatusStatistics"],
|
||||
queryFn: () => UserApi.selectUserSttsStats(),
|
||||
});
|
||||
|
||||
@@ -145,7 +145,7 @@ function UsersPage() {
|
||||
};
|
||||
|
||||
const handlePresetDateRange = (value: string) => {
|
||||
let presetDateRange: DateRange | undefined;
|
||||
let presetDateRange: DateRange;
|
||||
switch (value) {
|
||||
case "1M":
|
||||
presetDateRange = {
|
||||
@@ -166,15 +166,24 @@ function UsersPage() {
|
||||
};
|
||||
break;
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
presetDateRange = {
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
};
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange);
|
||||
};
|
||||
|
||||
const getUserSttsStatsCnt = (code: string): number => {
|
||||
if (!Array.isArray(userSttsStats)) return 0;
|
||||
return userSttsStats.find((item) => item.sttsCd === code)?.cnt ?? 0;
|
||||
const handleDateRangeChange = (dateRange: DateRange) => {
|
||||
setDateRange(dateRange);
|
||||
setPresetDateRange("");
|
||||
};
|
||||
|
||||
const getUserSttsStatsCnt = (code: string): string => {
|
||||
if (!Array.isArray(userSttsStats)) return "0";
|
||||
const count = userSttsStats.find((item) => item.sttsCd === code)?.cnt ?? 0;
|
||||
return count.toLocaleString();
|
||||
};
|
||||
|
||||
const handleUserStateCodeChange = (code: string, checked: boolean) => {
|
||||
@@ -292,20 +301,9 @@ function UsersPage() {
|
||||
<SelectItem value="6M">6개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange?.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange?.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
<InputDateRangePicker
|
||||
dateRange={dateRange}
|
||||
setDateRange={handleDateRangeChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { InputDatePicker } from "@common/components";
|
||||
import { useEffect } from "react";
|
||||
import { useAlertStore } from "../lib";
|
||||
|
||||
type DateRange = {
|
||||
from?: Date;
|
||||
to?: Date;
|
||||
};
|
||||
|
||||
type InputDateRangePickerProps = {
|
||||
dateRange: DateRange;
|
||||
setDateRange: (dateRange: DateRange) => void;
|
||||
};
|
||||
|
||||
function InputDateRangePicker({
|
||||
dateRange,
|
||||
setDateRange,
|
||||
}: InputDateRangePickerProps) {
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
useEffect(() => {
|
||||
if (dateRange.from && dateRange.to && dateRange.from > dateRange.to) {
|
||||
alert("시작일보다 종료일이 커야 합니다.");
|
||||
setDateRange({ ...dateRange, to: dateRange.from });
|
||||
}
|
||||
}, [dateRange]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export { InputDateRangePicker };
|
||||
+10
-4
@@ -3,7 +3,7 @@
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "./store/common";
|
||||
import { useDmStore } from "./store";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
export default function Base({
|
||||
children,
|
||||
@@ -13,10 +13,16 @@ export default function Base({
|
||||
const { getMenuAuth } = useDmStore((state) => state);
|
||||
|
||||
const router = useRouter();
|
||||
const handleClick = (id:MenuItem) =>{
|
||||
const pathname = usePathname();
|
||||
|
||||
const handleClick = (id: MenuItem) => {
|
||||
getMenuAuth(id.menuCd);
|
||||
router.push(id.urlPath);
|
||||
}
|
||||
};
|
||||
|
||||
return <BaseLayout handleMenuClick={handleClick}>{children}</BaseLayout>;
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleClick}>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
|
||||
+10
-5
@@ -3,7 +3,7 @@
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "./store/common";
|
||||
import { useFoStore } from "./store";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
export default function Base({
|
||||
children,
|
||||
@@ -11,12 +11,17 @@ export default function Base({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { getMenuAuth } = useFoStore((state) => state);
|
||||
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const handleClick = (id:MenuItem) =>{
|
||||
|
||||
const handleClick = (id: MenuItem) => {
|
||||
getMenuAuth(id.menuCd);
|
||||
router.push(id.urlPath);
|
||||
}
|
||||
};
|
||||
|
||||
return <BaseLayout handleMenuClick={handleClick}>{children}</BaseLayout>;
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleClick}>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useFoStore } from "@/app/store";
|
||||
import type { TableUpperProps } from "@/app/store/processFacility";
|
||||
import { ContentsDiv, UiModal, UiTable } from "@common/business_components";
|
||||
import { cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
import DashBoardChartModal from "./modal/page";
|
||||
|
||||
@@ -30,20 +30,20 @@ const headMakeColSpanPlanFclty = (upHeadList: TableUpperProps[], planYearList: s
|
||||
const headMakeColSpanOperationStatus = (upHeadList: TableUpperProps[], searchYear: string) => {
|
||||
return ( <>
|
||||
<TableRow>
|
||||
<TableHead rowSpan={1} colSpan={16} style={{ whiteSpace: 'pre-line' }}>{searchYear+'년도 운영실태'}</TableHead>
|
||||
<TableHead rowSpan={1} colSpan={17} style={{ whiteSpace: 'pre-line' }}>{searchYear+'년도 운영실태'}</TableHead>
|
||||
</TableRow>
|
||||
<TableRow>{
|
||||
upHeadList.map((head: TableUpperProps) =>
|
||||
(head.upName === 'upChangeRe1'&&head.upSequnce===1?<TableHead key={head.id} rowSpan={2}colSpan={2} style={{ whiteSpace: 'pre-line' }}>{head.title}</TableHead>:
|
||||
!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
|
||||
!head.upSequnce?<TableHead style={{ whiteSpace: 'pre-line' }} rowSpan={2}key={head.id}>{head.title}</TableHead>:
|
||||
head.upName === 'upChangeRe2'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'연간 운영실태'}</TableHead>:
|
||||
head.upName === 'upChangeRe3'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'하절기 운영실태\n(6~8월)'}</TableHead>:
|
||||
head.upName === 'upChangeRe4'&&head.upSequnce===1?<TableHead key={head.id} colSpan={4} style={{ whiteSpace: 'pre-line' }}>{'동절기 운영실태\n(1~2월)'}</TableHead>:
|
||||
head.upName === 'upChangeRe5'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'지하수유입량'}</TableHead>:null))}
|
||||
head.upName === 'upChangeRe5'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'지하수 유입량 추정'}</TableHead>:null))}
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
{upHeadList.map((head: TableUpperProps) =>
|
||||
(head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'||head.upName === 'upChangeRe4'||head.upName === 'upChangeRe5' ?<TableHead key={head.id}>{head.title}</TableHead>:null))}
|
||||
(head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'||head.upName === 'upChangeRe4'||head.upName === 'upChangeRe5'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
|
||||
</TableRow>
|
||||
</> )
|
||||
}
|
||||
@@ -238,10 +238,10 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}:
|
||||
(head.id === 'flowRate' && index === 11)?<TableCell rowSpan={2} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 0)?<TableCell rowSpan={2} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 2)?<TableCell rowSpan={3} className={cn("sticky top-0 bg-secondary [&_tr]:border-b", "text-center" )} style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{'RDII추정시\n일강수량(mm)'}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 5)?<TableCell rowSpan={2} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el['rdiRain']}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}></TableCell>:
|
||||
(head.id === 'rdiRain'&&index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el['rdiRain']}</TableCell>:
|
||||
(head.id === 'rdiRain'&&index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}></TableCell>:
|
||||
(head.id === 'waterQlty')?<TableCell className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=0 && index<=4))?<TableCell className="bg-yellow-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
|
||||
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=5 && index<=10))?<TableCell className="bg-green-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
|
||||
|
||||
@@ -14,7 +14,7 @@ const tabList = [
|
||||
{ value: 'facilityInfo', label: '시설정보 및 운영현황' },
|
||||
{ value: 'mainGraph', label: '주요그래프' },
|
||||
{ value: 'rainManage', label: '강우일 운영현황' },
|
||||
{ value: 'drainageEquipment', label: '배수설비 정비현황' }
|
||||
//{ value: 'drainageEquipment', label: '배수설비 정비현황' }
|
||||
];
|
||||
|
||||
export default function DashBoard() {
|
||||
@@ -57,9 +57,18 @@ export default function DashBoard() {
|
||||
|
||||
/******************선택된 데이터******************/
|
||||
// 연도
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
|
||||
const [searchYear, setSearchYear] = useState<string>('2023');
|
||||
// 시설명
|
||||
const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
});
|
||||
|
||||
const [searchFcltyList, setSearchFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
|
||||
bscFctCd: '',
|
||||
bscFctNm: '',
|
||||
fcltyCpc: '',
|
||||
@@ -70,12 +79,12 @@ export default function DashBoard() {
|
||||
useEffect(() => {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
setFcltyList({
|
||||
bscFctCd: '11000SW001R',
|
||||
bscFctNm: '난지',
|
||||
fcltyCpc: 860000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, A2O',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
|
||||
setSearchFcltyList({
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
});
|
||||
}
|
||||
}, [isInit, initializeCommonData]);
|
||||
@@ -257,12 +266,29 @@ export default function DashBoard() {
|
||||
|
||||
const initVal = () => {
|
||||
setFcltyList({
|
||||
bscFctCd: '11000SW001R',
|
||||
bscFctNm: '난지',
|
||||
fcltyCpc: 860000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, A2O',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
});
|
||||
|
||||
setSearchFcltyList({
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
});
|
||||
setSearchYear('2023');
|
||||
searchFcltyList.bscFctNm = '수영';
|
||||
}
|
||||
|
||||
const searchVal = () => {
|
||||
setSearchFcltyList(fcltyList);
|
||||
if(searchFcltyList.bscFctCd) {
|
||||
searchOperationStatus(false, searchYear, fcltyList.bscFctCd);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -274,10 +300,13 @@ export default function DashBoard() {
|
||||
<SelectBox className="col-span-3" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined}/>
|
||||
<SelectBox className="col-span-3" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined}/>
|
||||
<div className="col-span-4 flex">
|
||||
<DashBoardFclty onFacilityChange={handleFacilityChange} sidoSigun={sigunValue!='0'?sigunValue:sidoValue!='0'?sidoValue:''} facilityNm={fcltyList.bscFctNm}></DashBoardFclty>
|
||||
<DashBoardFclty onFacilityChange={handleFacilityChange} sidoSigun={sigunValue!='0'?sigunValue:sidoValue!='0'?sidoValue:''} facilityNm={searchFcltyList.bscFctNm}></DashBoardFclty>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-4/12 mx-5 flex items-center justify-end space-x-2">
|
||||
<Button className={btnClass} onClick={searchVal}>
|
||||
조회
|
||||
</Button>
|
||||
<Button className={btnClass} onClick={initVal}>
|
||||
초기화
|
||||
</Button>
|
||||
@@ -288,16 +317,16 @@ export default function DashBoard() {
|
||||
</div>
|
||||
<UITab tabList={tabList} defaultValue="facilityInfo" onValueChange={setActiveTab}>
|
||||
<TabsContent value="facilityInfo">
|
||||
<ProcessFacilitySearch searchYear={searchYear} fcltyList={[fcltyList]} onFacilityChange={handleFacilityChange} />
|
||||
<ProcessFacilitySearch searchYear={searchYear} fcltyList={[searchFcltyList]} onFacilityChange={handleFacilityChange} />
|
||||
</TabsContent>
|
||||
<TabsContent value="mainGraph">
|
||||
<DashBoardChart searchYear={searchYear} fcltyList={[fcltyList]}/>
|
||||
<DashBoardChart searchYear={searchYear} fcltyList={[searchFcltyList]}/>
|
||||
</TabsContent>
|
||||
<TabsContent value="rainManage">
|
||||
<RainManageTab searchYear={searchYear} fcltyList={[fcltyList]}/>
|
||||
<RainManageTab searchYear={searchYear} fcltyList={[searchFcltyList]}/>
|
||||
</TabsContent>
|
||||
<TabsContent value="drainageEquipment">
|
||||
<DrainageEquipment searchYear={searchYear} fcltyList={[fcltyList]}/>
|
||||
<DrainageEquipment searchYear={searchYear} fcltyList={[searchFcltyList]}/>
|
||||
</TabsContent>
|
||||
</UITab>
|
||||
</ContentsDiv>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useFoStore } from "@/app/store";
|
||||
import { useEffect, useState } from "react";
|
||||
import SearchInput from "../../components/common/SearchInput";
|
||||
|
||||
export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm }: { onFacilityChange?: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => void, sidoSigun?: string, facilityNm?: string }) {
|
||||
export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm, }: { onFacilityChange?: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => void, sidoSigun?: string, facilityNm?: string }) {
|
||||
// 입력값 상태 관리
|
||||
const { Common, initializeCommonData } = useFoStore((state) => state);
|
||||
|
||||
@@ -12,7 +12,7 @@ export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm
|
||||
} = Common;
|
||||
|
||||
// 시설명
|
||||
const [bscFctNm, setFcltyName] = useState<string>(facilityNm || '난지');
|
||||
const [bscFctNm, setFcltyName] = useState<string>(facilityNm || '수영');
|
||||
const [bscFctCd, setbscFctCd] = useState<string>('11000SW001R');
|
||||
const [fcltyCpc, setFcltyCpc] = useState<number | ''>(860000);
|
||||
const [trtFrfNm, setTrtFrfNm] = useState<string | ''>('표준활성슬러지법, MLE, A2O');
|
||||
@@ -48,8 +48,6 @@ export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm
|
||||
}
|
||||
}
|
||||
|
||||
console.log('bscFctNm : ', bscFctNm);
|
||||
|
||||
return (
|
||||
<div className="z-50">
|
||||
<SearchInput label={'시설명'} setValue={fncNextPage} sidoSigun={sidoSigun} fcltyName={bscFctNm}/>
|
||||
|
||||
@@ -81,7 +81,8 @@ export default function FacilityPrecipitation() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -125,9 +126,9 @@ export default function FacilityPrecipitation() {
|
||||
useEffect(() => {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
searchFacilityPrecipitationList(1, 10, searchYear || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]);
|
||||
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, searchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let maxEfflWqltBod = 0;
|
||||
let maxEfflWqltTn = 0;
|
||||
let maxEfflWqltTp = 0;
|
||||
let maxefflWqltCod = 0;
|
||||
let maxefflWqltToc = 0;
|
||||
let maxEfflWqltSs = 0;
|
||||
|
||||
let maxInflAssctAftWqltBod = 0;
|
||||
@@ -80,7 +80,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let minEfflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltBod) : 0;
|
||||
let minEfflWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTn) : 0;
|
||||
let minEfflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTp) : 0;
|
||||
let minefflWqltCod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltCod) : 0;
|
||||
let minefflWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltToc) : 0;
|
||||
let minEfflWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltSs) : 0;
|
||||
|
||||
let minInflAssctAftWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltBod) : 0;
|
||||
@@ -97,7 +97,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let avgEfflWqltBod = 0;
|
||||
let avgEfflWqltTn = 0;
|
||||
let avgEfflWqltTp = 0;
|
||||
let avgefflWqltCod = 0;
|
||||
let avgefflWqltToc = 0;
|
||||
let avgEfflWqltSs = 0;
|
||||
|
||||
let avgInflAssctAftWqltBod = 0;
|
||||
@@ -115,7 +115,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
const efflWqltBod = parseNumber(item.efflWqltBod);
|
||||
const efflWqltTn = parseNumber(item.efflWqltTn);
|
||||
const efflWqltTp = parseNumber(item.efflWqltTp);
|
||||
const efflWqltCod = parseNumber(item.efflWqltCod);
|
||||
const efflWqltToc = parseNumber(item.efflWqltToc);
|
||||
const efflWqltSs = parseNumber(item.efflWqltSs);
|
||||
|
||||
const inflAssctAftWqltBod = parseNumber(item.inflAssctAftWqltBod);
|
||||
@@ -133,7 +133,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
maxEfflWqltBod = Math.max(maxEfflWqltBod, efflWqltBod);
|
||||
maxEfflWqltTn = Math.max(maxEfflWqltTn, efflWqltTn);
|
||||
maxEfflWqltTp = Math.max(maxEfflWqltTp, efflWqltTp);
|
||||
maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod);
|
||||
maxefflWqltToc = Math.max(maxefflWqltToc, efflWqltToc);
|
||||
maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs);
|
||||
|
||||
maxInflAssctAftWqltBod = Math.max(maxInflAssctAftWqltBod, inflAssctAftWqltBod);
|
||||
@@ -150,7 +150,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
minEfflWqltBod = Math.min(minEfflWqltBod, efflWqltBod);
|
||||
minEfflWqltTn = Math.min(minEfflWqltTn, efflWqltTn);
|
||||
minEfflWqltTp = Math.min(minEfflWqltTp, efflWqltTp);
|
||||
minefflWqltCod = Math.min(minefflWqltCod, efflWqltCod);
|
||||
minefflWqltToc = Math.min(minefflWqltToc, efflWqltToc);
|
||||
minEfflWqltSs = Math.min(minEfflWqltSs, efflWqltSs);
|
||||
|
||||
minInflAssctAftWqltBod = Math.min(minInflAssctAftWqltBod, inflAssctAftWqltBod);
|
||||
@@ -167,7 +167,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
avgEfflWqltBod += efflWqltBod;
|
||||
avgEfflWqltTn += efflWqltTn;
|
||||
avgEfflWqltTp += efflWqltTp;
|
||||
avgefflWqltCod += efflWqltCod;
|
||||
avgefflWqltToc += efflWqltToc;
|
||||
avgEfflWqltSs += efflWqltSs;
|
||||
|
||||
avgInflAssctAftWqltBod += inflAssctAftWqltBod;
|
||||
@@ -186,7 +186,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt: '평균',
|
||||
efflWqltBod: filteredData.length==0?'0.0':(Math.round(avgEfflWqltBod / filteredData.length * 10) / 10).toFixed(1),
|
||||
efflWqltCod: filteredData.length==0?'0.0':(Math.round(avgefflWqltCod / filteredData.length * 10) / 10).toFixed(1),
|
||||
efflWqltToc: filteredData.length==0?'0.0':(Math.round(avgefflWqltToc / filteredData.length * 10) / 10).toFixed(1),
|
||||
efflWqltSs: filteredData.length==0?'0.0':(Math.round(avgEfflWqltSs / filteredData.length * 10) / 10).toFixed(1),
|
||||
efflWqltTn: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTn / filteredData.length * 1000) / 1000).toFixed(3),
|
||||
efflWqltTp: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTp / filteredData.length * 1000) / 1000).toFixed(3),
|
||||
@@ -209,7 +209,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt : '최대',
|
||||
efflWqltBod : Math.round(maxEfflWqltBod * 10) / 10?(Math.round(maxEfflWqltBod * 10) / 10).toFixed(1):'0.0',
|
||||
efflWqltCod : Math.round(maxefflWqltCod * 10) / 10?(Math.round(maxefflWqltCod * 10) / 10).toFixed(1):'0.0',
|
||||
efflWqltToc : Math.round(maxefflWqltToc * 10) / 10?(Math.round(maxefflWqltToc * 10) / 10).toFixed(1):'0.0',
|
||||
efflWqltSs : Math.round(maxEfflWqltSs * 10) / 10?(Math.round(maxEfflWqltSs * 10) / 10).toFixed(1):'0.0',
|
||||
efflWqltTn : Math.round(maxEfflWqltTn * 1000) / 1000?(Math.round(maxEfflWqltTn * 1000) / 1000).toFixed(3):'0.000',
|
||||
efflWqltTp : Math.round(maxEfflWqltTp * 1000) / 1000?(Math.round(maxEfflWqltTp * 1000) / 1000).toFixed(3):'0.000',
|
||||
@@ -232,7 +232,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt : '최소',
|
||||
efflWqltBod : Math.round(minEfflWqltBod * 10) / 10?(Math.round(minEfflWqltBod?minEfflWqltBod:0 * 10) / 10).toFixed(1) : '0.0',
|
||||
efflWqltCod : Math.round(minefflWqltCod * 10) / 10?(Math.round(minefflWqltCod?minefflWqltCod:0 * 10) / 10).toFixed(1) : '0.0',
|
||||
efflWqltToc : Math.round(minefflWqltToc * 10) / 10?(Math.round(minefflWqltToc?minefflWqltToc:0 * 10) / 10).toFixed(1) : '0.0',
|
||||
efflWqltSs : Math.round(minEfflWqltSs * 10) / 10?(Math.round(minEfflWqltSs * 10) / 10).toFixed(1) : '0.0',
|
||||
efflWqltTn : Math.round(minEfflWqltTn * 1000) / 1000?(Math.round(minEfflWqltTn * 1000) / 1000).toFixed(3) : '0.000',
|
||||
efflWqltTp : Math.round(minEfflWqltTp * 1000) / 1000?(Math.round(minEfflWqltTp * 1000) / 1000).toFixed(3) : '0.000',
|
||||
@@ -299,16 +299,16 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
"유입TN(mg/L)": item.inflAssctAftWqltTn,
|
||||
"유입TP(mg/L)": item.inflAssctBfrWqltTp,
|
||||
"유출BOD(mg/L)": item.efflWqltBod,
|
||||
"유출TOC(COD)(mg/L)": item.efflWqltCod,
|
||||
"유출TOC(COD)(mg/L)": item.efflWqltToc,
|
||||
"유출SS(mg/L)": item.efflWqltSs,
|
||||
"유출TN(mg/L)": item.efflWqltTn,
|
||||
"유출TP(mg/L)": item.efflWqltTp,
|
||||
"강우량(일)": item.infoRnDay?item.infoRnDay.toString():"0.0",
|
||||
/* "강우량(mm/일)": item.infoRnDay?item.infoRnDay.toString():"0.0", */
|
||||
});
|
||||
});
|
||||
}
|
||||
if (excelDownList && excelDownList.length > 0) {
|
||||
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: searchYear+'년 '+fcltyName + ' 수질현황', sheetNameArray: [fcltyName + ' 수질현황'], headerRange: 'A1:L1' });
|
||||
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: searchYear+'년 '+fcltyName + ' 수질현황', sheetNameArray: [fcltyName + ' 수질현황'], headerRange: 'A1:K1' });
|
||||
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
|
||||
alert('다운받을 데이터가 없습니다.');
|
||||
}
|
||||
@@ -351,7 +351,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
</div>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">수질 TMS</h3>
|
||||
<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3>
|
||||
{/*<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3> */}
|
||||
</div>
|
||||
<div className="overflow-y-auto h-[250px]">
|
||||
<UiTable
|
||||
@@ -449,6 +449,9 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
|
||||
</UiTable>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 청천시 : 강우량이 3mm미만의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tab_term_rain">
|
||||
@@ -476,6 +479,9 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
|
||||
</UiTable>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</UITab>
|
||||
</TabsContent>
|
||||
|
||||
@@ -48,7 +48,8 @@ export default function ProcessFacilitySearch() {
|
||||
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
|
||||
const [fcltyName, setFcltyName] = useState<string>("");
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
//const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>("2023");
|
||||
const [fcltCpct, setFcltCpct] = useState<string>("0");
|
||||
|
||||
// 모달 상태 추가
|
||||
@@ -196,7 +197,7 @@ export default function ProcessFacilitySearch() {
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>}
|
||||
bodyClass="sm:max-w-[90vw] w-[1200px] h-[920px]"
|
||||
bodyClass="sm:max-w-[90vw] w-[1300px] h-[920px]"
|
||||
open={selectedRow !== null}
|
||||
onOpenChange={(open) => !open && closeModal()}
|
||||
noHeaderYn="Y"
|
||||
|
||||
@@ -52,6 +52,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
return Number(value.replace(/,/g, ''));
|
||||
};
|
||||
|
||||
console.log('filteredData : ', filteredData);
|
||||
let maxtotInflAmnt = 0;
|
||||
let maxinflAmnt = 0;
|
||||
let maxsumFlow = 0;
|
||||
@@ -65,7 +66,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
let minsumFlow = parseNumber(filteredData[0]?.sumFlow);
|
||||
let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt);
|
||||
let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt);
|
||||
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay);
|
||||
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay && filteredData[0]?.infoRnDay.toString().trim() !== 'x' ? filteredData[0]?.infoRnDay.toString().trim() : 0);
|
||||
|
||||
let avgtotInflAmnt = 0;
|
||||
let avginflAmnt = 0;
|
||||
@@ -80,7 +81,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
const sumFlow = parseNumber(item.sumFlow);
|
||||
const returnAmnt = parseNumber(item.returnAmnt);
|
||||
const efflAmnt = parseNumber(item.efflAmnt);
|
||||
const infoRnDay = parseNumber(item.infoRnDay);
|
||||
const infoRnDay = parseNumber(item.infoRnDay && item.infoRnDay.toString().trim() !== 'x' ? item.infoRnDay.toString().trim() : 0);
|
||||
maxtotInflAmnt = Math.max(maxtotInflAmnt, totInflAmnt);
|
||||
maxinflAmnt = Math.max(maxinflAmnt, inflAmnt);
|
||||
maxsumFlow = Math.max(maxsumFlow, sumFlow);
|
||||
@@ -166,7 +167,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
"연계처리량(m³/일)": item.sumFlow,
|
||||
"총인반류수량(m³/일)": item.returnAmnt,
|
||||
"방류량(m³/일)": item.efflAmnt,
|
||||
"강우량": item.infoRnDay,
|
||||
/* "강우량(mm/일)": item.infoRnDay, */
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -194,20 +195,18 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="tab_term_summer">
|
||||
@@ -220,20 +219,18 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="tab_term_winter">
|
||||
@@ -246,20 +243,18 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="tab_term_chuncheon">
|
||||
@@ -272,20 +267,21 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 청천시 : 강우량이 3mm미만의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="tab_term_rain">
|
||||
@@ -298,10 +294,11 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</UITab>
|
||||
|
||||
@@ -49,7 +49,8 @@ export default function ProcessFacilitySearch() {
|
||||
const [fcltCpct, setFcltCpct] = useState<string>("0");
|
||||
const [fcltyName, setFcltyName] = useState<string>("");
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
//const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>("2023");
|
||||
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
|
||||
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
|
||||
const [resetPage, setResetPage] = useState<boolean>(false);
|
||||
@@ -125,10 +126,11 @@ export default function ProcessFacilitySearch() {
|
||||
// 시도 데이터 초기화
|
||||
useEffect(() => {
|
||||
if(!isInit) {
|
||||
console.log('searchYearValue', searchYearValue, isInit);
|
||||
initializeCommonData();
|
||||
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
searchFlowRateList(1, 10, searchYearValue || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]);
|
||||
}, [isInit, initializeCommonData, searchFlowRateList, searchYearValue]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
@@ -197,7 +199,7 @@ export default function ProcessFacilitySearch() {
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>}
|
||||
bodyClass="sm:max-w-[90vw] w-[1050px] h-[700px]"
|
||||
bodyClass="sm:max-w-[90vw] w-[1250px] h-[780px]"
|
||||
open={selectedRow !== null}
|
||||
onOpenChange={(open: boolean) => !open && closeModal()}
|
||||
/>
|
||||
|
||||
@@ -70,7 +70,8 @@ export default function PlanSewage() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -190,6 +191,11 @@ export default function PlanSewage() {
|
||||
<div className={widthSize}>
|
||||
<UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 여유용량 = 하수처리시설 시설용량 – 건기시 유입유량(평균)</span><br/>
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 변동부하율 = 건기시 일최대 유량/일평균유량(기준 최근 3개년)</span><br/>
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 분류식화율 = 분류식지역 면적(㎢) / 처리구역 면적합계(㎢) ×100</span><br/>
|
||||
</div>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</MainDiv>
|
||||
|
||||
@@ -1,103 +1,34 @@
|
||||
"use client"
|
||||
import { useFoStore } from "@/app/store";
|
||||
import type { SewageOccurDetailProps } from "@/app/store/sewageOccur";
|
||||
import type { modalTableType } from "@/app/store/sewageOccur";
|
||||
import { DateMonthPicker, UiTable } from "@common/business_components/ui";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { Table, TableCell, TableHead, TableRow } from "@common/components/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
|
||||
export default function SewageOccurModal(props: { fcltyName: string; facilityCd : string; searchYear : string}) {
|
||||
// 그리드 관련
|
||||
const {SewageOccurDetail} = useFoStore((state) => state);
|
||||
const {SewageOccurDetail, searchSewageOccurDetailList} = useFoStore((state) => state);
|
||||
|
||||
// searchYear 유효성 검사 및 기본값 설정
|
||||
const validSearchYear = props.searchYear && /^\d{4}$/.test(props.searchYear) ? props.searchYear : new Date().getFullYear().toString();
|
||||
|
||||
const [startYearMonth, setStartYearMonth] = useState(validSearchYear+'01');
|
||||
const [endYearMonth, setEndYearMonth] = useState(validSearchYear+'12');
|
||||
const [detailList, setDetailList] = useState<SewageOccurDetailProps[]>([]);
|
||||
const [detailList, setDetailList] = useState<modalTableType[]>([]);
|
||||
const {
|
||||
upHeadListSewageOccurDetail,
|
||||
gridListSewageOccurDetail
|
||||
} = SewageOccurDetail;
|
||||
|
||||
useEffect(() => {
|
||||
setDetailList(gridListSewageOccurDetail);
|
||||
}, [gridListSewageOccurDetail]);
|
||||
searchSewageOccurDetailList(props.facilityCd, validSearchYear);
|
||||
}, [searchSewageOccurDetailList, props.facilityCd, validSearchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredList = gridListSewageOccurDetail.filter(item => item.msrDt.replace(/-/g, '') >= startYearMonth && item.msrDt.replace(/-/g, '') <= endYearMonth);
|
||||
let maxWaterUsage = 0;
|
||||
let maxSwageChangePercent = 0;
|
||||
let maxEstimationSewage = 0;
|
||||
|
||||
let minWaterUsage = filteredList[0]?.waterUsage || 0;
|
||||
let minSwageChangePercent = filteredList[0]?.swageChangePercent || 0;
|
||||
let minEstimationSewage = filteredList[0]?.estimationSewage || 0;
|
||||
|
||||
let totalWaterUsage = 0;
|
||||
let totalSwageChangePercent = 0;
|
||||
let totalEstimationSewage = 0;
|
||||
|
||||
let avgWaterUsage = 0;
|
||||
let avgSwageChangePercent = 0;
|
||||
let avgEstimationSewage = 0;
|
||||
|
||||
filteredList.forEach(item => {
|
||||
if (item.waterUsage > maxWaterUsage) {
|
||||
maxWaterUsage = item.waterUsage;
|
||||
}
|
||||
if (item.swageChangePercent > maxSwageChangePercent) {
|
||||
maxSwageChangePercent = item.swageChangePercent;
|
||||
}
|
||||
if (item.estimationSewage > maxEstimationSewage) {
|
||||
maxEstimationSewage = item.estimationSewage;
|
||||
}
|
||||
|
||||
if (item.waterUsage < minWaterUsage) {
|
||||
minWaterUsage = item.waterUsage;
|
||||
}
|
||||
if (item.swageChangePercent < minSwageChangePercent) {
|
||||
minSwageChangePercent = item.swageChangePercent;
|
||||
}
|
||||
if (item.estimationSewage < minEstimationSewage) {
|
||||
minEstimationSewage = item.estimationSewage;
|
||||
}
|
||||
|
||||
totalWaterUsage += item.waterUsage;
|
||||
totalSwageChangePercent += item.swageChangePercent;
|
||||
totalEstimationSewage += item.estimationSewage;
|
||||
});
|
||||
|
||||
avgWaterUsage = totalWaterUsage?totalWaterUsage / filteredList.length:0;
|
||||
avgSwageChangePercent = totalSwageChangePercent?totalSwageChangePercent / filteredList.length:0;
|
||||
avgEstimationSewage = totalEstimationSewage?totalEstimationSewage / filteredList.length:0;
|
||||
|
||||
const newData = [
|
||||
{
|
||||
msrDt: '평균',
|
||||
waterUsage: Math.round(avgWaterUsage * 100) / 100,
|
||||
swageChangePercent: Math.round(avgSwageChangePercent * 100) / 100,
|
||||
estimationSewage: Math.round(avgEstimationSewage * 100) / 100
|
||||
},
|
||||
{
|
||||
msrDt: '최대',
|
||||
waterUsage: maxWaterUsage,
|
||||
swageChangePercent: maxSwageChangePercent,
|
||||
estimationSewage: maxEstimationSewage
|
||||
},
|
||||
{
|
||||
msrDt: '최소',
|
||||
waterUsage: minWaterUsage,
|
||||
swageChangePercent: minSwageChangePercent,
|
||||
estimationSewage: minEstimationSewage
|
||||
},
|
||||
...filteredList
|
||||
]
|
||||
setDetailList(newData);
|
||||
|
||||
|
||||
}, [startYearMonth, endYearMonth, gridListSewageOccurDetail]);
|
||||
setDetailList(gridListSewageOccurDetail);
|
||||
}, [gridListSewageOccurDetail]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -18,7 +18,8 @@ export default function PlanSewage() {
|
||||
SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
|
||||
SewageOccurSearch: { labelArray: [] },
|
||||
initializeCommonData: () => {},
|
||||
searchSewageOccurList: () => {}
|
||||
searchSewageOccurList: () => {},
|
||||
calculateModalDataForAllFacilities: async () => []
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -83,7 +84,8 @@ export default function PlanSewage() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
// const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -126,9 +128,9 @@ export default function PlanSewage() {
|
||||
useEffect(() => {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
searchSewageOccurList(1, 10, searchYear || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]);
|
||||
}, [isInit, initializeCommonData, searchSewageOccurList, searchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
@@ -224,7 +226,11 @@ export default function PlanSewage() {
|
||||
</ContentsDiv>
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<SewageOccurModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYear}></SewageOccurModal>}
|
||||
body={<SewageOccurModal
|
||||
fcltyName={fcltyNm}
|
||||
facilityCd={fcltyCd}
|
||||
searchYear={searchYear}
|
||||
/>}
|
||||
bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]"
|
||||
open={selectedRow !== null}
|
||||
onOpenChange={(open) => !open && closeModal()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client"
|
||||
import { useFoStore } from "@/app/store";
|
||||
import type { UnderWaterInputModalProps } from "@/app/store/underWaterInput";
|
||||
import type { UnderWaterInputModalProps } from "@/app/store/underWaterInput/types";
|
||||
import { DateMonthPicker, UITab, UiTable } from "@common/business_components/ui";
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { Table, TableCell, TableHead, TableRow, TabsContent } from "@common/components/ui";
|
||||
@@ -19,7 +19,7 @@ const tabList1 = [
|
||||
|
||||
export default function UnderWaterInputModal({fcltyName='', searchYear=new Date().getFullYear().toString()} : ModalProps) {
|
||||
// 그리드 관련
|
||||
const {UnderWaterDetail} = useFoStore((state) => state);
|
||||
const {UnderWaterDetail, unserWaterModal} = useFoStore((state) => state);
|
||||
|
||||
const [startYmDate, setStartYmDate] = useState<string>(searchYear+'01');
|
||||
const [endYmDate, setEndYmDate] = useState<string>(searchYear+'12');
|
||||
@@ -115,7 +115,10 @@ export default function UnderWaterInputModal({fcltyName='', searchYear=new Date(
|
||||
setPrevEndYmDate(searchYear+'12');
|
||||
}
|
||||
}, [searchYear, tabTerm]);
|
||||
|
||||
useEffect(()=>{
|
||||
console.log(fcltyName, searchYear);
|
||||
unserWaterModal(fcltyName, searchYear);
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if(endYmDate < startYmDate) {
|
||||
alert('종료일이 시작일보다 작습니다.')
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, Selec
|
||||
import ListDiv from "@common/business_components/ui/list_div";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import UnderWaterInputModal from "./modal/page";
|
||||
import type { UnderWaterProps } from "@/app/store/underWaterInput";
|
||||
import type { UnderWaterProps } from "@/app/store/underWaterInput/types";
|
||||
|
||||
export default function UnderWaterInput() {
|
||||
const {Common
|
||||
@@ -82,7 +82,8 @@ export default function UnderWaterInput() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -114,7 +115,7 @@ export default function UnderWaterInput() {
|
||||
// 셀 클릭 시 모달 표시
|
||||
const cellClick = (index: number) => {
|
||||
if (underWater[index]) {
|
||||
setFcltyNm(underWater[index].bscFctNm);
|
||||
setFcltyNm(underWater[index].bscFctCd);
|
||||
setSelectedRow(index); // 클릭한 행의 index 저장
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
====================================**/
|
||||
|
||||
import { getFetch } from "@/app/components/api";
|
||||
import { LoginApi } from "@/src/entities/auth";
|
||||
import type { ILogin } from "@/src/entities/auth/model/login-interface";
|
||||
|
||||
/**
|
||||
* STORE - TYPE
|
||||
@@ -413,15 +411,6 @@ export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)
|
||||
|
||||
// 시도 변경 이벤트
|
||||
initializeCommonData: async () => {
|
||||
const payLoad : ILogin = {
|
||||
userId: "tuser10003",
|
||||
password: "test1234!"
|
||||
};
|
||||
|
||||
console.log(payLoad.userId)
|
||||
console.log(payLoad.password)
|
||||
|
||||
LoginApi.login(payLoad);
|
||||
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
|
||||
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
|
||||
set((state: CommonType) => ({
|
||||
|
||||
@@ -347,6 +347,7 @@ const upHeadListOperationStatus : TableUpperProps[] = [
|
||||
{ id: "groundWaterSummer", title: "하절기\n평균", upName:"upChangeRe5", upSequnce:2 },
|
||||
{ id: "groundWaterWinter", title: "동절기\n평균", upName:"upChangeRe5", upSequnce:3 },
|
||||
{ id: "rdi", title: "RDII 추정" },
|
||||
{ id: "rdiRain", title: "RDII 추정시\n일강수량(mm)" },
|
||||
]
|
||||
|
||||
const upHeadListRainManage : TableUpperProps[] = [
|
||||
@@ -688,12 +689,12 @@ const drainageEquipmentList : DrainageEquipmentListProps[] = [
|
||||
/* 시설정보 및 운영현황 - 계획 */
|
||||
const fetchFacilityPlan = async (targetYear : string | undefined, fcltyCd : string | undefined) => {
|
||||
try {
|
||||
targetYear = targetYear ? targetYear : "2024";
|
||||
targetYear = targetYear ? targetYear : "2023";
|
||||
const baseUrl = 'https://apigateway.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy';
|
||||
const queryParams = new URLSearchParams();
|
||||
|
||||
if (fcltyCd) queryParams.append('swtrmtFcltCd', fcltyCd);
|
||||
if (targetYear) queryParams.append('targetYear',targetYear);
|
||||
//if (targetYear) queryParams.append('targetYear',targetYear);
|
||||
const queryString = queryParams.toString();
|
||||
const url = queryString ? `${baseUrl}?${queryString}` : baseUrl;
|
||||
const res = await getFetchAreaList(url);
|
||||
@@ -707,7 +708,7 @@ const fetchFacilityPlan = async (targetYear : string | undefined, fcltyCd : stri
|
||||
/* 시설정보 및 운영현황 - 운영실태 */
|
||||
const fetchOperationStatus = async (searchYear: string|undefined, fcltyCd: string|undefined) => {
|
||||
try{
|
||||
const url = '/dashBoard/mngInfo/' + (searchYear?searchYear:'2024') + '/' + (fcltyCd?fcltyCd:'');
|
||||
const url = '/dashBoard/mngInfo/' + (searchYear?searchYear:'2023') + '/' + (fcltyCd?fcltyCd:'');
|
||||
const res = await getFetch('fo', url);
|
||||
return { data: res.data, count: res.count }
|
||||
} catch (error) {
|
||||
@@ -719,7 +720,7 @@ const fetchOperationStatus = async (searchYear: string|undefined, fcltyCd: strin
|
||||
// 주요그래프 데이터
|
||||
const fetchMainGraph = async (searchYear: string|undefined, fcltyCd: string|undefined) => {
|
||||
try {
|
||||
const url = '/fclt/qlty/hist/' + (searchYear?searchYear:'2024') + '/' + (fcltyCd?fcltyCd:'11680PB001R');
|
||||
const url = '/fclt/qlty/hist/' + (searchYear?searchYear:'2023') + '/' + (fcltyCd?fcltyCd:'11680PB001R');
|
||||
const res = await getFetch('fo', url);
|
||||
return { data: res.data, count: res.count }
|
||||
} catch (error) {
|
||||
@@ -954,41 +955,51 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
/** 시설정보 및 운영현황 - 계획정보 */
|
||||
searchPlanInfo : async (targetYear: string, fcltyCd: string) => {
|
||||
const res = await fetchFacilityPlan(targetYear, fcltyCd);
|
||||
let minDiff=9999999;
|
||||
let minIndex;
|
||||
res.forEach((el:any, idx) => {
|
||||
if(minDiff>Math.abs(Number(targetYear) - Number(el.yr))){
|
||||
minDiff=Math.abs(Number(targetYear) - Number(el.yr));
|
||||
minIndex=idx;
|
||||
}
|
||||
});
|
||||
|
||||
const rtnPlanData = res[minIndex];
|
||||
|
||||
planFcltyList.map((el, index) => {
|
||||
planYearList = [];
|
||||
planYearList.push(res[0]?res[0].yrDt4.toString():"");
|
||||
planYearList.push(res[0]?res[0].yrDc1.toString():"");
|
||||
planYearList.push(res[0]?res[0].yrDc2.toString():"");
|
||||
planYearList.push(res[0]?res[0].yrDc3.toString():"");
|
||||
planYearList.push(res[0]?res[0].yrDc4.toString():"");
|
||||
planYearList.push(rtnPlanData?rtnPlanData.yrDt4.toString():"");
|
||||
planYearList.push(rtnPlanData?rtnPlanData.yrDc1.toString():"");
|
||||
planYearList.push(rtnPlanData?rtnPlanData.yrDc2.toString():"");
|
||||
planYearList.push(rtnPlanData?rtnPlanData.yrDc3.toString():"");
|
||||
planYearList.push(rtnPlanData?rtnPlanData.yrDc4.toString():"");
|
||||
|
||||
if(index === 0){
|
||||
el.provisionalApproval = res&&res.length>0?res[0].planSwageQntSumDt4:'';
|
||||
el.now = res&&res.length>0?res[0].planSwageQntSum:'';
|
||||
el.stepOne = res&&res.length>0?res[0].planSwageQntSumDc1:'';
|
||||
el.stepTwo = res&&res.length>0?res[0].planSwageQntSumDc2:'';
|
||||
el.stepThree = res&&res.length>0?res[0].planSwageQntSumDc3:'';
|
||||
el.stepFour = res&&res.length>0?res[0].planSwageQntSumDc4:'';
|
||||
el.provisionalApproval = rtnPlanData?rtnPlanData.planSwageQntSumDt4:'';
|
||||
el.now = rtnPlanData?rtnPlanData.planSwageQntSum:'';
|
||||
el.stepOne = rtnPlanData?rtnPlanData.planSwageQntSumDc1:'';
|
||||
el.stepTwo = rtnPlanData?rtnPlanData.planSwageQntSumDc2:'';
|
||||
el.stepThree = rtnPlanData?rtnPlanData.planSwageQntSumDc3:'';
|
||||
el.stepFour = rtnPlanData?rtnPlanData.planSwageQntSumDc4:'';
|
||||
}
|
||||
if(index === 1){
|
||||
el.provisionalApproval = res&&res.length>0?res[0].swtrmtPpltnCntDt4:'';
|
||||
el.now = res&&res.length>0?res[0].swtrmtPpltnCnt:'';
|
||||
el.stepOne = res&&res.length>0?res[0].swtrmtPpltnCntDc1:'';
|
||||
el.stepTwo = res&&res.length>0?res[0].swtrmtPpltnCntDc2:'';
|
||||
el.stepThree = res&&res.length>0?res[0].swtrmtPpltnCntDc3:'';
|
||||
el.stepFour = res&&res.length>0?res[0].swtrmtPpltnCntDc4:'';
|
||||
el.provisionalApproval = rtnPlanData?rtnPlanData.swtrmtPpltnCntDt4:'';
|
||||
el.now = rtnPlanData?rtnPlanData.swtrmtPpltnCnt:'';
|
||||
el.stepOne = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc1:'';
|
||||
el.stepTwo = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc2:'';
|
||||
el.stepThree = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc3:'';
|
||||
el.stepFour = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc4:'';
|
||||
}
|
||||
if(index === 2){
|
||||
el.provisionalApproval = res&&res.length>0?res[0].swtrmtZoneAreaDt4:'';
|
||||
el.now = res&&res.length>0?res[0].swtrmtZoneArea:'';
|
||||
el.stepOne = res&&res.length>0?res[0].swtrmtZoneAreaDc1:'';
|
||||
el.stepTwo = res&&res.length>0?res[0].swtrmtZoneAreaDc2:'';
|
||||
el.stepThree = res&&res.length>0?res[0].swtrmtZoneAreaDc3:'';
|
||||
el.stepFour = res&&res.length>0?res[0].swtrmtZoneAreaDc4:'';
|
||||
el.provisionalApproval = rtnPlanData?rtnPlanData.swtrmtZoneAreaDt4:'';
|
||||
el.now = rtnPlanData?rtnPlanData.swtrmtZoneArea:'';
|
||||
el.stepOne = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc1:'';
|
||||
el.stepTwo = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc2:'';
|
||||
el.stepThree = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc3:'';
|
||||
el.stepFour = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc4:'';
|
||||
}
|
||||
if(index === 4){
|
||||
el.provisionalApproval = res&&res.length>0?res[0].ugwtUseSum:'';
|
||||
el.provisionalApproval = rtnPlanData?rtnPlanData.ugwtUseSum:'';
|
||||
}
|
||||
|
||||
// 임시시
|
||||
@@ -1040,9 +1051,10 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
operationalStatusList[0].winterSunnyAvg = data.winterSunny === null ? '' : (data.winterSunny.AVG_INFL_AMNT ? data.winterSunny.AVG_INFL_AMNT : 0) + (data.winterSunny.AVG_SUM_FLOW ? data.winterSunny.AVG_SUM_FLOW : 0) + (data.winterSunny.AVG_TP_INFL_WQLT_TP ? data.winterSunny.AVG_TP_INFL_WQLT_TP : 0);
|
||||
operationalStatusList[0].winterSunnyMax = data.winterSunny === null ? '' : (data.winterSunny.MAX_INFL_AMNT ? data.winterSunny.MAX_INFL_AMNT : 0) + (data.winterSunny.MAX_SUM_FLOW ? data.winterSunny.MAX_SUM_FLOW : 0) + (data.winterSunny.MAX_TP_INFL_WQLT_TP ? data.winterSunny.MAX_TP_INFL_WQLT_TP : 0);
|
||||
// 지하수
|
||||
operationalStatusList[0].groundWaterAvg = 10;
|
||||
operationalStatusList[0].groundWaterSummer = 11;
|
||||
operationalStatusList[0].groundWaterWinter = 12;
|
||||
operationalStatusList[0].groundWaterAvg = data.yearData.AVG_INFL_AMNT === null ? '' : Number(data.yearData.AVG_INFL_AMNT) - (Number(data.yearData.AVG_INFL_AMNT)*0.65*0.8);
|
||||
operationalStatusList[0].groundWaterSummer = data.summer.AVG_INFL_AMNT === null ? '' : Number(data.summer.AVG_INFL_AMNT) - (Number(data.summer.AVG_INFL_AMNT)*0.65*0.8);
|
||||
operationalStatusList[0].groundWaterWinter = data.winter.AVG_INFL_AMNT === null ? '' : Number(data.winter.AVG_INFL_AMNT) - (Number(data.winter.AVG_INFL_AMNT)*0.65*0.8);
|
||||
|
||||
// 오염물질
|
||||
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
|
||||
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
|
||||
@@ -1053,7 +1065,20 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
operationalStatusList[0].rdiRain = summerLowRain;
|
||||
dataFormSetting(operationalStatusList[0]);
|
||||
}
|
||||
|
||||
|
||||
if(operationalStatusList[0] && operationalStatusList[7]){
|
||||
console.log(Number(operationalStatusList[0].groundWaterAvg)+' '+Number(data.yearData.AVG_INFL_AMNT));
|
||||
console.log(Number(operationalStatusList[0].groundWaterSummer)+' '+Number(data.summer.AVG_INFL_AMNT));
|
||||
console.log(Number(operationalStatusList[0].groundWaterWinter)+' '+Number(data.winter.AVG_INFL_AMNT));
|
||||
operationalStatusList[7].groundWaterAvg = Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT) * 100;
|
||||
operationalStatusList[7].groundWaterSummer = Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT) * 100;
|
||||
operationalStatusList[7].groundWaterWinter = Number(operationalStatusList[0].groundWaterWinter) / Number(data.winter.AVG_INFL_AMNT) * 100;
|
||||
/* operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : Number(Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT)) * 100;
|
||||
operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : Number(Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT)) * 100;
|
||||
operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : Number(Number(operationalStatusList[0].groundWaterWinter) / Number(data.winter.AVG_INFL_AMNT)) * 100;*/
|
||||
dataFormSetting(operationalStatusList[0]);
|
||||
}
|
||||
|
||||
for(let i=1; i<operationalStatusList.length; i++){
|
||||
const txtArray = ['1', 'INFL_AMNT', 'SUM_FLOW', 'RETURN_AMNT', 'EFFL_AMNT', 'INFL_ASSCT_BFR_WQLT_BOD', 'EFFL_WQLT_BOD', 'INFL_ASSCT_BFR_WQLT_TN', 'EFFL_WQLT_TN', 'INFL_ASSCT_BFR_WQLT_TP', 'EFFL_WQLT_TP', 'INFL_ASSCT_BFR_WQLT_TP', 'EFFL_WQLT_TP'];
|
||||
if(operationalStatusList[i]){
|
||||
@@ -1207,42 +1232,42 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
}
|
||||
const fcltyCpcValue = res.data.wqltObjs[0].fctCpc;
|
||||
|
||||
const itemList = ['계', '3mm 미만', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50mm~60mm 미만', '60mm 이상'];
|
||||
const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0', '0'];
|
||||
const rainDate = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTn: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTp: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTocCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltToc = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const itemList = ['계', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50mm~60mm 미만', '60mm 이상'];
|
||||
const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0'];
|
||||
const rainDate = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTn: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTp: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTocCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltToc = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSs = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTn = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTp = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSs = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTn = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTp = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
// Process the data and create rain management list
|
||||
res.data.wqltObjs.forEach((item: RainManageListProps) => {
|
||||
|
||||
// 강우일
|
||||
if(Number(item.infoRnDay) < 3){
|
||||
/*if(Number(item.infoRnDay) < 3){
|
||||
rainDate[1] = Number(rainDate[1] || 0) + 1;
|
||||
inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1;
|
||||
inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1267,7 +1292,33 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1;
|
||||
efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){
|
||||
}*/
|
||||
if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){
|
||||
rainDate[1] = Number(rainDate[1] || 0) + 1;
|
||||
inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1;
|
||||
inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[1] = 0;
|
||||
inflAssctAftWqltBodCnt[1] = Number(inflAssctAftWqltBodCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltBod[1] = Number(inflAssctAftWqltBod[1] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[1] = Number(inflAssctAftWqltTocCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltToc[1] = Number(inflAssctAftWqltToc[1] || 0) + Number(item.inflAssctAftWqltToc || 0);
|
||||
inflAssctAftWqltSsCnt[1] = Number(inflAssctAftWqltSsCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltSs[1] = Number(inflAssctAftWqltSs[1] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[1] = Number(inflAssctAftWqltTnCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltTn[1] = Number(inflAssctAftWqltTn[1] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[1] = Number(inflAssctAftWqltTpCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltTp[1] = Number(inflAssctAftWqltTp[1] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[1] = Number(efflWqltBodCnt[1] || 0) + 1;
|
||||
efflWqltBod[1] = Number(efflWqltBod[1] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[1] = Number(efflWqltCodCnt[1] || 0) + 1;
|
||||
efflWqltCod[1] = Number(efflWqltCod[1] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[1] = Number(efflWqltSsCnt[1] || 0) + 1;
|
||||
efflWqltSs[1] = Number(efflWqltSs[1] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[1] = Number(efflWqltTnCnt[1] || 0) + 1;
|
||||
efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1;
|
||||
efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 10 && Number(item.infoRnDay) < 20){
|
||||
rainDate[2] = Number(rainDate[2] || 0) + 1;
|
||||
inflAmtCnt[2] = Number(inflAmtCnt[2] || 0) + 1;
|
||||
inflAmnt[2] = Number(inflAmnt[2] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1283,7 +1334,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
inflAssctAftWqltTpCnt[2] = Number(inflAssctAftWqltTpCnt[2] || 0) + 1;
|
||||
inflAssctAftWqltTp[2] = Number(inflAssctAftWqltTp[2] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[2] = Number(efflWqltBodCnt[2] || 0) + 1;
|
||||
efflWqltBod[2] = Number(efflWqltBod[2] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltBod[2] = Number(efflWqltBod[2] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[2] = Number(efflWqltCodCnt[2] || 0) + 1;
|
||||
efflWqltCod[2] = Number(efflWqltCod[2] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[2] = Number(efflWqltSsCnt[2] || 0) + 1;
|
||||
@@ -1292,7 +1343,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[2] = Number(efflWqltTn[2] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[2] = Number(efflWqltTpCnt[2] || 0) + 1;
|
||||
efflWqltTp[2] = Number(efflWqltTp[2] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 10 && Number(item.infoRnDay) < 20){
|
||||
}else if(Number(item.infoRnDay) >= 20 && Number(item.infoRnDay) < 30){
|
||||
rainDate[3] = Number(rainDate[3] || 0) + 1;
|
||||
inflAmtCnt[3] = Number(inflAmtCnt[3] || 0) + 1;
|
||||
inflAmnt[3] = Number(inflAmnt[3] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1317,11 +1368,11 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[3] = Number(efflWqltTn[3] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[3] = Number(efflWqltTpCnt[3] || 0) + 1;
|
||||
efflWqltTp[3] = Number(efflWqltTp[3] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 20 && Number(item.infoRnDay) < 30){
|
||||
}else if(Number(item.infoRnDay) >= 30 && Number(item.infoRnDay) < 40){
|
||||
rainDate[4] = Number(rainDate[4] || 0) + 1;
|
||||
inflAmtCnt[4] = Number(inflAmtCnt[4] || 0) + 1;
|
||||
inflAmnt[4] = Number(inflAmnt[4] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[4] = 0;
|
||||
fcltyCpct[4] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0;
|
||||
inflAssctAftWqltBodCnt[4] = Number(inflAssctAftWqltBodCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltBod[4] = Number(inflAssctAftWqltBod[4] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[4] = Number(inflAssctAftWqltTocCnt[4] || 0) + 1;
|
||||
@@ -1329,7 +1380,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
inflAssctAftWqltSsCnt[4] = Number(inflAssctAftWqltSsCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltSs[4] = Number(inflAssctAftWqltSs[4] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[4] = Number(inflAssctAftWqltTnCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltTn[4] = Number(inflAssctAftWqltTn[4] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTn[4] = Number(inflAssctAftWqltTn[5] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[4] = Number(inflAssctAftWqltTpCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltTp[4] = Number(inflAssctAftWqltTp[4] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[4] = Number(efflWqltBodCnt[4] || 0) + 1;
|
||||
@@ -1338,15 +1389,15 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltCod[4] = Number(efflWqltCod[4] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[4] = Number(efflWqltSsCnt[4] || 0) + 1;
|
||||
efflWqltSs[4] = Number(efflWqltSs[4] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[4] = Number(efflWqltTnCnt[4] || 0) + 1;
|
||||
efflWqltTnCnt[4] = Number(efflWqltTnCnt[5] || 0) + 1;
|
||||
efflWqltTn[4] = Number(efflWqltTn[4] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[4] = Number(efflWqltTpCnt[4] || 0) + 1;
|
||||
efflWqltTpCnt[4] = Number(efflWqltTpCnt[5] || 0) + 1;
|
||||
efflWqltTp[4] = Number(efflWqltTp[4] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 30 && Number(item.infoRnDay) < 40){
|
||||
}else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){
|
||||
rainDate[5] = Number(rainDate[5] || 0) + 1;
|
||||
inflAmtCnt[5] = Number(inflAmtCnt[5] || 0) + 1;
|
||||
inflAmnt[5] = Number(inflAmnt[5] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[5] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0;
|
||||
fcltyCpct[5] = 0;
|
||||
inflAssctAftWqltBodCnt[5] = Number(inflAssctAftWqltBodCnt[5] || 0) + 1;
|
||||
inflAssctAftWqltBod[5] = Number(inflAssctAftWqltBod[5] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[5] = Number(inflAssctAftWqltTocCnt[5] || 0) + 1;
|
||||
@@ -1367,7 +1418,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[5] = Number(efflWqltTn[5] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[5] = Number(efflWqltTpCnt[5] || 0) + 1;
|
||||
efflWqltTp[5] = Number(efflWqltTp[5] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){
|
||||
}else if(Number(item.infoRnDay) >= 50 && Number(item.infoRnDay) < 60){
|
||||
rainDate[6] = Number(rainDate[6] || 0) + 1;
|
||||
inflAmtCnt[6] = Number(inflAmtCnt[6] || 0) + 1;
|
||||
inflAmnt[6] = Number(inflAmnt[6] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1392,7 +1443,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[6] = Number(efflWqltTn[6] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[6] = Number(efflWqltTpCnt[6] || 0) + 1;
|
||||
efflWqltTp[6] = Number(efflWqltTp[6] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 50 && Number(item.infoRnDay) < 60){
|
||||
}else if(Number(item.infoRnDay) >= 60){
|
||||
rainDate[7] = Number(rainDate[7] || 0) + 1;
|
||||
inflAmtCnt[7] = Number(inflAmtCnt[7] || 0) + 1;
|
||||
inflAmnt[7] = Number(inflAmnt[7] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1417,31 +1468,6 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[7] = Number(efflWqltTn[7] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[7] = Number(efflWqltTpCnt[7] || 0) + 1;
|
||||
efflWqltTp[7] = Number(efflWqltTp[7] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 60){
|
||||
rainDate[8] = Number(rainDate[8] || 0) + 1;
|
||||
inflAmtCnt[8] = Number(inflAmtCnt[8] || 0) + 1;
|
||||
inflAmnt[8] = Number(inflAmnt[8] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[8] = 0;
|
||||
inflAssctAftWqltBodCnt[8] = Number(inflAssctAftWqltBodCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltBod[8] = Number(inflAssctAftWqltBod[8] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[8] = Number(inflAssctAftWqltTocCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltToc[8] = Number(inflAssctAftWqltToc[8] || 0) + Number(item.inflAssctAftWqltToc || 0);
|
||||
inflAssctAftWqltSsCnt[8] = Number(inflAssctAftWqltSsCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltSs[8] = Number(inflAssctAftWqltSs[8] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[8] = Number(inflAssctAftWqltTnCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltTn[8] = Number(inflAssctAftWqltTn[8] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[8] = Number(inflAssctAftWqltTpCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltTp[8] = Number(inflAssctAftWqltTp[8] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[8] = Number(efflWqltBodCnt[8] || 0) + 1;
|
||||
efflWqltBod[8] = Number(efflWqltBod[8] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[8] = Number(efflWqltCodCnt[8] || 0) + 1;
|
||||
efflWqltCod[8] = Number(efflWqltCod[8] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[8] = Number(efflWqltSsCnt[8] || 0) + 1;
|
||||
efflWqltSs[8] = Number(efflWqltSs[8] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[8] = Number(efflWqltTnCnt[8] || 0) + 1;
|
||||
efflWqltTn[8] = Number(efflWqltTn[8] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[8] = Number(efflWqltTpCnt[8] || 0) + 1;
|
||||
efflWqltTp[8] = Number(efflWqltTp[8] || 0) + Number(item.efflWqltTp || 0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1488,7 +1514,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
|
||||
rainDate[0] = (Number(rainDate[1]) || 0) + (Number(rainDate[2]) || 0) + (Number(rainDate[3]) || 0) +
|
||||
(Number(rainDate[4]) || 0) + (Number(rainDate[5]) || 0) + (Number(rainDate[6]) || 0) +
|
||||
(Number(rainDate[7]) || 0) + (Number(rainDate[8]) || 0);
|
||||
(Number(rainDate[7]) || 0);
|
||||
|
||||
// 강수비율
|
||||
for(let idx=0; idx<rainDate.length; idx++){
|
||||
@@ -1532,16 +1558,16 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
|
||||
inflAmnt[0] = calculateAverage(calculateTotal(inflAmnt), dataIsCount);
|
||||
fcltyCpct[0] = calculateTotal(fcltyCpct);
|
||||
inflAssctAftWqltBod[0] = Number((calculateTotal(inflAssctAftWqltBod)/8).toFixed(1));
|
||||
inflAssctAftWqltToc[0] = Number((calculateTotal(inflAssctAftWqltToc)/8).toFixed(1));
|
||||
inflAssctAftWqltSs[0] = Number((calculateTotal(inflAssctAftWqltSs)/8).toFixed(1));
|
||||
inflAssctAftWqltTn[0] = Number((calculateTotal(inflAssctAftWqltTn)/8).toFixed(3));
|
||||
inflAssctAftWqltTp[0] = Number((calculateTotal(inflAssctAftWqltTp)/8).toFixed(3));
|
||||
efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/8).toFixed(1));
|
||||
efflWqltCod[0] = Number((calculateTotal(efflWqltCod)/8).toFixed(1));
|
||||
efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/8).toFixed(1));
|
||||
efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/8).toFixed(3));
|
||||
efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/8).toFixed(3));
|
||||
inflAssctAftWqltBod[0] = Number((calculateTotal(inflAssctAftWqltBod)/7).toFixed(1));
|
||||
inflAssctAftWqltToc[0] = Number((calculateTotal(inflAssctAftWqltToc)/7).toFixed(1));
|
||||
inflAssctAftWqltSs[0] = Number((calculateTotal(inflAssctAftWqltSs)/7).toFixed(1));
|
||||
inflAssctAftWqltTn[0] = Number((calculateTotal(inflAssctAftWqltTn)/7).toFixed(3));
|
||||
inflAssctAftWqltTp[0] = Number((calculateTotal(inflAssctAftWqltTp)/7).toFixed(3));
|
||||
efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/7).toFixed(1));
|
||||
efflWqltCod[0] = Number((calculateTotal(efflWqltCod)/7).toFixed(1));
|
||||
efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/7).toFixed(1));
|
||||
efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/7).toFixed(3));
|
||||
efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/7).toFixed(3));
|
||||
|
||||
// 최종 리스트
|
||||
const filteredData = itemList.map((item, index) => ({
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface FacilityWaterQltyProps {
|
||||
inflAssctAftWqltTp: string | '',
|
||||
inflAssctBfrWqltTp: string | '',
|
||||
efflWqltBod: string | '',
|
||||
efflWqltCod: string | '',
|
||||
efflWqltToc: string | '',
|
||||
efflWqltSs: string | '',
|
||||
efflWqltTn: string | '',
|
||||
efflWqltTp: string | '',
|
||||
@@ -104,7 +104,7 @@ export type modalExcelType = {
|
||||
"유출SS(mg/L)": string;
|
||||
"유출TN(mg/L)": string;
|
||||
"유출TP(mg/L)": string;
|
||||
"강우량(일)": string;
|
||||
/* "강우량(mm/일)": string; */
|
||||
}
|
||||
|
||||
/**====================================
|
||||
@@ -133,7 +133,7 @@ const upHeadList1: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1 },
|
||||
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1 },
|
||||
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 },
|
||||
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 4, align: "right", row: 1 },
|
||||
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 },
|
||||
@@ -157,7 +157,7 @@ const upHeadList2: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
|
||||
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
|
||||
@@ -185,7 +185,7 @@ const upHeadList3: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
|
||||
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
|
||||
@@ -203,11 +203,11 @@ const upHeadListModalYear: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
|
||||
{ id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" },
|
||||
{ id: "efflWqltCod", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
|
||||
{ id: "efflWqltToc", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
|
||||
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" },
|
||||
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
|
||||
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" },
|
||||
{ id: "infoRnDay", title: "강우량", align: "right" },
|
||||
/* { id: "infoRnDay", title: "강우량(mm/일)", align: "right" }, */
|
||||
];
|
||||
|
||||
// 처리시설 수질현황 헤더(모달-수질TMS)
|
||||
@@ -409,8 +409,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.inflAssctAftWqltBod){
|
||||
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
|
||||
@@ -427,8 +427,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -456,7 +456,7 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
유입TN: item.inflAssctAftWqltTn,
|
||||
유입TP: item.inflAssctAftWqltTp,
|
||||
유출BOD: item.efflWqltBod,
|
||||
유출TOC: item.efflWqltCod,
|
||||
유출TOC: item.efflWqltToc,
|
||||
유출SS: item.efflWqltSs,
|
||||
유출TN: item.efflWqltTn,
|
||||
유출TP: item.efflWqltTp,
|
||||
@@ -610,8 +610,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.inflAssctAftWqltBod){
|
||||
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
|
||||
@@ -628,8 +628,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
|
||||
if(item.tpInflWqltBod){
|
||||
|
||||
@@ -13,8 +13,8 @@ const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용
|
||||
|
||||
export type flowRateListProps = {
|
||||
bscFctCd : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
sido : string,
|
||||
gugun : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
@@ -50,13 +50,13 @@ export interface ExcelType {
|
||||
}
|
||||
|
||||
export type modalExcelType = {
|
||||
날짜: string,
|
||||
"날짜": string,
|
||||
"총유입량(m³/일)": number | '',
|
||||
"하수유입량(m³/일)": number | '',
|
||||
"연계처리량(m³/일)": number | '',
|
||||
"총인반류수량(m³/일)": number | '',
|
||||
"방류량(m³/일)": number | '',
|
||||
"강우량": number | string | '',
|
||||
/* "강우량(mm/일)": number | string | '', */
|
||||
}
|
||||
/**
|
||||
* (2) 모달
|
||||
@@ -93,8 +93,8 @@ export type FlowRateModalProps = {
|
||||
const upHeadList1:TableUpperProps[] = [
|
||||
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2},
|
||||
{ id: "A", title: "행정명", row: 0 ,colSpan: 2 },
|
||||
{ id: "sidoNm", title: "시도", upName:"upChangeRe", row:1 },
|
||||
{ id: "sggNm", title: "시군구", upName:"upChangeRe", row:1 },
|
||||
{ id: "sido", title: "시도", upName:"upChangeRe", row:1 },
|
||||
{ id: "gugun", title: "시군구", upName:"upChangeRe", row:1 },
|
||||
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
|
||||
{ id: "daddr", title: "주소", row: 0, rowSpan: 2},
|
||||
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
|
||||
@@ -131,8 +131,8 @@ const upHeadList3:TableUpperProps[] = [
|
||||
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", row:1 },
|
||||
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
|
||||
{ id: "B", title: "행정명", row: 0 ,colSpan: 2 },
|
||||
{ id: "ctpvNm", title: "시도", upName:"upChangeRe2", row:1 },
|
||||
{ id: "sggNm", title: "시군구", upName:"upChangeRe2", row:1 },
|
||||
{ id: "sido", title: "시도", upName:"upChangeRe2", row:1 },
|
||||
{ id: "gugun", title: "시군구", upName:"upChangeRe2", row:1 },
|
||||
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2 },
|
||||
{ id: "daddr", title: "주소", row: 0, rowSpan: 2 },
|
||||
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
|
||||
@@ -157,7 +157,7 @@ const upHeadListFlowRateModal:TableUpperProps[] = [
|
||||
{ id: "sumFlow", title: "연계처리량(m³/일)", align:"right"},
|
||||
{ id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"},
|
||||
{ id: "efflAmnt", title: "방류량(m³/일)", align:"right"},
|
||||
{ id: "infoRnDay", title: "강우량", align:"right"}
|
||||
/* { id: "infoRnDay", title: "강우량(mm/일)", align:"right"} */
|
||||
]
|
||||
|
||||
const modalFlowRateListYear:FlowRateModalProps[] = [
|
||||
@@ -268,11 +268,8 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
|
||||
const data = res.data;
|
||||
const excelDownList: ExcelType[] = [];
|
||||
|
||||
const rtnData = data.map((item: flowRateListProps) => {
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
|
||||
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0)
|
||||
dataFormSetting(item);
|
||||
return item;
|
||||
@@ -286,8 +283,8 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
단위유역: item.lgbsn,
|
||||
중권역: item.mdmbsn,
|
||||
환경청: item.rgnNm,
|
||||
시도: item.sidoNm,
|
||||
시군구: item.sggNm,
|
||||
시도: item.sido,
|
||||
시군구: item.gugun,
|
||||
시설이름: item.bscFctNm,
|
||||
주소: item.daddr,
|
||||
용량: item.fctCpc,
|
||||
@@ -300,7 +297,6 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
});
|
||||
}
|
||||
|
||||
console.log('excelDownList' , excelDownList);
|
||||
set((state: FlowRateSearchType) => ({
|
||||
...state,
|
||||
FlowRateList: pageSize<5000?{
|
||||
@@ -316,8 +312,14 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
searchFlowRateModalList: async (bscFctCd: string, searchYear: string) => {
|
||||
const res = await fetchFlowRateModalList(bscFctCd, searchYear);
|
||||
const data = res.data;
|
||||
|
||||
const tmpData:FlowRateModalProps[] = [];
|
||||
|
||||
data.forEach((item: FlowRateModalProps) => {
|
||||
if(item.infoRnDay && item.infoRnDay.toString().trim() === 'x') {
|
||||
item.infoRnDay = '';
|
||||
}
|
||||
});
|
||||
|
||||
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
|
||||
for(let idx = 0; idx < data.length; idx++) {
|
||||
const item = data[idx];
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import DataForm from "@/app/components/common/DataForm";
|
||||
import type { TableUpperProps } from "../dashboard";
|
||||
import { getFetch } from "@/app/components/api";
|
||||
export type { TableUpperProps };
|
||||
@@ -29,21 +28,14 @@ export type SewageOccurProps = {
|
||||
mdmbsn : string,
|
||||
msrDt : string,
|
||||
}
|
||||
|
||||
export type modalTableType = { msrDt: string,waterUsage: number,swageChangePercent: number,estimationSewage: number};
|
||||
export type SewageOccurDetailProps = {
|
||||
msrDt : string,
|
||||
waterUsage : number,
|
||||
swageChangePercent : number,
|
||||
estimationSewage : number,
|
||||
cnt : number,
|
||||
sum : number,
|
||||
yr:string,
|
||||
usege:number
|
||||
}
|
||||
|
||||
/**====================================
|
||||
* Store - data
|
||||
====================================*/
|
||||
|
||||
/**
|
||||
* (1) 메인페이지
|
||||
*/
|
||||
// 검색단
|
||||
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
|
||||
|
||||
@@ -120,159 +112,13 @@ const upHeadList3:TableUpperProps[] = [
|
||||
const upHeadListSewageOccurDetail:TableUpperProps[] = [
|
||||
{ id: "msrDt", title: "년월" },
|
||||
{ id: "waterUsage", title: "물사용량(m³/일)" },
|
||||
{ id: "swageChangePercent", title: "오수전환율" },
|
||||
{ id: "swageChangePercent", title: "오수전환율(%)" },
|
||||
{ id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2 },
|
||||
];
|
||||
|
||||
const sewageOccurList:SewageOccurProps[] = [
|
||||
]
|
||||
const sewageOccurList:SewageOccurProps[] = []
|
||||
|
||||
const tempSewageOccurList:SewageOccurDetailProps[] = [
|
||||
{
|
||||
msrDt: '2024-01',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 60,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-02',
|
||||
waterUsage: 106,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-03',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-04',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-05',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 10,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-06',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-07',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 580,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-08',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-09',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-10',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-11',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-12',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-01',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-02',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-03',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-04',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-05',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-06',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-07',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-08',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 0.8,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-09',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-10',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-11',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-12',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
]
|
||||
const tempSewageOccurList:modalTableType[] = []
|
||||
|
||||
/**====================================
|
||||
* Store - API
|
||||
@@ -280,11 +126,6 @@ const tempSewageOccurList:SewageOccurDetailProps[] = [
|
||||
|
||||
const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
|
||||
// 20250701임시시
|
||||
console.log("partCd", partCd);
|
||||
console.log("nameCd", nameCd);
|
||||
console.log("fcltCpct", fcltCpct);
|
||||
console.log("operationValue", operationValue);
|
||||
console.log("fcltyNm", fcltyNm);
|
||||
console.log("searchYear", searchYear);
|
||||
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
|
||||
const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
|
||||
@@ -304,6 +145,7 @@ const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear
|
||||
====================================*/
|
||||
export type SewageOccurActions = {
|
||||
searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
|
||||
searchSewageOccurDetailList: (bscFctCd: string, searchYear: string) => void;
|
||||
}
|
||||
|
||||
export type SewageOccurType = {
|
||||
@@ -325,7 +167,7 @@ export type SewageOccurType = {
|
||||
SewageOccurDetail : {
|
||||
isInit: boolean;
|
||||
upHeadListSewageOccurDetail: TableUpperProps[];
|
||||
gridListSewageOccurDetail: SewageOccurDetailProps[];
|
||||
gridListSewageOccurDetail: modalTableType[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,14 +194,6 @@ export const sewageOccurInitState: SewageOccurType = {
|
||||
}
|
||||
}
|
||||
|
||||
const dataFormSetting=(item : SewageOccurProps)=> {
|
||||
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
|
||||
DataForm({ type: 'float1', column: 'waterUsage', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'estimationSewage', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'sewageInputYear', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'sewageInputChungchun', item: item as unknown as Record<string, unknown>});
|
||||
}
|
||||
|
||||
export type SewageOccurStore = SewageOccurType & SewageOccurActions;
|
||||
|
||||
// Add type definitions for API responses
|
||||
@@ -375,9 +209,7 @@ type FlowResponse = {
|
||||
count: number;
|
||||
}
|
||||
|
||||
type SetStateFunction = (state: SewageOccurType | ((state: SewageOccurType) => SewageOccurType)) => void;
|
||||
|
||||
export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set: SetStateFunction) => {
|
||||
export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
|
||||
return {
|
||||
searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
|
||||
const res = await fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
|
||||
@@ -388,67 +220,31 @@ export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set:
|
||||
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
item.waterUsage = item.fctCpc * 0.6;
|
||||
item.swageChangePercent = 80;
|
||||
item.estimationSewage = item.waterUsage * (item.swageChangePercent/100);
|
||||
|
||||
|
||||
const bscFctCd = item.bscFctCd;
|
||||
const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear;
|
||||
const res2 = await getFetch('fo', url2) as FlowResponse;
|
||||
const sumFlow = res2.data.reduce((acc: number, curr: FlowDataItem) => Number(acc) + Number(curr.inflAmnt), 0);
|
||||
const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse;
|
||||
|
||||
let sumChungcunDay = 0;
|
||||
let chungcunSumFlow = 0;
|
||||
|
||||
let totWaterUsage = 0;
|
||||
res2.data.forEach((item: FlowDataItem) => {
|
||||
if(Number(item.infoRnDay) ===0){
|
||||
chungcunSumFlow += Number(item.inflAmnt);
|
||||
sumChungcunDay += 1;
|
||||
}else{
|
||||
console.log(item.bscFctCd +' day : ' +' '+item.msrDt);
|
||||
}
|
||||
totWaterUsage += Number(item.inflAmnt);
|
||||
});
|
||||
|
||||
item.sewageInputYear = sumFlow/res2.count;
|
||||
item.sewageInputChungchun = chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay);
|
||||
const avg = totWaterUsage /res2.count;
|
||||
item.waterUsage = parseFloat((avg * 0.65).toFixed(2));
|
||||
item.sewageInputYear = parseFloat(avg.toFixed(2));
|
||||
item.sewageInputChungchun = parseFloat((chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay)).toFixed(2));
|
||||
item.estimationSewage = parseFloat((item.waterUsage * (item.swageChangePercent/100)).toFixed(2));
|
||||
return item;
|
||||
}));
|
||||
|
||||
console.log('calData : ', calData);
|
||||
|
||||
const excelDownList: ExcelType[] = [];
|
||||
if(pageSize>5000){
|
||||
calData.forEach(item => {
|
||||
dataFormSetting(item)
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
/* item.ctpvNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
|
||||
excelDownList.push({
|
||||
시설코드: item.bscFctCd,
|
||||
권역: item.rvbsn,
|
||||
단위유역: item.lgbsn,
|
||||
중권역: item.mdmbsn,
|
||||
환경청: item.rgnNm,
|
||||
시도: item.sidoNm,
|
||||
시군구: item.sggNm,
|
||||
시설명: item.bscFctNm,
|
||||
주소: item.daddr,
|
||||
시설용량: item.fctCpc,
|
||||
물사용량: item.waterUsage,
|
||||
오수전환율: item.swageChangePercent,
|
||||
오수발생량추정: item.estimationSewage,
|
||||
연간하수유입량: item.sewageInputYear,
|
||||
청천시하수유입량: item.sewageInputChungchun,
|
||||
});
|
||||
});
|
||||
}else{
|
||||
calData.forEach((item: SewageOccurProps) => {
|
||||
dataFormSetting(item)
|
||||
/* item.sidoNmNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
|
||||
});
|
||||
}
|
||||
set((state: SewageOccurType) => ({
|
||||
// 조회 데이터 설정
|
||||
set((state: any) => ({
|
||||
...state,
|
||||
SewageOccur: pageSize<5000?{
|
||||
...state.SewageOccur,
|
||||
@@ -456,7 +252,81 @@ export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set:
|
||||
sewageOccurCount: res.count,
|
||||
}:{
|
||||
...state.SewageOccur,
|
||||
excelList: excelDownList,
|
||||
excelList: [],
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
searchSewageOccurDetailList: async (bscFctCd: string, searchYear: string) => {
|
||||
const res = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear);
|
||||
const monthData: SewageOccurDetailProps[] = [];
|
||||
let tot = 0, cnt= 0
|
||||
res.data.forEach((item: FlowDataItem) => {
|
||||
const month = item.msrDt?.substring(4, 6);
|
||||
const monthIdx = month ? Number(month) - 1 : -1;
|
||||
if (monthIdx >= 0) {
|
||||
if (typeof monthData[monthIdx] === 'object') {
|
||||
monthData[monthIdx].sum += Number(item.inflAmnt);
|
||||
monthData[monthIdx].cnt++;
|
||||
} else {
|
||||
monthData[monthIdx] = {cnt:1, sum:Number(item.inflAmnt), yr:item.msrDt.substring(0,4), usege:0};
|
||||
}
|
||||
}
|
||||
tot += Number(item.inflAmnt)
|
||||
cnt++
|
||||
})
|
||||
console.log('tot', tot, cnt, tot/cnt, tot/cnt*0.65);
|
||||
const tableData:modalTableType[] = []
|
||||
|
||||
let maxWaterUsage = 0;
|
||||
let minWaterUsage = 9999999999;
|
||||
let avgWaterUsage = 0;
|
||||
monthData.forEach((item, index) => {
|
||||
item.usege = item.sum/item.cnt *0.65
|
||||
tableData.push({
|
||||
msrDt: item.yr+'-'+(index+1>10?String(index+1):'0'+(index+1)),
|
||||
waterUsage: Math.round(item.usege*100) /100,
|
||||
swageChangePercent: (Math.round((item.usege/item.usege*0.8) * 100) / 100) * 100,
|
||||
estimationSewage: Math.round(item.usege*0.8 * 100) / 100
|
||||
})
|
||||
if (item.usege > maxWaterUsage) {
|
||||
maxWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
if (item.usege < minWaterUsage) {
|
||||
minWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
});
|
||||
avgWaterUsage = tot/cnt*0.65
|
||||
|
||||
const newData= [
|
||||
{
|
||||
msrDt: '평균',
|
||||
waterUsage: Math.round(avgWaterUsage*100) /100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: Math.round(avgWaterUsage*0.8*100) /100,
|
||||
},
|
||||
{
|
||||
msrDt: '최대',
|
||||
waterUsage: Math.round(maxWaterUsage*100) /100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: Math.round(maxWaterUsage*0.8*100) /100,
|
||||
},
|
||||
{
|
||||
msrDt: '최소',
|
||||
waterUsage: Math.round(minWaterUsage*100) /100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: Math.round(minWaterUsage*0.8*100) /100,
|
||||
},
|
||||
...tableData
|
||||
]
|
||||
|
||||
set((state: SewageOccurType) => ({
|
||||
...state,
|
||||
SewageOccurDetail: {
|
||||
...state.SewageOccurDetail,
|
||||
gridListSewageOccurDetail: newData,
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
+14
-12
@@ -1,13 +1,19 @@
|
||||
import { LoginApi } from '@/src/entities/auth/api/login-service';
|
||||
import { createStore } from 'zustand/vanilla';
|
||||
import { CommonReducer, commonState, type CommonStore, type CommonType } from './common';
|
||||
import { dashBoardReducer, dashBoardInitState, type DashBoardStore, type DashBoardType } from './dashboard';
|
||||
import { dashBoardInitState, dashBoardReducer, type DashBoardStore, type DashBoardType } from './dashboard';
|
||||
import {
|
||||
facilityPrecipitationInitState,
|
||||
facilityPrecipitationReducer,
|
||||
type FacilityPrecipitationStore, type FacilityPrecipitationType
|
||||
} from './facilityPrecipitation';
|
||||
import { planSewageInitState, planSewageReducer, type PlanSewageStore, type PlanSewageType } from './planSewage';
|
||||
import {
|
||||
facilityWaterQltyInitState,
|
||||
facilityWaterQltyReducer,
|
||||
type FacilityWaterQltyStore, type FacilityWaterQltyType
|
||||
} from './facilityWaterQlty';
|
||||
import { flowRateSearchInitState, flowRateSearchReducer, type FlowRateSearchStore, type FlowRateSearchType } from './flowRate';
|
||||
import { planSewageInitState, planSewageReducer, type PlanSewageStore, type PlanSewageType } from './planSewage';
|
||||
import {
|
||||
processAreaInitState,
|
||||
processAreaReducer,
|
||||
@@ -18,11 +24,12 @@ import {
|
||||
processFacilityReducer,
|
||||
type ProcessFacilityStore, type ProcessFacilityType
|
||||
} from './processFacility';
|
||||
import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';
|
||||
import {
|
||||
facilityWaterQltyInitState,
|
||||
facilityWaterQltyReducer,
|
||||
type FacilityWaterQltyStore, type FacilityWaterQltyType
|
||||
} from './facilityWaterQlty';
|
||||
rdiInitState,
|
||||
rdiReducer,
|
||||
type RdiStore, type RdiType
|
||||
} from './rdi';
|
||||
import {
|
||||
sewageOccurInitState,
|
||||
sewageOccurReducer,
|
||||
@@ -33,12 +40,6 @@ import {
|
||||
underWaterReducer,
|
||||
type UnderWaterStore, type UnderWaterType
|
||||
} from './underWaterInput';
|
||||
import {
|
||||
rdiInitState,
|
||||
rdiReducer,
|
||||
type RdiStore, type RdiType
|
||||
} from './rdi';
|
||||
import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';
|
||||
export type FoStore = CommonStore & tableType & DashBoardStore & ProcessFacilityStore & ProcessAreaStore & FlowRateSearchStore & FacilityPrecipitationStore & FacilityWaterQltyStore & PlanSewageStore & SewageOccurStore & UnderWaterStore & RdiStore;
|
||||
export type FoState = CommonType & tableState & ProcessFacilityType & ProcessAreaType & FacilityPrecipitationType & FlowRateSearchType & DashBoardType & FacilityWaterQltyType & PlanSewageType & SewageOccurType & UnderWaterType & RdiType;
|
||||
|
||||
@@ -58,6 +59,7 @@ export const StoreZus =
|
||||
}
|
||||
) => {
|
||||
return createStore<FoStore>()((set) => {
|
||||
LoginApi.login({userId: "admin1gmail", password: "test1234!"})
|
||||
return {
|
||||
...initState,
|
||||
...CommonReducer(set),
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import DataForm from "@/app/components/common/DataForm";
|
||||
import type { TableUpperProps } from "../dashboard";
|
||||
import { getFetch } from "@/app/components/api";
|
||||
import type { ExcelType, UnderWaterInputModalProps, UnderWaterProps } from "./types";
|
||||
import type { SewageOccurDetailProps } from "../sewageOccur";
|
||||
export type { TableUpperProps };
|
||||
/**====================================
|
||||
* Store - 타입
|
||||
@@ -10,47 +12,6 @@ export type { TableUpperProps };
|
||||
* (1) 메인페이지
|
||||
*/
|
||||
// 시설별 강수현황
|
||||
export type UnderWaterProps = {
|
||||
bscFctCd : string,
|
||||
lclgvrnmntNm : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
underWaterInputYear : number,
|
||||
underWaterInputSummer : number,
|
||||
underWaterInputWinter : number,
|
||||
infoRnDay : number,
|
||||
rgnNm : string,
|
||||
rvbsn : string,
|
||||
lgbsn : string,
|
||||
mdmbsn : string,
|
||||
}
|
||||
export interface UnderWaterInterface {
|
||||
bscFctCd : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
underWaterInputYear : number,
|
||||
underWaterInputSummer : number,
|
||||
underWaterInputWinter : number,
|
||||
infoRnDay : number,
|
||||
rgnNm : string,
|
||||
rvbsn : string,
|
||||
lgbsn : string,
|
||||
mdmbsn : string,
|
||||
}
|
||||
|
||||
// 모달
|
||||
export interface UnderWaterInputModalProps {
|
||||
msrDt : string,
|
||||
realSewageInput : number | '',
|
||||
sewageOccur : number | '',
|
||||
underWaterInput : number | '',
|
||||
}
|
||||
|
||||
/**====================================
|
||||
* Store - data
|
||||
@@ -62,22 +23,6 @@ export interface UnderWaterInputModalProps {
|
||||
// 검색단
|
||||
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
|
||||
|
||||
export interface ExcelType {
|
||||
시설코드: string,
|
||||
권역:string|'',
|
||||
단위유역:string|'',
|
||||
중권역:string|'',
|
||||
환경청:string|'',
|
||||
시도: string,
|
||||
시군구: string,
|
||||
시설명: string,
|
||||
주소: string,
|
||||
시설용량: number,
|
||||
연간지하수유입량: number | '',
|
||||
하절기지하수유입량: number | '',
|
||||
동절기지하수유입량: number | '',
|
||||
}
|
||||
|
||||
// 리스트 헤더
|
||||
const upHeadList1:TableUpperProps[] = [
|
||||
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
|
||||
@@ -247,6 +192,7 @@ const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear:
|
||||
====================================*/
|
||||
export type UnderWaterActions = {
|
||||
searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
|
||||
unserWaterModal:(bscFctCd:string, searchYear:string)=>void
|
||||
}
|
||||
|
||||
export type UnderWaterType = {
|
||||
@@ -315,8 +261,7 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
|
||||
const bscFctCd = item.bscFctCd;
|
||||
const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear;
|
||||
const res2 = await getFetch('fo', url2) as ApiResponse;
|
||||
const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as ApiResponse;
|
||||
|
||||
let sumChungcunDay = 0;
|
||||
let chungcunSumFlow = 0;
|
||||
@@ -393,5 +338,77 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy
|
||||
}
|
||||
}))
|
||||
},
|
||||
unserWaterModal:async (bscFctCd, searchYear)=>{
|
||||
const res = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear);
|
||||
const monthData: SewageOccurDetailProps[] = [];
|
||||
let tot = 0, cnt= 0
|
||||
res.data.forEach((item: FlowDataItem) => {
|
||||
const month = item.msrDt?.substring(4, 6);
|
||||
const monthIdx = month ? Number(month) - 1 : -1;
|
||||
if (monthIdx >= 0) {
|
||||
if (typeof monthData[monthIdx] === 'object') {
|
||||
monthData[monthIdx].sum += Number(item.inflAmnt);
|
||||
monthData[monthIdx].cnt++;
|
||||
} else {
|
||||
monthData[monthIdx] = {cnt:1, sum:Number(item.inflAmnt), yr:item.msrDt.substring(0,4), usege:0};
|
||||
}
|
||||
}
|
||||
tot += Number(item.inflAmnt)
|
||||
cnt++
|
||||
})
|
||||
const tableData:UnderWaterInputModalProps[] = []
|
||||
|
||||
let maxWaterUsage = 0;
|
||||
let minWaterUsage = 9999999999;
|
||||
let avgWaterUsage = 0;
|
||||
monthData.forEach((item, index) => {
|
||||
item.usege = item.sum/item.cnt
|
||||
tableData.push({
|
||||
msrDt: item.yr+'-'+(index+1>10?String(index+1):'0'+(index+1)),
|
||||
realSewageInput:Math.round(item.usege*100) /100,
|
||||
sewageOccur:Math.round(item.usege*0.65*0.8 * 100) / 100,
|
||||
underWaterInput:Math.round((item.usege-(item.usege*0.65*0.8)) * 100) / 100,
|
||||
})
|
||||
if (item.usege > maxWaterUsage) {
|
||||
maxWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
if (item.usege < minWaterUsage) {
|
||||
minWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
});
|
||||
avgWaterUsage = tot/cnt
|
||||
|
||||
const newData= [
|
||||
{
|
||||
msrDt: '평균',
|
||||
realSewageInput:Math.round(avgWaterUsage*100) /100,
|
||||
sewageOccur:Math.round(avgWaterUsage*0.65*0.8 * 100) / 100,
|
||||
underWaterInput:Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.8)) * 100) / 100,
|
||||
},
|
||||
{
|
||||
msrDt: '최대',
|
||||
realSewageInput: Math.round(maxWaterUsage*100) /100,
|
||||
sewageOccur: Math.round(maxWaterUsage*0.65*0.8 * 100) / 100,
|
||||
underWaterInput: Math.round((maxWaterUsage-(maxWaterUsage*0.65*0.8)) * 100) / 100,
|
||||
},
|
||||
{
|
||||
msrDt: '최소',
|
||||
realSewageInput: Math.round(minWaterUsage*100) /100,
|
||||
sewageOccur: Math.round(minWaterUsage*0.65*0.8 * 100) / 100,
|
||||
underWaterInput: Math.round((minWaterUsage-(minWaterUsage*0.65*0.8)) * 100) / 100,
|
||||
},
|
||||
...tableData
|
||||
]
|
||||
|
||||
set((state: UnderWaterType) => ({
|
||||
...state,
|
||||
UnderWaterDetail: {
|
||||
...state.UnderWaterDetail,
|
||||
gridListUnderWaterDetail:newData
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
export type UnderWaterProps = {
|
||||
bscFctCd : string,
|
||||
lclgvrnmntNm : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
underWaterInputYear : number,
|
||||
underWaterInputSummer : number,
|
||||
underWaterInputWinter : number,
|
||||
infoRnDay : number,
|
||||
rgnNm : string,
|
||||
rvbsn : string,
|
||||
lgbsn : string,
|
||||
mdmbsn : string,
|
||||
}
|
||||
export interface UnderWaterInterface {
|
||||
bscFctCd : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
underWaterInputYear : number,
|
||||
underWaterInputSummer : number,
|
||||
underWaterInputWinter : number,
|
||||
infoRnDay : number,
|
||||
rgnNm : string,
|
||||
rvbsn : string,
|
||||
lgbsn : string,
|
||||
mdmbsn : string,
|
||||
}
|
||||
|
||||
// 모달
|
||||
export interface UnderWaterInputModalProps {
|
||||
msrDt : string,
|
||||
realSewageInput : number | '',
|
||||
sewageOccur : number | '',
|
||||
underWaterInput : number | '',
|
||||
}
|
||||
|
||||
|
||||
export interface ExcelType {
|
||||
시설코드: string,
|
||||
권역:string|'',
|
||||
단위유역:string|'',
|
||||
중권역:string|'',
|
||||
환경청:string|'',
|
||||
시도: string,
|
||||
시군구: string,
|
||||
시설명: string,
|
||||
주소: string,
|
||||
시설용량: number,
|
||||
연간지하수유입량: number | '',
|
||||
하절기지하수유입량: number | '',
|
||||
동절기지하수유입량: number | '',
|
||||
}
|
||||
@@ -11,6 +11,10 @@ const compat = new FlatCompat({
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{rules:{
|
||||
// other rules..
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
}},
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
@@ -20,7 +20,7 @@ export class TokenManager {
|
||||
|
||||
public setToken(key: string, value: string): void {
|
||||
setCookie(key, value, {
|
||||
secure: window.location.protocol === "https:",
|
||||
secure: location.protocol === "https:",
|
||||
sameSite: "lax",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">구분</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">년도</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">진단연도</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">사업명</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">유형</TableHead>
|
||||
</TableRow>
|
||||
@@ -40,7 +40,7 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
|
||||
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto">
|
||||
{row.bizNm}
|
||||
</TableCell>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
|
||||
|
||||
@@ -22,7 +22,7 @@ type LayerContentMap = {
|
||||
// 레이어 이름과 컴포넌트를 매핑
|
||||
// 레이어 선택시 변경되는 팝업 컨텐츠 동적으로 변경하도록 작업
|
||||
const layerContentMap: LayerContentMap = {
|
||||
"하수처리시설": { component: SwtrmtFclt, },
|
||||
// "하수처리시설": { component: SwtrmtFclt, }, // 디지털 자료관 연계정보가 없으니 일단 주석 처리
|
||||
"하수처리구역": {},
|
||||
"우수토실": { component: Ssc, },
|
||||
"중점우수토실": { component: Ssc, }, // 중점우수토실 여부 설정
|
||||
|
||||
@@ -28,10 +28,11 @@ export const MsSwagePplCol = [
|
||||
// { id: 'lyrTypeNm', title: '레이어타입명' },
|
||||
]
|
||||
export const MsSwagePplCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'fcltMngNo', title: '시설 관리번호' },
|
||||
{ id: 'instMngNo', title: '기관 관리번호' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mapshtNo', title: '도엽번호' },
|
||||
{ id: 'pdmt', title: '관경(mm)' },
|
||||
{ id: 'prlg', title: '연장(m)' },
|
||||
{ id: 'avgGrdn', title: '평균구배(m/m)' },
|
||||
@@ -296,10 +297,11 @@ export const SwtrmtFcltCol = [
|
||||
// { id: 'obsrvnSptCd', title: '관측지점코드' },
|
||||
];
|
||||
export const SwtrmtFcltCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
{ id: 'fcltCpct', title: '시설용량(㎥/d)' },
|
||||
{ id: 'rgnExpln', title: '지역설명' },
|
||||
{ id: 'cmcnYmd', title: '준공일자' },
|
||||
// { id: 'cmcnYmd', title: '준공일자' },
|
||||
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
|
||||
// { id: 'clsgYmd', title: '폐쇄일자' },
|
||||
// { id: 'prcsMsrcCd', title: '처리공법코드' },
|
||||
@@ -319,7 +321,7 @@ export const SwtrmtFcltCol2 = [
|
||||
// { id: 'pllnArea', title: '부지면적(m²)' },
|
||||
// { id: 'mngRprtvBzentyNm', title: '관리대행업체명' },
|
||||
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수(인)' },
|
||||
{ id: 'prcsZoneAreaSum', title: '처리구역면적합계(m²)' },
|
||||
// { id: 'prcsZoneAreaSum', title: '처리구역면적합계(m²)' },
|
||||
// { id: 'xcrd', title: 'X좌표' },
|
||||
// { id: 'ycrd', title: 'Y좌표' },
|
||||
// { id: 'oprtngSttsCd', title: '가동상태코드' },
|
||||
@@ -346,6 +348,7 @@ export const SwtrmtPrcsZoneCol = [
|
||||
{ id: 'swtrmt_fclt_nm', title: '하수처리시설명' },
|
||||
];
|
||||
export const SwtrmtPrcsZoneCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
// { id: 'swtrmtZoneSn', title: '하수처리구역 일련번호' },
|
||||
{ id: 'swtrmtZoneNm', title: '하수처리구역명' },
|
||||
// { id: 'swtrmt_fclt_cd', title: '하수처리시설코드' },
|
||||
@@ -380,12 +383,13 @@ export const MsSwageMhlCol = [
|
||||
// { id: 'inst_mng_no', title: '기관관리번호' }
|
||||
];
|
||||
export const MsSwageMhlCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
// { id: 'fid', title: 'FID' },
|
||||
// { id: 'geom', title: 'geometry' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
{ id: 'fcltMngNo', title: '시설관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mngInstCd', title: '관리기관코드' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
// { id: 'lastDrdgnYmd', title: '최종준설일자' },
|
||||
@@ -487,12 +491,13 @@ export const ExclnSewagePumpCol = [
|
||||
// { id: 'instMngNo', title: '기관관리번호' },
|
||||
]
|
||||
export const ExclnSewagePumpCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
// { id: 'fid', title: 'FID' },
|
||||
// { id: 'geom', title: 'geometry' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
{ id: 'fcltMngNo', title: '시설관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mngInstCd', title: '관리기관코드' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
{ id: 'swagePmstnNm', title: '하수펌프장명' },
|
||||
@@ -528,6 +533,7 @@ export const DrtwSewagePumpCol = [
|
||||
// { id: 'instMngNo', title: '기관관리번호' },
|
||||
]
|
||||
export const DrtwSewagePumpCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
// { id: 'fid', title: 'FID' },
|
||||
// { id: 'geom', title: 'geometry' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
@@ -574,6 +580,7 @@ export const stofcbrCol = [
|
||||
{ id: 'drtn_angl', title: '방향각도(°)' },
|
||||
]
|
||||
export const stofcbrCol2 = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
{ id: 'fcltMngNo', title: '시설관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
@@ -650,7 +657,8 @@ export const dateFields = [
|
||||
// 🎯 camelCase 버전 추가
|
||||
'instlYmd', 'lastDrdgnYmd', 'cmcnYmd', 'oprtngStrtYmd', 'clsgYmd',
|
||||
'mesureBeginDt', 'mesureEndDt', 'qltwtrBeginDt', 'qltwtrEndDt',
|
||||
'rainBeginDt', 'rainEndDt'
|
||||
'rainBeginDt', 'rainEndDt',
|
||||
'flfmtBgngYmd', 'flfmtEndYmd', 'flfmtYr'
|
||||
];
|
||||
|
||||
// 🎯 연월 필드들 (yyyymm -> yyyy년 m월)
|
||||
@@ -677,5 +685,6 @@ export const numberFields = [
|
||||
'worflwWallHgt',
|
||||
'swrPplEqpAr', 'swrTrtPopl', 'weirHg', 'weirLt',
|
||||
'precdngDry', 'rainFall', 'cntncTime', 'rainIn',
|
||||
'mesureIntrvl', 'qltwtrCo', 'rnysnFvlc', 'clarFvlc'
|
||||
'mesureIntrvl', 'qltwtrCo', 'rnysnFvlc', 'clarFvlc',
|
||||
'bizAmt', 'count'
|
||||
];
|
||||
@@ -107,7 +107,7 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
|
||||
console.log('repairArr', repairArr);
|
||||
};
|
||||
fetchAll();
|
||||
}, []);
|
||||
}, [featureData]);
|
||||
|
||||
// 폼 데이터가 변경될 때 호출될 핸들러
|
||||
const handleFormDataChange = (newFormData: any) => {
|
||||
@@ -122,18 +122,6 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
|
||||
}
|
||||
};
|
||||
|
||||
// 변환 함수: 객체 → [{ year, type }]
|
||||
// function convertToYearTypeArray(obj: any): { year: string, type: string }[] {
|
||||
// if (!obj || typeof obj !== "object") return [];
|
||||
// // 이미 배열이면 그대로 반환
|
||||
// if (Array.isArray(obj)) return obj;
|
||||
// // 객체의 각 key를 type, value를 year로 변환
|
||||
// return Object.entries(obj).map(([type, year]) => ({
|
||||
// year: String(year ?? ""),
|
||||
// type: String(type ?? ""),
|
||||
// }));
|
||||
// }
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import { getData, getDcData } from '@/components/lib/api';
|
||||
import { getData } from '@/components/lib/api';
|
||||
import moment from "moment";
|
||||
|
||||
export type TInftwtAnls = {
|
||||
@@ -64,9 +64,19 @@ export type TOilqData = {
|
||||
mtrngDt: string; // 계측일시
|
||||
oilq: number; // 유량
|
||||
}
|
||||
export type TRainData = {
|
||||
rainInfwtAnlsSn: number;
|
||||
mtrngDt: string;
|
||||
amrf: number; // 강우량
|
||||
}
|
||||
|
||||
function roundTo(n: number, digits: number): number {
|
||||
const factor = Math.pow(10, digits);
|
||||
return Math.round(n * factor) / factor;
|
||||
}
|
||||
|
||||
const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string, hidden?: boolean }[] => {
|
||||
const yyyymms1 = yyyymms.map(dateStr => {
|
||||
const yyyymms1 : { text: string; val: string; hidden?: boolean }[] = yyyymms.map(dateStr => {
|
||||
const year = dateStr.slice(0, 4);
|
||||
const month = dateStr.slice(4, 6);
|
||||
return {
|
||||
@@ -104,7 +114,7 @@ export const useAplcnYms = (gubun:string) => {
|
||||
return data;
|
||||
};
|
||||
export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string; val: number, hidden?: boolean }[] => {
|
||||
const options = anlsArr.map(anls => ({
|
||||
const options : { text: string; val: number; hidden?: boolean }[] = anlsArr.map(anls => ({
|
||||
text: anls.anlsInfo,
|
||||
val: anls.inftwtAnlsSn,
|
||||
}));
|
||||
@@ -112,7 +122,7 @@ export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string
|
||||
return options;
|
||||
};
|
||||
|
||||
export const selectInftwtAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
export const selectInftwtAnls = async(aplcnYm:string) => { // 침입수분석
|
||||
try {
|
||||
const queryString = aplcnYm ? `aplcnYmFrom=${aplcnYm}&aplcnYmTo=${aplcnYm}` : '';
|
||||
const result = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
@@ -127,7 +137,7 @@ export const selectInftwtAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
export const getChartDataInftwtAnlsRslt = (arr: TInftwtAnlsRslt[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("HH시"),
|
||||
val: anlsRslt.predcOilq,
|
||||
val: roundTo(anlsRslt.predcOilq, 2),
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
@@ -149,7 +159,7 @@ export const selectInftwtAnlsRslt = async(inftwtAnlsSn:number, pplMngNo:string)
|
||||
export const getChartDataOilq = (arr: TOilqData[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: anlsRslt.mtrngDt,
|
||||
val: anlsRslt.oilq,
|
||||
val: roundTo(anlsRslt.oilq, 2),
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
@@ -203,7 +213,7 @@ export const selectPplRdiiQty = async(rainInfwtAnlsSn:number, pplMngNo:string) =
|
||||
};
|
||||
|
||||
export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; val: number, hidden?: boolean }[] => {
|
||||
const options = anlsArr.map(anls => ({
|
||||
const options : { text: string; val: number; hidden?: boolean }[] = anlsArr.map(anls => ({
|
||||
text: anls.anlsInfo,
|
||||
val: anls.rainInfwtAnlsSn,
|
||||
}));
|
||||
@@ -211,7 +221,7 @@ export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; va
|
||||
return options;
|
||||
};
|
||||
|
||||
export const selectRdiiAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
export const selectRdiiAnls = async(aplcnYm:string) => { // 침입수분석
|
||||
try {
|
||||
const queryString = aplcnYm ? `aplcnYmFrom=${aplcnYm}&aplcnYmTo=${aplcnYm}` : '';
|
||||
const result = await getData(`v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
@@ -223,21 +233,22 @@ export const selectRdiiAnls = async(aplcnYm:String) => { // 침입수분석
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
export const getChartDataRdiiAnlsRslt = (arr: TRdiiAnlsRslt[]): { label: string; val: number }[] => {
|
||||
export const getChartDataRdiiAnlsRslt = (arr: TRdiiAnlsRslt[]): { label: string; lineVal0: number, barVal0:number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("MM-DD HH시"),
|
||||
val: anlsRslt.predcOilq,
|
||||
lineVal0: roundTo(anlsRslt.predcOilq, 2),
|
||||
barVal0: 0
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
export const getChartDataRdiiAnlsRsltMinq = (arr: TRdiiAnlsRslt[]): { label: string; val: number }[] => {
|
||||
const options = arr.map(anlsRslt => ({
|
||||
label: moment(anlsRslt.anlsTm, "YYYYMMDDHHmmss").format("MM-DD HH시"),
|
||||
val: anlsRslt.minq,
|
||||
val: roundTo(anlsRslt.minq, 2),
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
export const selectRdiiAnlsRslt = async(rainInfwtAnlsSn:number, pplMngNo:string) => { // 침입수분석 결과
|
||||
export const selectRdiiAnlsRslt = async(rainInfwtAnlsSn:number, pplMngNo:string) => { // RDII분석 결과
|
||||
try {
|
||||
if ((rainInfwtAnlsSn === undefined || rainInfwtAnlsSn === 0) || !pplMngNo) {
|
||||
console.warn("RdiiAnlsSn or pplMngNo is not provided");
|
||||
@@ -245,10 +256,32 @@ export const selectRdiiAnlsRslt = async(rainInfwtAnlsSn:number, pplMngNo:string)
|
||||
}
|
||||
const result = await getData(`v1/rdiiAnlsRslt/ppl/${rainInfwtAnlsSn}/${pplMngNo}`);
|
||||
if (result && result.numberOfElements > 0) {
|
||||
console.log("selectRdiiAnlsRslt", result.content);
|
||||
return result.content;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
export const selectRainData = async(rainInfwtAnlsSn:number) => { // 강우데이터
|
||||
try {
|
||||
if ((rainInfwtAnlsSn === undefined || rainInfwtAnlsSn === 0)) {
|
||||
console.warn("RdiiAnlsSn or pplMngNo is not provided");
|
||||
return [];
|
||||
}
|
||||
const result = await getData(`v1/rdiiAnlsRslt/rainData/${rainInfwtAnlsSn}`);
|
||||
if (result && result.numberOfElements > 0) {
|
||||
console.log("selectRainData", result.content);
|
||||
return result.content;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
export const getChartDataRainData = (arr: TRainData[]): { label: string; lineVal0: number, barVal0: number }[] => {
|
||||
const options = arr.map(data => ({
|
||||
label: moment(data.mtrngDt, "YYYY-MM-DD HH:mm:ss").format("MM-DD HH시"),
|
||||
lineVal0: 0,
|
||||
barVal0: roundTo(data.amrf,2),
|
||||
}));
|
||||
return options;
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import { Button, Input, Label } from '@common/components';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import moment from "moment";
|
||||
|
||||
import type { TInftwtAnls, TOilqData } from './aplcnYms';
|
||||
import type { TInftwtAnls } from './aplcnYms';
|
||||
import * as fn from './aplcnYms';
|
||||
import { useInfilChartModalStore } from '../chartModalStore'; // 모달 상태 관리 스토어
|
||||
type ModalOpenProp = {
|
||||
@@ -20,13 +20,13 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
|
||||
const [selectedAplcnYm, setSelectedAplcnYm] = useState("");
|
||||
const [inftwtAnlss, setInftwtAnlss] = useState<TInftwtAnls[]>([]);
|
||||
const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options
|
||||
const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options
|
||||
const [inftwtAnls, setInftwtAnls] = useState<TInftwtAnls | null>(null); // 침입수분석정보
|
||||
const [selectedInftwtAnls, setSelectedInftwtAnls] = useState<number>(0); // 침입수분석 정보 선택시 사용
|
||||
const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({
|
||||
chartData1: [], chartData2: [],
|
||||
});
|
||||
const [inftwtQty, setInftwtQty] = useState<number>(0); // 침입수량
|
||||
const [inftwtQty, setInftwtQty] = useState<string>("0"); // 침입수량
|
||||
const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt'); // 상수사용월 데이터 가져오기
|
||||
|
||||
pplMngNo = "L" + pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여
|
||||
@@ -56,7 +56,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
setInftwtAnls(null); // 침입수분석정보 초기화
|
||||
setSelectedInftwtAnls(0); // 침입수분석 일련번호 초기화
|
||||
setChartData({ chartData1: [], chartData2: [], }); // 차트 데이터 초기화
|
||||
setInftwtQty(0); // 침입수량 초기화
|
||||
setInftwtQty("0"); // 침입수량 초기화
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
@@ -133,7 +133,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
});
|
||||
fn.selectPplInftwtQty(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수량 조회
|
||||
if (data > 0) {
|
||||
setInftwtQty(data);
|
||||
setInftwtQty(data.toLocaleString('ko-KR'));
|
||||
}
|
||||
});
|
||||
setPopupOpen(false);
|
||||
|
||||
@@ -5,13 +5,20 @@ import { Button, Input, Label } from '@common/components';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import moment from "moment";
|
||||
|
||||
import type { TRdiiAnls, TRdiiAnlsRslt, TOilqData } from './aplcnYms';
|
||||
import type { TRdiiAnls } from './aplcnYms';
|
||||
import * as fn from './aplcnYms';
|
||||
import { useRdiiChartModalStore } from '../chartModalStore'; // 모달 상태 관리 스토어
|
||||
|
||||
type ModalOpenProp = {
|
||||
pplMngNo: string, // 관로관리번호
|
||||
}
|
||||
type DataType = {label: string; val: number};
|
||||
// 차트
|
||||
type lineBarChartData = {
|
||||
label : string;
|
||||
lineVal0 : number;
|
||||
barVal0 : number;
|
||||
}
|
||||
|
||||
|
||||
const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useRdiiChartModalStore(); // 모달 상태 관리 스토어
|
||||
@@ -20,17 +27,16 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
|
||||
const [aplcnYm, setAplcnYm] = useState("");
|
||||
const [rdiiAnlss, setRdiiAnlss] = useState<TRdiiAnls[]>([]);
|
||||
const [rdiiAnlsInfos, setRdiiAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 강우유입수분석정보 options
|
||||
const [rdiiAnlsInfos, setRdiiAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>([{ val: 0, text: '선택' }]); // 강우유입수분석정보 options
|
||||
const [rdiiAnls, setRdiiAnls] = useState<TRdiiAnls | null>(null); // 강우유입수분석정보
|
||||
const [rainInfwtAnlsSn, setRainInfwtAnlsSn] = useState<number>(0); // 강우유입수분석 정보 선택시 사용
|
||||
const [chartData, setChartData] = useState<{ chartData1: DataType[]; }>({
|
||||
chartData1: [], //chartData2: [],
|
||||
});
|
||||
const [rdiiQty, setRdiiQty] = useState<number>(0); // 강우유입수량
|
||||
const [totalVolume, setTotalVolume] = useState<number>(0); // 총체적 m³/mm
|
||||
const [chartData, setChartData] = useState<lineBarChartData[]>([]);
|
||||
const [rdiiQty, setRdiiQty] = useState<string>("0"); // 강우유입수량
|
||||
const [rainTotal, setRainTotal] = useState<string>("0"); // 적용강우량
|
||||
const [totalVolume, setTotalVolume] = useState<string>("0"); // 총체적 m³/mm
|
||||
const aplcnYms = fn.useAplcnYms('rdiiAnlsRslt'); // 상수사용월 데이터 가져오기
|
||||
pplMngNo = "L"+pplMngNo; // "L13101003";
|
||||
console.log("pplMngNo", pplMngNo);
|
||||
|
||||
const openPopup = () => {
|
||||
if (buttonRef.current) {
|
||||
const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
|
||||
@@ -42,10 +48,16 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
setPopupOpen(true); // 팝업 열기
|
||||
};
|
||||
|
||||
const chartForm = CHART_FORM.LINE;
|
||||
const chartForm = CHART_FORM.LINEBAR2;
|
||||
const chartConfig = {
|
||||
chartData1: { label: 'RDII', color: "red", },
|
||||
//chartData2: { label: '순오수량(m³/h)', color: "orange", },
|
||||
barVal0: {
|
||||
label: '강수량',
|
||||
color: "#33AAFF",
|
||||
},
|
||||
lineVal0: {
|
||||
label: 'RDII',
|
||||
color: "#FF0000",
|
||||
},
|
||||
} as ChartConfig;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -56,9 +68,10 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
setRdiiAnlsInfos([{ val: 0, text: '선택' }]); // 강우유입수분석정보 초기화
|
||||
setRdiiAnls(null); // 강우유입수분석정보 초기화
|
||||
setRainInfwtAnlsSn(0); // 강우유입수분석 일련번호 초기화
|
||||
setChartData({chartData1: [], }); // 차트 데이터 초기화
|
||||
setRdiiQty(0); // 강우유입수량 초기화
|
||||
setTotalVolume(0); // 총체적 m³/mm 초기화
|
||||
setChartData([]); // 차트 데이터 초기화
|
||||
setRdiiQty("0"); // 강우유입수량 초기화
|
||||
setRainTotal("0"); // 적용강우량 초기화
|
||||
setTotalVolume("0"); // 총체적 m³/mm 초기화
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
@@ -70,7 +83,6 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
setRdiiAnlsInfos([]);
|
||||
return;
|
||||
}
|
||||
console.log(moment(aplcnYm).format("YYYY-MM"));
|
||||
fn.selectRdiiAnls(aplcnYm)
|
||||
.then((data) => {
|
||||
setRdiiAnlss(data ?? []); // 선택한 월의 분석성공한 RDII분석데이터[]
|
||||
@@ -93,29 +105,38 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
if (selectedAnls) {
|
||||
setRdiiAnls(selectedAnls);
|
||||
}
|
||||
}, [rainInfwtAnlsSn]);
|
||||
}, [rainInfwtAnlsSn, rdiiAnlss]);
|
||||
|
||||
const closePopup = () => { // 팝업 닫기
|
||||
setPopupOpen(false);
|
||||
}
|
||||
const rdiiRsltChart = () => { // 그래프 & 팝업 닫기
|
||||
fn.selectRdiiAnlsRslt(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수분석 결과 조회
|
||||
const rsltData1 = fn.getChartDataRdiiAnlsRslt(data ?? []);
|
||||
//const rsltData2 = fn.getChartDataRdiiAnlsRsltMinq(data ?? []);
|
||||
setChartData(prev => ({
|
||||
chartData1: rsltData1, // 순오수량(m³/h)
|
||||
// chartData2: rsltData2 // RDII(m³/h) 강우유입수분석결과
|
||||
}));
|
||||
});
|
||||
fetchFilteredData();
|
||||
fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회
|
||||
setRdiiQty(Math.round(data.rdiih*24)); // cmd: m³/d 단위로 변환
|
||||
setRdiiQty(Math.round(data.rdiih*24).toLocaleString('ko-KR')); // cmd: m³/d 단위로 변환
|
||||
if (data.raintotal && data.rainTotal !== 0) {
|
||||
setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal)); // 총체적 m³/mm
|
||||
setRainTotal(data.raintotal.toLocaleString('ko-KR')); // 총강우량mm
|
||||
setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal).toLocaleString('ko-KR')); // 총체적 m³/mm
|
||||
}
|
||||
});
|
||||
setPopupOpen(false);
|
||||
};
|
||||
|
||||
const fetchFilteredData: () => Promise<void> = async () => {
|
||||
const rsltData = await fn.selectRdiiAnlsRslt(rainInfwtAnlsSn, pplMngNo);
|
||||
const rsltDataformatted = fn.getChartDataRdiiAnlsRslt(rsltData ?? []); // format 변경
|
||||
const rainData = await fn.selectRainData(rainInfwtAnlsSn);
|
||||
const rainDataformatted = fn.getChartDataRainData(rainData ?? []); // format 변경
|
||||
const rainMap = new Map(rainDataformatted.map(item => [item.label, item.barVal0]));
|
||||
|
||||
rsltDataformatted.forEach(item => {
|
||||
const matchedVal = rainMap.get(item.label);
|
||||
if (matchedVal !== undefined) {
|
||||
item.barVal0 = matchedVal;
|
||||
}
|
||||
});
|
||||
setChartData(rsltDataformatted); // 차트 데이터 설정
|
||||
}
|
||||
const renderBody = () => (
|
||||
<>
|
||||
<div className="text-black text-3xl font-bold mb-5">RDII분석결과</div>
|
||||
@@ -143,6 +164,11 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {rdiiQty}</span>
|
||||
<span className="ml-2 text-black"> m³/d</span>
|
||||
</div>
|
||||
<div className="flex items-center mt-2">
|
||||
<span className="w-[95px] inline-block text-right text-black text-xl">적용강우량 : </span>
|
||||
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {rainTotal}</span>
|
||||
<span className="ml-2 text-black"> mm</span>
|
||||
</div>
|
||||
<div className="flex items-center mt-2">
|
||||
<span className="w-[95px] inline-block text-right text-black text-xl">강우량대비RDII : </span>
|
||||
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {totalVolume}</span>
|
||||
@@ -153,7 +179,7 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
|
||||
{/* 차트 */}
|
||||
<div className="h-[90%] bg-white text-2xl">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'RDII 그래프'}
|
||||
yFieldText2="시간" yFieldText="유량(m³/h)" />
|
||||
yFieldText2="강수량(mm)" yFieldText="유량(m³/h)" />
|
||||
</div>
|
||||
</div>
|
||||
{popupOpen && (
|
||||
|
||||
@@ -8,8 +8,8 @@ const exclnInputColumn = [
|
||||
]
|
||||
// 오수펌프 자체 입력 정보
|
||||
const drtwInputColumn = [
|
||||
{ id: "swtrmt_zone_cd", title: "배수구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "처리분구", type: "input" },
|
||||
]
|
||||
|
||||
// 하수펌프장
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 하수맨홀
|
||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
const inputColumn = [
|
||||
{ id: "swtrmtZoneCd", title: "처리구역", type: "input" },
|
||||
{ id: "mltvZoneNm", title: "배수구역", type: "input" },
|
||||
|
||||
@@ -12,6 +12,8 @@ import RdiiChart from './components/popupComponent/chart/rdiiChart';
|
||||
import { useInfilChartModalStore, useRdiiChartModalStore } from "./components/popupComponent/chartModalStore";
|
||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
import { codeMapper } from "./components/common/codeMapper";
|
||||
import { searchUpperPpls } from '@common/business_components/ui/map/Gis';
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar';
|
||||
|
||||
|
||||
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
|
||||
@@ -33,6 +35,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
const { setIsOpen: setIsModalRdii } = useRdiiChartModalStore();
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지 상태
|
||||
const [isClicked, setIsClicked] = useState(true); // 클릭 여부 상태
|
||||
const [isLoading, setIsLoading] = useState(false); // 로딩 상태
|
||||
|
||||
// 🎯 관로관리번호 추출
|
||||
const pplMngNo = featureData?.values_?.fclt_mng_no || "";
|
||||
@@ -92,13 +95,13 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
else if (lyrTyNm === 'tb_ms_swage_pmstn') { // 하수펌프장
|
||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
|
||||
setIsDrtwSewagePump(false);
|
||||
setHeadlist(ExclnSewagePumpCol);
|
||||
}
|
||||
else {
|
||||
setIsDrtwSewagePump(true);
|
||||
setHeadlist(DrtwSewagePumpCol);
|
||||
}
|
||||
else {
|
||||
setIsDrtwSewagePump(false);
|
||||
setHeadlist(ExclnSewagePumpCol);
|
||||
}
|
||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
result = '하수펌프장';
|
||||
}
|
||||
@@ -469,42 +472,59 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
|
||||
return (
|
||||
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[50]">
|
||||
<div className="card-header bg-primary flex items-center justify-between p-4">
|
||||
<div className="flex items-center">
|
||||
<h3 className="text-2xl font-medium text-white text-label whitespace-nowrap mr-4">{layerNm}
|
||||
<LoadingOverlay isLoading={isLoading} />
|
||||
<div className="card-header bg-primary flex items-center justify-between relative p-4">
|
||||
<div className="flex items-center space-x-4 flex-1">
|
||||
<h3 className="text-2xl font-medium text-white text-label whitespace-nowrap">{layerNm}
|
||||
{tableData?.usg_cd && (
|
||||
<>
|
||||
({tableData.usg_cd})
|
||||
</>
|
||||
)} 정보 <br />관리번호 : {layerSpecificNm}
|
||||
</h3>
|
||||
<div className="flex items-center space-x-3">
|
||||
{/* 🎯 하수관로일 때만 Infil/RDII 버튼 표시 */}
|
||||
{layerNm === '하수관로' && (
|
||||
<>
|
||||
<Button onClick={() => setIsModalInfil(true)}
|
||||
className="py-1 text-xl text-blue-600 border border-blue-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
size="sm"
|
||||
>
|
||||
침입수분석
|
||||
</Button>
|
||||
<Button onClick={() => setIsModalRdii(true)}
|
||||
className="py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
size="sm"
|
||||
>
|
||||
RDII분석
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{layerNm === '하수맨홀' && (
|
||||
<>
|
||||
<Button onClick={() => {
|
||||
setIsLoading(true);
|
||||
searchUpperPpls(layerSpecificNm).finally(() => setIsLoading(false));
|
||||
}}
|
||||
className="px-2 py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
>
|
||||
상류관로 보기
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
{/* 🎯 하수관로일 때만 Infil/RDII 버튼 표시 */}
|
||||
{layerNm === '하수관로' && (
|
||||
<>
|
||||
<Button onClick={() => setIsModalInfil(true)}
|
||||
className="px-2 py-1 text-xl text-blue-600 border border-blue-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
>
|
||||
침입수분석
|
||||
</Button>
|
||||
<Button onClick={() => setIsModalRdii(true)}
|
||||
className="px-2 py-1 text-xl text-gray-600 border border-gray-600 rounded hover:bg-gray-700"
|
||||
color="white"
|
||||
>
|
||||
RDII분석
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
onClick={onClose}
|
||||
className="text-white hover:text-gray-300 focus:outline-none"
|
||||
>
|
||||
✕
|
||||
</Button>
|
||||
</div>
|
||||
<Button
|
||||
onClick={onClose}
|
||||
// className="text-white hover:text-gray-300 focus:outline-none"
|
||||
className="absolute top-8 right-4 text-white hover:text-gray-300 focus:outline-none"
|
||||
size="xs"
|
||||
>
|
||||
✕
|
||||
</Button>
|
||||
</div>
|
||||
<div className="overflow-y-auto overflow-x-hidden flex-1" ref={contentRef}>
|
||||
<VerticalTable
|
||||
|
||||
@@ -41,7 +41,7 @@ const Map = () => {
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getData('v1/map/lyrStngs/SN_NETWORK');
|
||||
const data = await getData("v1/map/lyrStngs/SN_NETWORK");
|
||||
setTreeData(data);
|
||||
} catch (error) {
|
||||
console.error("데이터 불러오기 실패:", error);
|
||||
@@ -115,22 +115,22 @@ const Map = () => {
|
||||
};
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
treeData={treeData}
|
||||
isSearch={true}
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={false}
|
||||
searchView={SearchModal}
|
||||
detailView={FeatureClickEventPopup}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
featureData={featureData}
|
||||
setIsSearchModalOpen={setIsSearchModalOpen}
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={undefined}
|
||||
/>
|
||||
<Gis
|
||||
treeData={treeData}
|
||||
isSearch={true}
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={false}
|
||||
searchView={SearchModal}
|
||||
detailView={FeatureClickEventPopup}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
featureData={featureData}
|
||||
setIsSearchModalOpen={setIsSearchModalOpen}
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ const BasicTab: React.FC<BasicTabProps> = ({
|
||||
setVar2,
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
<div className="relative border rounded-lg p-6 w-full h-[10vh]">
|
||||
{/* 구분 */}
|
||||
<div className="mb-3 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -39,6 +39,7 @@ export const BIZ_TY_CD_SELECT_ARRAY = BIZ_TY_CD.map(item => ({
|
||||
}));
|
||||
// 운영정보 - 사업 현황
|
||||
export const operationTableHeadlist = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: "bizNm", title: "사업명" },
|
||||
{ id: "flfmtBgngYmd", title: "수행시작일자" },
|
||||
{ id: "flfmtEndYmd", title: "수행종료일자" },
|
||||
@@ -48,6 +49,7 @@ export const operationTableHeadlist = [
|
||||
];
|
||||
// 운영정보 - 보수 및 갱신 결과표
|
||||
export const operationMaintenanceTableHeadlist = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
@@ -58,6 +60,7 @@ export const operationMaintenanceTableHeadlist = [
|
||||
];
|
||||
// 운영정보 - 청소 및 준설 결과표
|
||||
export const operationCleaningTableHeadlist = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
@@ -67,6 +70,7 @@ export const operationCleaningTableHeadlist = [
|
||||
];
|
||||
// 운영정보 - 점검 및 진단 결과표
|
||||
export const operationInspectionTableHeadlist = [
|
||||
{ id: 'rowNumber', title: '일련번호' },
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
@@ -109,7 +113,7 @@ const OperationTab: React.FC<OperationTabProps> = ({
|
||||
setYearFilter,
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<SelectBox
|
||||
className="w-80 mr-8"
|
||||
|
||||
@@ -61,7 +61,7 @@ const ReportTab: React.FC<BasicTabProps> = ({
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-6 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<SelectBox
|
||||
className="w-[150px] mr-8"
|
||||
|
||||
@@ -5,13 +5,14 @@ import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { ExclnSewagePumpCol2, MsSwageMhlCol2, MsSwagePplCol2, stofcbrCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2 } from '../components/common/table';
|
||||
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
import { type SelectDataType } from '../commonStore';
|
||||
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from '../components/common/table';
|
||||
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES } from '../components/common/table';
|
||||
import { addFileName, flattenFolders, getFileDownload, useCityCd, useDcTree, useSggCd } from '../components/searchModalReportApi';
|
||||
// import { useBizByFcltStore } from "../../../store/StoreProvider";
|
||||
import { getData } from "@/components/lib/api";
|
||||
import BasicTab, { BASIC_TYPES } from './basicTab';
|
||||
import OperationTab, { BIZ_TY_CD, operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab';
|
||||
import ReportTab, { reportTableHeadlist } from './reportTab';
|
||||
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
|
||||
|
||||
@@ -58,6 +59,12 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const [detailTableCount, setDetailTableCount] = useState(0);
|
||||
const [curPage, setCurPage] = useState(1);
|
||||
const [detailCurPage, setDetailCurPage] = useState(1);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isDetailLoading, setIsDetailLoading] = useState(false);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||
const [isResetDetailPage, setIsResetDetailPage] = useState<boolean>(false);
|
||||
const [isResetSelection, setIsResetSelection] = useState<boolean>(false);
|
||||
const [isResetDetailSelection, setIsResetDetailSelection] = useState<boolean>(false);
|
||||
const [detailBizId, setDetailBizId] = useState(0);
|
||||
const [city, setCity] = useState(0);
|
||||
const [sgg, setSgg] = useState(0);
|
||||
@@ -76,10 +83,6 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const [yearFilter, setYearFilter] = useState({ start: "", end: "" });
|
||||
const [filteredSiguns, setFilteredSiguns] = useState<SelectDataType[]>([{ val: '0', text: '전체', sido: '0' }]);
|
||||
|
||||
// 로딩바
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isDetailLoading, setIsDetailLoading] = useState(false);
|
||||
|
||||
const sidos = useCityCd();
|
||||
const siguns = useSggCd();
|
||||
const reportOriginData = useDcTree(""); //:Folder[]
|
||||
@@ -138,10 +141,18 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
end: "",
|
||||
});
|
||||
setShowTable(false);
|
||||
setTableCount(0);
|
||||
setTableData([]);
|
||||
setDetailTableData([]);
|
||||
setTableCount(0);
|
||||
setDetailTableCount(0);
|
||||
setCurPage(1); // 페이지를 1로 초기화
|
||||
setDetailCurPage(1); // 페이지를 1로 초기화
|
||||
setIsResetPage(true); // 페이지를 초기화
|
||||
setIsResetDetailPage(true); // 디테일 페이지를 초기화
|
||||
setIsResetSelection(true); // 선택 초기화
|
||||
setIsResetDetailSelection(true); // 선택 초기화
|
||||
// queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음
|
||||
}, [activeTab, basicType, open]);
|
||||
}, [activeTab, basicType, workType, open]);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTab(TABS[0]?.val ?? "basic");
|
||||
@@ -156,19 +167,27 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
// 페이지 변경 시마다 데이터 fetch
|
||||
useEffect(() => {
|
||||
// showTable이 true일 때만 fetch (최초 렌더 방지)
|
||||
setIsResetPage(false);
|
||||
setIsResetDetailPage(true);
|
||||
if (showTable) fetchFilteredData();
|
||||
}, [curPage]);
|
||||
|
||||
// 페이지 변경 시마다 데이터 fetch
|
||||
useEffect(() => {
|
||||
setIsResetDetailSelection(false);
|
||||
// showTable이 true일 때만 fetch (최초 렌더 방지)
|
||||
if (showTable) handleOperationPageNation(Number(detailBizId));
|
||||
if (showTable) {
|
||||
handleOperationPageNation(Number(detailBizId));
|
||||
}
|
||||
}, [detailCurPage]);
|
||||
|
||||
|
||||
// 검색 API 호출 함수
|
||||
const fetchFilteredData = async () => {
|
||||
setIsLoading(true); // 로딩 시작
|
||||
setIsResetSelection(true); // 선택 초기화
|
||||
setIsResetDetailSelection(true); // 선택 초기화
|
||||
|
||||
try {
|
||||
let cqlFilters: string[] = [];
|
||||
if (activeTab === 'basic') {
|
||||
@@ -193,7 +212,23 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
console.log('필터링된 API 결과(개수):', res.data.count);
|
||||
console.log('필터링된 API 결과(데이터):', res.data.data);
|
||||
setTableData(res.data.data || []);
|
||||
|
||||
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
|
||||
const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => {
|
||||
// 🎯 먼저 각 아이템을 포맷팅
|
||||
const formattedItem = formatDisplayData(item);
|
||||
const matched = MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim());
|
||||
|
||||
// 추가 처리 및 일련번호 추가
|
||||
return {
|
||||
...formattedItem,
|
||||
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경
|
||||
mqltCd: matched ? matched.text : item.mqltCd,
|
||||
};
|
||||
});
|
||||
|
||||
setTableData(dataWithRowNumber);
|
||||
setTableCount(res.data.count || []);
|
||||
console.log('tableCount 상태 업데이트 후:', res.data.count); // tableCount 로그 추가
|
||||
|
||||
@@ -230,10 +265,13 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
console.log('필터링된 API 결과(개수):', res.totalElements);
|
||||
console.log('필터링된 API 결과(데이터):', res.content);
|
||||
|
||||
const mappedContent = (res.content || []).map((item: any) => {
|
||||
const mappedContent = (res.content || []).map((item: any, index: number) => {
|
||||
console.log("item:", item);
|
||||
const formattedItem = formatDisplayData(item);
|
||||
const matched = BIZ_TY_CD.find(code => code.lwpoCd === item.bizTyCd);
|
||||
return {
|
||||
...item,
|
||||
...formattedItem,
|
||||
rowNumber: (curPage - 1) * 4 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
bizTyCd: matched ? matched.lwpoCdNm : item.bizTyCd,
|
||||
};
|
||||
});
|
||||
@@ -285,6 +323,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
setTableData([]);
|
||||
} finally {
|
||||
setIsLoading(false); // 로딩 종료
|
||||
// setIsResetSelection(false);
|
||||
// setIsResetDetailSelection(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -448,9 +488,17 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
|
||||
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
|
||||
const handleSearch = () => {
|
||||
setCurPage(1); // 페이지를 1로 초기화
|
||||
setDetailCurPage(1); // 페이지를 1로 초기화
|
||||
setCurPage(1); // 페이지를 1로 초기화
|
||||
// setDetailCurPage(1); // 페이지를 1로 초기화
|
||||
// setIsResetPage(false); // 페이지를 초기화
|
||||
// setIsResetDetailPage(true); // 디테일 페이지를 초기화
|
||||
// setIsResetSelection(true); // 선택 초기화
|
||||
// setIsResetDetailSelection(true); // 선택 초기화
|
||||
fetchFilteredData();
|
||||
setIsResetPage(true); // 페이지를 초기화
|
||||
setIsResetDetailPage(true); // 디테일 페이지를 초기화
|
||||
setIsResetSelection(false); // 선택 초기화
|
||||
setIsResetDetailSelection(false); // 선택 초기화
|
||||
setShowTable(true);
|
||||
};
|
||||
|
||||
@@ -463,6 +511,10 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
};
|
||||
// 운영정보 테이블 cellClick 핸들러
|
||||
const handleOperationCellClick = async (index?: number | undefined, headId?: String) => {
|
||||
setIsResetDetailPage(true); // 페이지를 1로 초기화
|
||||
setIsResetDetailSelection(true); // 선택 초기화
|
||||
setIsResetDetailPage(true); // 디테일 페이지를 초기화
|
||||
|
||||
if (typeof index === "number") {
|
||||
setIsDetailLoading(true);
|
||||
setDetailBizId(tableData[index].bizId);
|
||||
@@ -478,11 +530,14 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
console.log('운영정보 cellClick 데이터:', res);
|
||||
console.log('운영정보 length:', res.data.length);
|
||||
|
||||
const mappedData = (res.data || []).map((item: any) => {
|
||||
const mappedData = (res.data || []).map((item: any, detailIndex: number) => {
|
||||
const formattedItem = formatDisplayData(item);
|
||||
|
||||
const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim());
|
||||
const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim());
|
||||
return {
|
||||
...item,
|
||||
...formattedItem,
|
||||
rowNumber: (detailCurPage - 1) * 4 + detailIndex + 1,
|
||||
usgCd: usgCdObj ? usgCdObj.text : item.usgCd,
|
||||
mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd,
|
||||
};
|
||||
@@ -494,6 +549,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
console.error('운영정보 cellClick 에러:', error);
|
||||
} finally {
|
||||
setIsDetailLoading(false);
|
||||
setIsResetDetailPage(false);
|
||||
setIsResetDetailSelection(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -508,14 +565,15 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
bizId: String(detailBizId),
|
||||
};
|
||||
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
|
||||
console.log('운영정보 페이지네이션 데이터:', res);
|
||||
console.log('운영정보 페이지네이션 length:', res.data.length);
|
||||
|
||||
const mappedData = (res.data || []).map((item: any) => {
|
||||
const mappedData = (res.data || []).map((item: any, detailIndex: number) => {
|
||||
const formattedItem = formatDisplayData(item);
|
||||
|
||||
const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim());
|
||||
const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim());
|
||||
return {
|
||||
...item,
|
||||
...formattedItem,
|
||||
rowNumber: (detailCurPage - 1) * 4 + detailIndex + 1,
|
||||
usgCd: usgCdObj ? usgCdObj.text : item.usgCd,
|
||||
mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd,
|
||||
};
|
||||
@@ -567,6 +625,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
}))}
|
||||
onValueChange={(value: string) => setActiveTab(value)}
|
||||
>
|
||||
</UITab>
|
||||
{/* 각각의 탭 영역 */}
|
||||
{/* 기본정보 탭 */}
|
||||
{activeTab === 'basic' && (
|
||||
@@ -628,19 +687,19 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
/>
|
||||
)}
|
||||
{/* </UITab2> */}
|
||||
</UITab>
|
||||
|
||||
|
||||
{/* 📋 결과 리스트 */}
|
||||
{/* 기본정보 탭, 하수도정비기본계획보고서 탭 */}
|
||||
{(activeTab === 'basic' || activeTab === 'report') && (
|
||||
<div className="h-[80vh] w-full overflow-hidden rounded-lg border">
|
||||
<div className="h-[79vh] w-full overflow-hidden rounded-lg border">
|
||||
{/* 테이블2 영역 (가로/세로 스크롤 가능) */}
|
||||
<div className="flex-1 px-2 pb-2">
|
||||
<LoadingOverlay isLoading={isLoading} />
|
||||
<UiTable
|
||||
headName={(() => {
|
||||
const label = BASIC_TYPES.find((type) => type.val === basicType)?.text
|
||||
return (<>{label} 조회 결과 ({tableCount})건</>);
|
||||
return (<>{label} 조회 결과 ({tableCount.toLocaleString('ko-KR')})건</>);
|
||||
})()
|
||||
}
|
||||
tableData={tableData}
|
||||
@@ -649,7 +708,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
total={tableCount}
|
||||
cellClick={activeTab === 'report' ? handleReportCellClick : handleBasicCellClick}
|
||||
onPageChange={setCurPage}
|
||||
resetPageNo={false}
|
||||
resetPageNo={isResetPage}
|
||||
resetSelection={isResetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -657,14 +717,14 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
{/* 운영정보 탭 */}
|
||||
{activeTab === 'operation' && (
|
||||
<>
|
||||
<div className="h-[35vh] w-full overflow-hidden rounded-lg border">
|
||||
<div className="h-[39vh] w-full overflow-hidden rounded-lg border">
|
||||
<div className="flex-1 px-2 pb-2">
|
||||
<LoadingOverlay isLoading={isLoading} />
|
||||
<UiTable
|
||||
headName={
|
||||
(() => {
|
||||
const label = WORK_TYPES.find((type) => type.val === workType)?.text
|
||||
return (<>{label} 사업 현황 ({tableCount})건</>);
|
||||
return (<>{label} 사업 현황 ({tableCount.toLocaleString('ko-KR')})건</>);
|
||||
})()
|
||||
}
|
||||
tableData={tableData}
|
||||
@@ -673,19 +733,20 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
total={tableCount}
|
||||
cellClick={handleOperationCellClick}
|
||||
onPageChange={setCurPage}
|
||||
resetPageNo={false}
|
||||
resetPageNo={isResetPage}
|
||||
resetSelection={isResetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-[35vh] w-full overflow-hidden rounded-lg border">
|
||||
<div className="h-[39vh] w-full overflow-hidden rounded-lg border">
|
||||
<div className="flex-1 px-2 pb-2">
|
||||
<LoadingOverlay isLoading={isDetailLoading} />
|
||||
<UiTable
|
||||
headName={
|
||||
(() => {
|
||||
const label = WORK_TYPES.find((type) => type.val === workType)?.text
|
||||
return (<>{label} 결과표 ({detailTableCount})건</>);
|
||||
return (<>{label} 결과표 ({detailTableCount.toLocaleString('ko-KR')})건</>);
|
||||
})()
|
||||
}
|
||||
tableData={detailTableData}
|
||||
@@ -694,7 +755,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
total={detailTableCount}
|
||||
cellClick={undefined}
|
||||
onPageChange={setDetailCurPage}
|
||||
resetPageNo={false}
|
||||
resetPageNo={isResetDetailPage}
|
||||
resetSelection={isResetDetailSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,13 @@ const SwageMhl: React.FC<SwageMhlProps> = ({
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="mb-2 flex items-center">
|
||||
@@ -70,6 +77,7 @@ const SwageMhl: React.FC<SwageMhlProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -81,6 +89,7 @@ const SwageMhl: React.FC<SwageMhlProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
|
||||
@@ -31,6 +31,13 @@ const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center mb-2">
|
||||
@@ -53,6 +60,7 @@ const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -64,6 +72,7 @@ const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
@@ -78,6 +87,7 @@ const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart2(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -89,6 +99,7 @@ const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd2(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
|
||||
@@ -33,6 +33,13 @@ const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="mb-2 flex items-center">
|
||||
@@ -85,6 +92,7 @@ const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setVar1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -96,6 +104,7 @@ const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 연장 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">연장(m)</Label>
|
||||
@@ -108,6 +117,7 @@ const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -119,6 +129,7 @@ const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
|
||||
@@ -35,6 +35,13 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{/* 가동개시일자 */}
|
||||
@@ -64,6 +71,7 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -75,6 +83,7 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 처리구역 면적 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">처리구역 면적(m²)</Label>
|
||||
@@ -87,6 +96,7 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart2(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
@@ -98,6 +108,7 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd2(val);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 하수처리시설명 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">하수처리시설명</Label>
|
||||
@@ -108,6 +119,7 @@ const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
|
||||
@@ -27,6 +27,13 @@ const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
setSgg,
|
||||
handleSearch,
|
||||
}) => {
|
||||
|
||||
const handleEnterKey = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="mb-2 flex items-center">
|
||||
@@ -47,6 +54,7 @@ const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">하수처리시설명</Label>
|
||||
<Input
|
||||
@@ -56,6 +64,7 @@ const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
onChange={(e) => {
|
||||
setVar2(e.target.value);
|
||||
}}
|
||||
onKeyDown={handleEnterKey}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
|
||||
@@ -218,7 +218,7 @@ const BaseLayout = ({
|
||||
<div className="flex items-center w-full">
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">상수사용기간</Label>
|
||||
<Label className="mr-4 w-20 text-xl text-gray-600 whitespace-nowrap">상수사용기간</Label>
|
||||
<DateMonthPicker
|
||||
field={{
|
||||
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
|
||||
@@ -247,7 +247,7 @@ const BaseLayout = ({
|
||||
setAplcnYmTo?.(value); // string 값을 직접 전달
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 w-[100px]"
|
||||
selectClass="w-[100px]"
|
||||
startYear={1900} // 시작 연도 설정
|
||||
/>
|
||||
</>
|
||||
@@ -258,7 +258,7 @@ const BaseLayout = ({
|
||||
setRtnEnvPartSidoValue={(val) => setCtpvCd?.(val)}
|
||||
setRtnEnvPartSigunValue={setSigungu} // 선택된 시군구 값을 받을 콜백 함수 전달
|
||||
>
|
||||
<SelectBox label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} selectContentClass="z-[101]" />
|
||||
<SelectBox label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="ml-5 rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} selectContentClass="z-[101]" />
|
||||
{/* <SelectBox label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} selectContentClass="z-[101]" /> */}
|
||||
</GovSidoSigunguSearchForm>
|
||||
{selectBox?.searchChk ? <SearchFormRight><Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button></SearchFormRight> : null}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import { deleteData, getData } from '@/components/lib/api'
|
||||
import { deleteData, getData, useCookieAuth } from '@/components/lib/api'
|
||||
import { UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import { useState, useEffect } from 'react'
|
||||
@@ -53,6 +53,8 @@ const tableColumn = [
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
const AUTH = useCookieAuth();
|
||||
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
@@ -81,24 +83,36 @@ const Result = () => {
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지
|
||||
|
||||
useEffect(() => {
|
||||
// setCurPage(1);
|
||||
handleSearchButtonClick();
|
||||
}, [])
|
||||
|
||||
// 페이지 변경 시마다 데이터 fetch
|
||||
useEffect(() => {
|
||||
setIsResetPage(false);
|
||||
handleSearchButtonClick();
|
||||
}, [curPage]);
|
||||
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
|
||||
const handleSearch: () => Promise<void> = async () => {
|
||||
setCurPage(1); // 페이지를 1로 초기화
|
||||
setIsResetPage(true); // 페이지를 초기화
|
||||
await handleSearchButtonClick();
|
||||
};
|
||||
|
||||
|
||||
const handleSearchButtonClick = async () => {
|
||||
setIsLoadingResult(true);
|
||||
setResetSelection(true);
|
||||
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
// swageTmplntCd 처리 로직 추가
|
||||
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
|
||||
? (() => {
|
||||
@@ -108,30 +122,21 @@ const Result = () => {
|
||||
})()
|
||||
: null;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom,
|
||||
"aplcnYmTo": formValues.aplcnYmTo,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
};
|
||||
}
|
||||
let inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
"auth": AUTH,
|
||||
};
|
||||
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
page: `${curPage - 1}`,
|
||||
size: '10',
|
||||
sort: 'rainInfwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
@@ -153,14 +158,13 @@ const Result = () => {
|
||||
// 추가 처리 및 일련번호 추가
|
||||
return {
|
||||
...formattedItem,
|
||||
rowNumber: index + 1, // 1부터 시작하는 일련번호 추가
|
||||
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
};
|
||||
});
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
|
||||
setTableData(dataWithRowNumber);
|
||||
setTotalData(dataWithRowNumber.length);
|
||||
@@ -173,6 +177,7 @@ const Result = () => {
|
||||
alert("분석결과 조회 중 오류가 발생했습니다.");
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
setSelectedRowData(null);
|
||||
}
|
||||
setResetSelection(false);
|
||||
};
|
||||
@@ -181,7 +186,6 @@ const Result = () => {
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
@@ -258,16 +262,8 @@ const Result = () => {
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYmFrom: selectedRowData.aplcnYmFrom || null,
|
||||
aplcnYmTo: selectedRowData.aplcnYmTo || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swageTmplntCd: selectedRowData.swageTmplntCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
// 검색 버튼 클릭 이벤트 트리거
|
||||
await handleSearchButtonClick();
|
||||
|
||||
setIsLoadingResult(false);
|
||||
|
||||
@@ -285,7 +281,7 @@ const Result = () => {
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearch }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: 'RDII 산정결과 현황' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
@@ -311,8 +307,8 @@ const Result = () => {
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ RDII 분석결과 현황</div>
|
||||
{[<div key='table1' className="h-[58vh] flex flex-col border rounded-lg bg-white shadow-md">
|
||||
{/* <div className="mb-[1.2rem] text-xl font-medium">○ RDII 분석결과 현황</div> */}
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
@@ -320,10 +316,10 @@ const Result = () => {
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
onPageChange={setCurPage}
|
||||
resetSelection={resetSelection}
|
||||
resetPageNo={isResetPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -74,26 +74,41 @@ const Result = () => {
|
||||
}
|
||||
})
|
||||
|
||||
const AUTH = useCookieAuth();
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수l
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
const [isResetPage, setIsResetPage] = useState<boolean>(false);
|
||||
const [curPage, setCurPage] = useState(1); // 현재 페이지
|
||||
|
||||
useEffect(() => {
|
||||
// setCurPage(1);
|
||||
handleSearchButtonClick();
|
||||
}, [])
|
||||
const auth = useCookieAuth();
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
|
||||
// 페이지 변경 시마다 데이터 fetch
|
||||
useEffect(() => {
|
||||
setIsResetPage(false);
|
||||
handleSearchButtonClick();
|
||||
}, [curPage]);
|
||||
|
||||
// 조회 버튼 클릭 시 1페이지로 이동 및 데이터 초기화
|
||||
const handleSearch: () => Promise<void> = async () => {
|
||||
setCurPage(1); // 페이지를 1로 초기화
|
||||
setIsResetPage(true); // 페이지를 초기화
|
||||
await handleSearchButtonClick();
|
||||
};
|
||||
|
||||
const handleSearchButtonClick = async () => {
|
||||
setIsLoadingResult(true);
|
||||
setResetSelection(true);
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
|
||||
// swageTmplntCd 처리 로직 추가
|
||||
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
|
||||
@@ -104,34 +119,22 @@ const Result = () => {
|
||||
})()
|
||||
: null;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom,
|
||||
"aplcnYmTo": formValues.aplcnYmTo,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
"auth" : auth,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
"auth" : auth,
|
||||
};
|
||||
}
|
||||
let inftwtAnlsSearchDto = {
|
||||
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
|
||||
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swageTmplntCd": processedSwageTmplntCd,
|
||||
// "swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
"auth": AUTH,
|
||||
};
|
||||
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
page: `${curPage - 1}`,
|
||||
size: '10',
|
||||
sort: 'inftwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
@@ -144,6 +147,7 @@ const Result = () => {
|
||||
console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
console.log("API response:", data);
|
||||
|
||||
// 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가
|
||||
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => {
|
||||
@@ -153,17 +157,16 @@ const Result = () => {
|
||||
// 추가 처리 및 일련번호 추가
|
||||
return {
|
||||
...formattedItem,
|
||||
rowNumber: index + 1, // 1부터 시작하는 일련번호 추가
|
||||
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
|
||||
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
|
||||
setTableData(dataWithRowNumber);
|
||||
setTotalData(dataWithRowNumber.length);
|
||||
setTotalData(data?.totalElements || 0); // totalElements가 없을 경우 0으로 설정
|
||||
console.log("response:", data);
|
||||
console.log("response data:", dataWithRowNumber);
|
||||
setIsLoadingResult(false);
|
||||
@@ -174,7 +177,6 @@ const Result = () => {
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
}
|
||||
setResetSelection(false);
|
||||
};
|
||||
@@ -183,7 +185,6 @@ const Result = () => {
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
@@ -262,17 +263,8 @@ const Result = () => {
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYmFrom: selectedRowData.aplcnYmFrom || null,
|
||||
aplcnYmTo: selectedRowData.aplcnYmTo || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swageTmplntCd: selectedRowData.swageTmplntCd || null,
|
||||
// swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
// 검색 버튼 클릭 이벤트 트리거
|
||||
await handleSearchButtonClick();
|
||||
|
||||
setIsLoadingResult(false);
|
||||
|
||||
@@ -287,10 +279,9 @@ const Result = () => {
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearch }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: '분석이력' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
@@ -316,8 +307,8 @@ const Result = () => {
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ 침입수 산정결과 현황</div>
|
||||
{[<div key='table1' className="h-[58vh] flex flex-col border rounded-lg bg-white shadow-md">
|
||||
{/* <div className="mb-[1.2rem] text-xl font-medium">○ 침입수 산정결과 현황</div> */}
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
@@ -325,10 +316,10 @@ const Result = () => {
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
onPageChange={setCurPage}
|
||||
resetSelection={resetSelection}
|
||||
resetPageNo={isResetPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user