Compare commits

..
Author SHA1 Message Date
mind-one003 c72fa450bc 엑셀다운로드 자연수까지 나오도록 array추가 2025-09-23 14:06:45 +09:00
mind-one003 400d6cf732 엑셀 숫자형식 다운로드 추가 2025-09-23 11:07:23 +09:00
swcho 39e2734a49 fix: searchBar.tsx 하수처리시설 검색 에러 수정 2025-09-22 09:54:35 +09:00
mind-one003 d92d4d3949 - 테이블 비율 고정 변수 추가(wideFixed)
- store headerlist 에서 비율 변수 추가(widePercent)
2025-09-17 16:15:11 +09:00
mind-one003 b93efa5503 라인바, 라인차트 x축 라벨 크기 확대 2025-09-11 11:17:37 +09:00
swcho 0b8559891a remove: Gis.js에서 SearchView 컴포넌트 props 삭제 2025-09-09 09:54:40 +09:00
mind-one003 ffa1781005 라인바 차트 좌측 여백 및 라벨 좌측으로 좀 더 이동 2025-09-05 14:21:39 +09:00
mind-one003 b47c0dff2b 라인바 차트 크기 수정, custom left 축 만드는 로직 추가 2025-09-05 13:49:22 +09:00
mind-one003 83f5d878e7 라인바 y축 범위 수정 2025-09-02 16:44:43 +09:00
swcho e815b9662f remove: comboSearchBar index 삭제 2025-09-02 16:00:49 +09:00
swcho 2e6cbad49c remove, refactor:
- comboSearchBar.tsx 삭제
- any 타입 추가
- procSearchBar 시군구, 시설 가나다순 정렬
2025-09-02 15:52:13 +09:00
swcho ede5ef6a21 refactor: GisTree.js 주석 추가 및 최신화 2025-09-02 11:06:37 +09:00
swcho 621d222799 remove: GisTree, Gis 에서 orderedLayers porps 삭제 2025-09-02 10:55:16 +09:00
mind-one003 01df37d0b3 라인차트 호출 시 config color에 dash붙을시 해당색상의 대시라인 생성 2025-09-01 17:01:18 +09:00
swcho 072a5337a3 Merge branch 'iipbs/common/main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/common/main 2025-09-01 13:07:49 +09:00
swcho 2a0f5dce25 remove: 상류관로 보기 함수 삭제 2025-09-01 13:07:46 +09:00
mind-one003 26f2c60f4c 라인바 차트
- 데이터가 NaN이거나 null인 경우 툴팁에서 제외
- NULL인 경우 0으로 라인을 그리는 현상 다음 있는데이터로 라인을 그리도록 수정
2025-08-29 14:59:10 +09:00
swcho 28d0e21815 feat: UiTable에 페이지 번호 props 추가 2025-08-28 17:32:52 +09:00
swcho 49c475709f feat:
- 시설 검색, 다중 시설 검색 시 이전 검색 시설 레이어 삭제 기능 추가
- 시설 위치 정보 없을 때 alert 추가
- 시설 검색 시 해당 시설 아이콘 이미지 표출
2025-08-28 17:11:35 +09:00
mind-one003 31b8152d37 차트 좌우 axis custom 추가 2025-08-27 14:56:02 +09:00
swcho b9f5eb50e5 feat: UiTable 내부 셀 줄바꿈 처리 pre-line 적용 2025-08-26 18:42:36 +09:00
mind-one003 c7772a0a23 빌드에러 수정 2025-08-26 17:50:50 +09:00
mind-one003 392ccefb16 라인바 차트property 추가 (setXAxisYearTick - 날짜 x축일경우 (년도-월)별로 만들어주는 속성) 2025-08-26 17:47:51 +09:00
mind-one003 aeaf507c47 차트property 추가 (setXAxisYearTick - 날짜 x축일경우 (년도-월)별로 만들어주는 속성) 2025-08-26 17:39:07 +09:00
mind-one003 2f4cfa0d78 바차트 툴팁 숫자의 경우 천자리 쉼포 추가 2025-08-26 13:30:24 +09:00
swcho ebfc520d67 feat:
- 여러 개의 검색 결과를 GeoJSON으로 표시 함수 추가
- 피쳐 ID로 지오서버에서 검색해서 여러개 피처의 벡터소스 리턴하는 함수 추가
- 여러 extent 배열을 받아 중앙값 extent를 반환하는 함수 추가
2025-08-20 18:50:08 +09:00
swcho 4df286b086 feat: UiTable 컴포넌트에 headButton props 추가 2025-08-19 15:24:52 +09:00
swcho 262cf21546 remove: 잘못 추가된 물재이용(wr)의 가뭄지도 팝업 페이지 삭제 2025-08-13 17:20:40 +09:00
swcho 056b7bae3c fix: defaultZoom props 수정 2025-08-13 16:31:01 +09:00
swcho 428f617802 fix: defaultZoom props 수정 2025-08-13 16:20:15 +09:00
swcho e797bdbdf7 feat: Gis.js defaultZoom, defaultLonLat props 추가 2025-08-13 16:11:33 +09:00
mind-one005 cb11ec839e UiModal <div> <> 2025-08-13 15:50:23 +09:00
swcho 772dd1c1aa refactor: 시도, 시군구 순서 Custom 정렬 2025-08-13 15:40:07 +09:00
swcho 579e599eb1 feat searchBar 시설명 가나다 순으로 정렬 2025-08-11 18:23:05 +09:00
swcho 3cc8fcb2eb fix: 오타 수정 2025-08-09 23:46:20 +09:00
swcho f644bd6b7e refactor, remove, add: GIS main 브랜치와 동기화 2025-08-09 23:43:37 +09:00
swcho f685884ed7 feat: WR(물재이용) 가뭄지도 팝업 구현 을 위한 Gis 수정 2025-08-09 22:38:02 +09:00
swcho a2af60d52e fix: NoFooterLayout 사용으로 인한 Gis.js의 default height 수정 2025-08-08 13:34:58 +09:00
swcho 395590bbe4 feat: gis.layer.js 최신화 2025-08-06 20:59:56 +09:00
swcho 6c0d65a69a feat: Gis Search 버튼 hover시에 표시되는 title 추가 2025-08-06 09:50:50 +09:00
swcho a6b34780ae feat: 침수심 범례에 진행바(Progress Bar) 추가 2025-08-05 18:11:09 +09:00
swcho a83ea455fb feat: GisTree 레이어 Check 함수 구현 (침수심 범례 기능) 2025-08-05 16:35:03 +09:00
swcho ab63cbe992 feat: Gis.js에 침수심 범례 추가 2025-08-05 10:55:47 +09:00
swcho b2a6285475 feat: UiModal 컴포넌트에 headerTitleClass props 추가 2025-08-04 18:41:35 +09:00
swcho 6808430c9a feat: UITable 컴포넌트의 index props를 통해 외부에서 내부 select idx를 제어할 수 있도록 변경 2025-07-31 16:09:36 +09:00
Your Name dad8b9925e monthpicker enableMonth string[] 파라미터 추가
(선택가능한 월 배열 추가하면됨 ex.['4월', '5월'])
2025-07-28 11:12:27 +09:00
swcho 07c7947da1 fix: gis.layer.js 하수펌프장 CQL 필터 적용 안되는 에러 수정 2025-07-28 10:51:37 +09:00
swcho 35e1203168 fix: monthPicker.tsx 현재 월에 hover 효과가 지속되는 코드 주석 처리 2025-07-28 10:34:35 +09:00
mind-one005 bec3132417 SelectBox labelClass props 추가 2025-07-27 17:13:43 +09:00
Your Name e7a46ac3a2 년월 캘린더 년 readonly 옵션추가 (not necessary) 2025-07-27 14:43:42 +09:00
jyha00 6fb39d6384 UiTable index 수정 2025-07-26 19:25:19 +09:00
jyha00 0ad7d6a2c4 searchBaseForm 버튼 순서 변경 파라미터 추가 2025-07-26 15:58:47 +09:00
mind-one005 8fbecc41a5 LineBarChart2 비율대로 보이도록 2025-07-26 15:03:09 +09:00
leedj312 711adc2e17 폼 오류 수정 2025-07-25 18:45:31 +09:00
JIeunhuh 4920b4c1a2 수요처 추가 2025-07-25 18:35:57 +09:00
leedj312 c4f2af0bfe rgtr nullish 작업 2025-07-25 16:58:52 +09:00
Your Name 190adb6bc1 테이블 헤더 위치 좀더 위로 수정 2025-07-25 14:30:59 +09:00
Your Name 59bccfe1d6 테이블 및 굵은영역 뒷쪽에 글자보이는 문제해결 2025-07-25 14:03:52 +09:00
mind-one005 84ff24c5b5 LineBarChart2 추가 2025-07-25 10:52:47 +09:00
mind-one005 e1b48ab7c8 Merge branch 'iipbs/common/main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/common/main 2025-07-25 10:44:19 +09:00
mind-one005 4c3be448f0 LineBarChar2 추가 2025-07-25 10:42:36 +09:00
jyha00 8ede32f4c4 수요처폼 글씨 크기 변경 2025-07-25 10:41:41 +09:00
swcho 2862fdc9fd fix: Gis.js fetch 로직 에러 수정 2025-07-25 00:50:03 +09:00
swcho a446739432 feat:
- gis.select.js 피처 다중 셀렉트 기능 추가
- Gis.js 상류관로 조회 메서드 추가
- GisLonLat 주석 처리(위경도 표시X)
2025-07-25 00:23:02 +09:00
mind-one005 ea125605d1 Merge branch 'iipbs/common/main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/common/main 2025-07-24 22:01:01 +09:00
mind-one005 8353800dac feat: LineBarChar.tsx Null 체크 추가 2025-07-24 22:00:58 +09:00
swcho 6492ad5500 fix: Gis.js featureId 찾는 로직 수정 2025-07-24 20:01:41 +09:00
jyha00 523925459c 바차트 포매터 적용 2025-07-24 19:28:20 +09:00
swcho d5c967e9ad feat, remove:
- SelectBox 컴포넌트에 selectContentClass props 추가 (드롭다운에 z index 값을 추가 하기 위함)
- SelectBox2 컴포넌트 삭제
2025-07-23 17:45:06 +09:00
swcho 804688b3c1 remove: UITable2 컴포넌트 삭제 2025-07-22 16:28:48 +09:00
swcho 8435096e5a remove: UITab2 컴포넌트 삭제 2025-07-22 16:16:42 +09:00
378 changed files with 15132 additions and 19249 deletions
View File
@@ -185,15 +185,6 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) {
...prev,
[siteId]: value,
}));
// 상시 선택 시 게시 기간 미설정
if (value === "99") {
handleUpdateFields(siteId)({
pstgPrdSeCd: value,
});
return;
}
setRememberPeriodDate((prev) => ({
...prev,
[siteId]: { from, to },
@@ -20,7 +20,7 @@ import {
Skeleton,
} from "@common/components";
import { AttachmentApi } from "@/entities/attachment";
import { PostApi, PostPageGuard, type PostParams } from "@/entities/post";
import { PostApi, type PostParams } from "@/entities/post";
import { useAlertStore, useMenuStore, useUserStore } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import dayjs from "dayjs";
@@ -103,147 +103,143 @@ function FaqPage({ bbsId }: { bbsId: string }) {
};
return (
<PostPageGuard bbsId={bbsId}>
<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 text-label">검색</Label>
<Select
value={postParams.keywordType}
onValueChange={(keywordType) => {
setPostParams({ ...postParams, keywordType });
}}
>
<SelectTrigger className="h-[3.2rem] w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="title">제목</SelectItem>
<SelectItem value="all">제목+내용</SelectItem>
<SelectItem value="writer">작성자</SelectItem>
</SelectContent>
</Select>
<Input
value={postParams.keyword}
onChange={(e) =>
setPostParams({ ...postParams, keyword: e.target.value })
}
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
<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 text-label">검색</Label>
<Select
value={postParams.keywordType}
onValueChange={(keywordType) => {
setPostParams({ ...postParams, keywordType });
}}
>
<SelectTrigger className="h-[3.2rem] w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="title">제목</SelectItem>
<SelectItem value="all">제목+내용</SelectItem>
<SelectItem value="writer">작성자</SelectItem>
</SelectContent>
</Select>
<Input
value={postParams.keyword}
onChange={(e) =>
setPostParams({ ...postParams, keyword: e.target.value })
}
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</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">
{pageMenuAuthority?.regAuthrtYn === "Y" && (
<Button onClick={() => router.push(`${pathname}/register`)}>
등록
</Button>
)}
{pageMenuAuthority?.delAuthrtYn === "Y" && (
<Button
color="red"
onClick={onSelectedPostDelete}
disabled={!selectedPostNumbers.length}
>
선택삭제
</Button>
)}
</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">
{pageMenuAuthority?.regAuthrtYn === "Y" && (
<Button onClick={() => router.push(`${pathname}/register`)}>
등록
</Button>
)}
{pageMenuAuthority?.delAuthrtYn === "Y" && (
<Button
color="red"
onClick={onSelectedPostDelete}
disabled={!selectedPostNumbers.length}
<div className="card !m-[1.2rem] h-full overflow-auto !p-0">
<div className="mb-4 flex items-center gap-2 rounded-sm border border-border bg-background p-4">
<Checkbox
id="all-select"
checked={selectedPostNumbers.length === postList?.content.length}
onClick={(e) => e.stopPropagation()}
onCheckedChange={handleAllCheckboxChange}
/>
<Label htmlFor="all-select" className="text-2xl">
전체선택
</Label>
</div>
{isFetched ? (
<Accordion
type="multiple"
className="secondary flex flex-col gap-4"
>
{postList?.content.map((item) => (
<div key={item.pstNo} className="flex items-start gap-2">
<Checkbox
id={item.pstNo.toString()}
checked={selectedPostNumbers.includes(item.pstNo)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) =>
handleCheckboxChange(item.pstNo, checked)
}
className="mx-2 my-6"
/>
<AccordionItem
value={item.pstNo.toString()}
className="w-full"
>
선택삭제
</Button>
)}
</div>
</div>
</div>
<div className="card !m-[1.2rem] h-full overflow-auto !p-0">
<div className="mb-4 flex items-center gap-2 rounded-sm border border-border bg-background p-4">
<Checkbox
id="all-select"
checked={
selectedPostNumbers.length === postList?.content.length
}
onClick={(e) => e.stopPropagation()}
onCheckedChange={handleAllCheckboxChange}
/>
<Label htmlFor="all-select" className="text-2xl">
전체선택
</Label>
</div>
{isFetched ? (
<Accordion
type="multiple"
className="secondary flex flex-col gap-4"
>
{postList?.content.map((item) => (
<div key={item.pstNo} className="flex items-start gap-2">
<Checkbox
id={item.pstNo.toString()}
checked={selectedPostNumbers.includes(item.pstNo)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) =>
handleCheckboxChange(item.pstNo, checked)
}
className="mx-2 my-6"
/>
<AccordionItem
value={item.pstNo.toString()}
className="w-full"
>
<AccordionTrigger>
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center gap-4">
<span className="text-primary">Q</span>
<span>{item.pstTtl}</span>
</div>
<span className="px-4 text-[1.2rem] font-normal text-label">
등록일: {dayjs(item.regDt).format("YYYY-MM-DD")}
</span>
<AccordionTrigger>
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center gap-4">
<span className="text-primary">Q</span>
<span>{item.pstTtl}</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="flex items-start gap-4">
<span className="text-[1.4rem] font-bold text-label">
A
</span>
<div className="flex-1">
<pre className="whitespace-pre-wrap font-noto text-[1.4rem] tracking-tighter text-foreground">
{item.pstCn}
</pre>
<Attachment atflCd={item.atflCd} />
</div>
<div className="flex justify-end gap-2">
<Button
size="icon"
color="white"
onClick={() =>
router.push(`${pathname}/${item.pstNo}`)
}
>
<i className="diveicon di-pencil" />
</Button>
<Button
size="icon"
color="white"
onClick={() => handleDeletePost(item.pstNo)}
>
<i className="diveicon di-trash" />
</Button>
</div>
<span className="px-4 text-[1.2rem] font-normal text-label">
등록일: {dayjs(item.regDt).format("YYYY-MM-DD")}
</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="flex items-start gap-4">
<span className="text-[1.4rem] font-bold text-label">
A
</span>
<div className="flex-1">
<pre className="whitespace-pre-wrap font-noto text-[1.4rem] tracking-tighter text-foreground">
{item.pstCn}
</pre>
<Attachment atflCd={item.atflCd} />
</div>
</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
) : (
<Skeleton className="h-[36rem] w-full" />
)}
</div>
<div className="flex justify-end gap-2">
<Button
size="icon"
color="white"
onClick={() =>
router.push(`${pathname}/${item.pstNo}`)
}
>
<i className="diveicon di-pencil" />
</Button>
<Button
size="icon"
color="white"
onClick={() => handleDeletePost(item.pstNo)}
>
<i className="diveicon di-trash" />
</Button>
</div>
</div>
</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
) : (
<Skeleton className="h-[36rem] w-full" />
)}
</div>
</div>
</PostPageGuard>
</div>
);
}
+8 -27
View File
@@ -1,12 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import {
CLAIM_NAME,
AUTH_USER_ID,
PORT,
PROXY_HOST,
SERVER_API_URL,
SITE_ID,
} from "./src/shared/config";
import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
const serverApiUrl =
process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:3000";
@@ -14,23 +7,17 @@ const serverApiUrl =
export function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
const searchParams = req.nextUrl.searchParams;
const logMessages: string[] = [
`[Middleware Request] ${new Date().toISOString()}`,
`[Environment]\n Mode: ${process.env.NODE_ENV}\n Port: ${PORT}\n Proxy Host: ${PROXY_HOST}\n Server API URL: ${SERVER_API_URL}\n Site ID: ${SITE_ID}`,
`[Request]\n Original Pathname: ${pathname}`,
];
if (pathname.startsWith("/server")) {
const queryParams = searchParams.toString();
const decodedParams = Array.from(searchParams.entries())
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
.join("&");
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : ""
decodedParams ? `?${decodedParams}` : ""
}`;
logMessages.push(` Rewritten URL: ${newUrl}`);
logMessages.push("\n--------------------------------\n");
console.log(logMessages.join("\n"));
return NextResponse.rewrite(new URL(newUrl));
}
@@ -43,10 +30,6 @@ export function middleware(req: NextRequest) {
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
logMessages.push(` Rewritten URL: ${newUrl}`);
logMessages.push("\n--------------------------------\n");
console.log(logMessages.join("\n"));
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
@@ -56,8 +39,6 @@ export function middleware(req: NextRequest) {
return NextResponse.redirect(new URL("/dashboard", req.url));
}
logMessages.push("\n--------------------------------\n");
console.log(logMessages.join("\n"));
return NextResponse.next();
}
@@ -67,22 +67,11 @@ export const contentsDetailSchema = z.object({
useYn: z.string(),
});
export const contentsFormSchema = contentsDetailSchema
.omit({
contsVer: true,
aprvDt: true,
autzr: true,
})
.extend({
contsSn: z.number().optional(),
aprvCd: z
.enum([
CONTENTS_APPROVE_STATUS.DRAFT,
CONTENTS_APPROVE_STATUS.REQUESTED,
CONTENTS_APPROVE_STATUS.APPROVED,
])
.optional(),
});
export const contentsFormSchema = contentsDetailSchema.omit({
contsVer: true,
aprvDt: true,
autzr: true,
});
export type Contents = z.infer<typeof contentsSchema>;
export type ContentsDetail = z.infer<typeof contentsDetailSchema>;
@@ -6,7 +6,6 @@ import type {
MenuItem,
MenuFormData,
MenuTemporarilyStoredData,
SiteMapFormData,
} from "../model/menu-interface";
export class MenuApiService implements MenuService {
@@ -66,14 +65,6 @@ export class MenuApiService implements MenuService {
);
return response.data;
}
public async publishSiteMap(data: SiteMapFormData["siteMap"]): Promise<void> {
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.MENUS}/sitemap/publish`,
data,
);
return response.data;
}
}
export const MenuApi = new MenuApiService();
-3
View File
@@ -2,11 +2,8 @@ export { MenuApi } from "./api/menu-service";
export {
MENU_TYPE,
menuFormSchema,
siteMapFormSchema,
type MenuType,
type MenuItem,
type MenuFormData,
type SiteMapFormData,
type SiteMapPayloadData,
} from "./model/menu-interface";
export { MenuWrapper } from "./lib/menu-wrapper";
@@ -72,19 +72,8 @@ export const menuFormSchema = z.object({
menuPicDsgnYmd: z.string().nullish(),
});
export const siteMapPayloadSchema = z.object({
menuCd: z.number(),
menuSubKornNm: z.string(),
otptYn: z.string(),
});
export const siteMapFormSchema = z.object({
siteMap: z.array(siteMapPayloadSchema),
});
export type MenuFormData = z.infer<typeof menuFormSchema>;
export type SiteMapFormData = z.infer<typeof siteMapFormSchema>;
export type SiteMapPayloadData = z.infer<typeof siteMapPayloadSchema>;
export type Menu = Omit<MenuFormData, "iconNm"> & {
menuCd: number;
iconName?: string;
@@ -103,5 +92,4 @@ export interface MenuService {
getMenuTemporarilyStoredData: (
menuCd: number,
) => Promise<MenuTemporarilyStoredData[]>;
publishSiteMap: (data: SiteMapFormData["siteMap"]) => Promise<void>;
}
@@ -194,15 +194,6 @@ function PostingDialog({
...prev,
[siteId]: value,
}));
// 상시 선택 시 게시 기간 미설정
if (value === "99") {
handleUpdateFields(siteId)({
pstgPrdSeCd: value,
});
return;
}
setRememberPeriodDate((prev) => ({
...prev,
[siteId]: { from, to },
-2
View File
@@ -1,2 +0,0 @@
export { TreeMenuApi } from "./api/tree-menu-service";
export type { TreeMenu, TreeNode } from "./model/tree-menu-interface";
-1
View File
@@ -4,7 +4,6 @@ export {
userSaveSchema,
userPasswordUpdateSchema,
userUpdateSchema,
type UserStateCode,
type UserSearchParams,
type UserListResponse,
type UserSaveData,
@@ -9,8 +9,7 @@ export const USER_STATE_CODE = {
삭제: "09",
} as const;
export type UserStateCode =
(typeof USER_STATE_CODE)[keyof typeof USER_STATE_CODE];
type UserStateCode = (typeof USER_STATE_CODE)[keyof typeof USER_STATE_CODE];
// 사용자 검색 파라미터
export interface UserSearchParams extends PageableParams {
+133 -138
View File
@@ -23,7 +23,6 @@ import {
import { AttachmentApi } from "@/entities/attachment";
import {
PostApi,
PostPageGuard,
deleteReasonMessages,
type PostParams,
} from "@/entities/post";
@@ -121,147 +120,143 @@ function FaqPage({ bbsId }: { bbsId: string }) {
};
return (
<PostPageGuard bbsId={bbsId}>
<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 text-label">검색</Label>
<Select
value={postParams.keywordType}
onValueChange={(keywordType) => {
setPostParams({ ...postParams, keywordType });
}}
>
<SelectTrigger className="h-[3.2rem] w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="title">제목</SelectItem>
<SelectItem value="all">제목+내용</SelectItem>
<SelectItem value="writer">작성자</SelectItem>
</SelectContent>
</Select>
<Input
value={postParams.keyword}
onChange={(e) =>
setPostParams({ ...postParams, keyword: e.target.value })
}
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</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">
<AuthorityButton
authority="regAuthrtYn"
onClick={() => router.push(`${pathname}/register`)}
>
등록
</AuthorityButton>
<AuthorityButton
authority="delAuthrtYn"
color="red"
onClick={onSelectedPostDelete}
disabled={!selectedPostNumbers.length}
>
선택삭제
</AuthorityButton>
</div>
</div>
</div>
<div className="card !m-[1.2rem] h-full overflow-auto !p-0">
<div className="mb-4 flex items-center gap-2 rounded-sm border border-border bg-background p-4">
<Checkbox
id="all-select"
checked={
selectedPostNumbers.length === postList?.content.length
}
onClick={(e) => e.stopPropagation()}
onCheckedChange={handleAllCheckboxChange}
/>
<Label htmlFor="all-select" className="text-2xl">
전체선택
</Label>
</div>
{isFetched ? (
<Accordion
type="multiple"
className="secondary flex flex-col gap-4"
<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 text-label">검색</Label>
<Select
value={postParams.keywordType}
onValueChange={(keywordType) => {
setPostParams({ ...postParams, keywordType });
}}
>
<SelectTrigger className="h-[3.2rem] w-[10rem]">
<SelectValue placeholder="선택" />
</SelectTrigger>
<SelectContent>
<SelectItem value="title">제목</SelectItem>
<SelectItem value="all">제목+내용</SelectItem>
<SelectItem value="writer">작성자</SelectItem>
</SelectContent>
</Select>
<Input
value={postParams.keyword}
onChange={(e) =>
setPostParams({ ...postParams, keyword: e.target.value })
}
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</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">
<AuthorityButton
authority="regAuthrtYn"
onClick={() => router.push(`${pathname}/register`)}
>
{postList?.content.map((item) => (
<div key={item.pstNo} className="flex items-start gap-2">
<Checkbox
id={item.pstNo.toString()}
checked={selectedPostNumbers.includes(item.pstNo)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) =>
handleCheckboxChange(item.pstNo, checked)
}
className="mx-2 my-6"
/>
<AccordionItem
value={item.pstNo.toString()}
className="w-full"
>
<AccordionTrigger>
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center gap-4">
<span className="text-primary">Q</span>
<span>{item.pstTtl}</span>
</div>
<span className="px-4 text-[1.2rem] font-normal text-label">
등록일: {dayjs(item.regDt).format("YYYY-MM-DD")}
</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="flex items-start gap-4">
<span className="text-[1.4rem] font-bold text-label">
A
</span>
<div className="flex-1">
<pre className="whitespace-pre-wrap font-noto text-[1.4rem] tracking-tighter text-foreground">
{item.pstCn}
</pre>
<Attachment atflCd={item.atflCd} />
</div>
<div className="flex justify-end gap-2">
<Button
size="icon"
color="white"
onClick={() =>
router.push(`${pathname}/${item.pstNo}`)
}
>
<i className="diveicon di-pencil" />
</Button>
<Button
size="icon"
color="white"
onClick={() => handleDeletePost(item.pstNo)}
>
<i className="diveicon di-trash" />
</Button>
</div>
</div>
</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
) : (
<Skeleton className="h-[36rem] w-full" />
)}
등록
</AuthorityButton>
<AuthorityButton
authority="delAuthrtYn"
color="red"
onClick={onSelectedPostDelete}
disabled={!selectedPostNumbers.length}
>
선택삭제
</AuthorityButton>
</div>
</div>
</div>
<div className="card !m-[1.2rem] h-full overflow-auto !p-0">
<div className="mb-4 flex items-center gap-2 rounded-sm border border-border bg-background p-4">
<Checkbox
id="all-select"
checked={selectedPostNumbers.length === postList?.content.length}
onClick={(e) => e.stopPropagation()}
onCheckedChange={handleAllCheckboxChange}
/>
<Label htmlFor="all-select" className="text-2xl">
전체선택
</Label>
</div>
{isFetched ? (
<Accordion
type="multiple"
className="secondary flex flex-col gap-4"
>
{postList?.content.map((item) => (
<div key={item.pstNo} className="flex items-start gap-2">
<Checkbox
id={item.pstNo.toString()}
checked={selectedPostNumbers.includes(item.pstNo)}
onClick={(e) => e.stopPropagation()}
onCheckedChange={(checked) =>
handleCheckboxChange(item.pstNo, checked)
}
className="mx-2 my-6"
/>
<AccordionItem
value={item.pstNo.toString()}
className="w-full"
>
<AccordionTrigger>
<div className="flex flex-1 items-center justify-between">
<div className="flex items-center gap-4">
<span className="text-primary">Q</span>
<span>{item.pstTtl}</span>
</div>
<span className="px-4 text-[1.2rem] font-normal text-label">
등록일: {dayjs(item.regDt).format("YYYY-MM-DD")}
</span>
</div>
</AccordionTrigger>
<AccordionContent>
<div className="flex items-start gap-4">
<span className="text-[1.4rem] font-bold text-label">
A
</span>
<div className="flex-1">
<pre className="whitespace-pre-wrap font-noto text-[1.4rem] tracking-tighter text-foreground">
{item.pstCn}
</pre>
<Attachment atflCd={item.atflCd} />
</div>
<div className="flex justify-end gap-2">
<Button
size="icon"
color="white"
onClick={() =>
router.push(`${pathname}/${item.pstNo}`)
}
>
<i className="diveicon di-pencil" />
</Button>
<Button
size="icon"
color="white"
onClick={() => handleDeletePost(item.pstNo)}
>
<i className="diveicon di-trash" />
</Button>
</div>
</div>
</AccordionContent>
</AccordionItem>
</div>
))}
</Accordion>
) : (
<Skeleton className="h-[36rem] w-full" />
)}
</div>
</div>
</PostPageGuard>
</div>
);
}
+1 -1
View File
@@ -38,7 +38,7 @@ function AdminPage() {
const alert = useAlertStore((state) => state.setMessage);
const { confirm } = useConfirm();
const [keywordType, setKeywordType] = useState("userId");
const [keywordType, setKeywordType] = useState("");
const [keyword, setKeyword] = useState("");
const [grpId, setGrpId] = useState("");
const [searchParams, setSearchParams] = useState<UserSearchParams>({
+2 -1
View File
@@ -15,7 +15,8 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import { ApiLogApi, type ApiLogParams } from "@/entities/apilog";
import { ApiLogApi } from "@/entities/apilog";
import { type ApiLogParams } from "@/entities/apilog/model/apilog-interface";
import { useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { columns } from "@/pages/apilog/model/table-columns";
@@ -35,14 +35,9 @@ export const columns: ColumnDef<Post>[] = [
accessorKey: "pstgPrdSeNm",
header: "게시기간",
cell: ({ row }) => {
const pstgPrdSeCd = row.original.pstgPrdSeCd;
const pstgPrdSeNm = row.original.pstgPrdSeNm;
const pstgBgngDt = row.original.pstgBgngDt;
const pstgEndDt = row.original.pstgEndDt;
if (pstgPrdSeCd === "99") {
return pstgPrdSeNm;
}
return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`;
},
},
@@ -34,7 +34,7 @@ function CreatePage() {
<div className="card-header">
<h3 className="text-2xl font-medium text-label">콘텐츠 등록</h3>
</div>
<ContentsForm isEditable form={form} handleSave={createContents} />
<ContentsForm form={form} handleSave={createContents} />
</div>
);
}
+7 -13
View File
@@ -1,6 +1,5 @@
"use client";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
@@ -25,7 +24,6 @@ import { useAlertStore, useMutationWrapper } from "@/shared/lib";
import { ContentsForm } from "./contents-form";
function ModifyPage({ sequential }: { sequential: string }) {
const router = useRouter();
const { confirm } = useConfirm();
const queryClient = useQueryClient();
const alert = useAlertStore((state) => state.setMessage);
@@ -53,11 +51,9 @@ function ModifyPage({ sequential }: { sequential: string }) {
mutationFn: (data: ContentsFormData) => ContentsApi.createContents(data),
onSuccess: () => {
setContsVer(0);
alert("수정되었습니다.", () => {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
});
router.back();
alert("수정되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
});
},
});
@@ -66,7 +62,7 @@ function ModifyPage({ sequential }: { sequential: string }) {
mutationFn: (data: ContentsFormData) =>
ContentsApi.updateContents(contsSn, contsVer, data),
onSuccess: () => {
alert("수정되었습니다.", router.back);
alert("수정되었습니다.");
},
});
@@ -74,11 +70,9 @@ function ModifyPage({ sequential }: { sequential: string }) {
mutationFn: () => ContentsApi.deleteContents(contsSn, contsVer),
onSuccess: () => {
setContsVer(0);
alert("삭제되었습니다.", () => {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
});
router.back();
alert("삭제되었습니다.");
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS, contsSn],
});
},
});
+2 -1
View File
@@ -39,7 +39,8 @@ function FilePage() {
mutationFn: (atflId: string) =>
AttachmentApi.deleteAttachmentByAtflId(atflId),
onSuccess: () => {
alert("삭제되었습니다.", query.refetch);
query.refetch();
alert("삭제되었습니다.");
},
});
+20 -56
View File
@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { useState } from "react";
import {
keepPreviousData,
useQuery,
@@ -22,7 +22,6 @@ import {
DialogFooter,
Input,
Checkbox,
Label,
} from "@common/components";
import {
UserApi,
@@ -42,8 +41,7 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
const alert = useAlertStore((state) => state.setMessage);
const [open, setOpen] = useState(false);
const [userName, setUserName] = useState("");
const [searchParam, setSearchParam] = useState<UserSearchParams>({
const [userSearchQuery, setUserSearchQuery] = useState<UserSearchParams>({
userName: "",
});
const [pagination, setPagination] = useState<PaginationState>({
@@ -55,10 +53,10 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
);
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS, searchParam, pagination],
queryKey: [ENDPOINT.USER_SERVICE.USERS, userSearchQuery, pagination],
queryFn: () =>
UserApi.findPageUser({
...searchParam,
...userSearchQuery,
page: pagination.pageIndex,
size: pagination.pageSize,
}),
@@ -120,9 +118,6 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
onRowSelectionChange: setUserRowSelection,
getRowId: (row) => row.userUnqId,
},
dataTableOptions: {
viewPagination: true,
},
});
const handleAddUsers = async () => {
@@ -149,30 +144,10 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
}
};
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
e.stopPropagation();
setPagination({
...pagination,
pageIndex: 0,
});
setSearchParam({ userName });
const handleSearch = () => {
setPagination({ pageIndex: 0, pageSize: 10 });
};
const resetSearch = () => {
setPagination({
pageIndex: 0,
pageSize: 10,
});
setUserName("");
setSearchParam({ userName: "" });
setUserRowSelection({});
};
useEffect(() => {
if (open) resetSearch();
}, [open]);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
@@ -182,31 +157,20 @@ function AddUserDialog({ groupId }: AdditionalUserDialogProps) {
<DialogHeader>
<DialogTitle>사용자 추가</DialogTitle>
</DialogHeader>
<form onSubmit={handleSearch} className="mb-4 flex items-end gap-2">
<div className="flex items-center gap-2">
<Label htmlFor="userName" className="mr-2 text-label">
사용자 이름
</Label>
<Input
id="userName"
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
value={userName}
onChange={(e) => setUserName(e.target.value)}
placeholder="사용자 이름으로 검색"
/>
</div>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
<Button
type="button"
color="white"
className="h-[3.2rem]"
onClick={resetSearch}
>
초기화
</Button>
</form>
<div className="mb-4 flex items-end gap-2">
<Input
value={userSearchQuery.userName}
onChange={(e) =>
setUserSearchQuery({
...userSearchQuery,
userName: e.target.value,
})
}
className="!text-[1.3rem]"
placeholder="사용자 이름으로 검색"
/>
<Button onClick={handleSearch}>검색</Button>
</div>
{tableComponent}
<DialogFooter>
<Button color="white" onClick={() => setOpen(false)}>
@@ -35,14 +35,9 @@ export const columns: ColumnDef<Post>[] = [
accessorKey: "pstgPrdSeNm",
header: "게시기간",
cell: ({ row }) => {
const pstgPrdSeCd = row.original.pstgPrdSeCd;
const pstgPrdSeNm = row.original.pstgPrdSeNm;
const pstgBgngDt = row.original.pstgBgngDt;
const pstgEndDt = row.original.pstgEndDt;
if (pstgPrdSeCd === "99") {
return pstgPrdSeNm;
}
return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`;
},
},
@@ -16,7 +16,8 @@ import {
SelectValue,
LoadingWithImage,
} from "@common/components";
import { LoginlogApi, type LoginlogParams } from "@/entities/loginlog";
import { LoginlogApi } from "@/entities/loginlog";
import { type LoginlogParams } from "@/entities/loginlog/model/loginlog-interface";
import { useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { columns } from "@/pages/loginlog/model/table-columns";
+1 -1
View File
@@ -3,7 +3,7 @@ import type {
TreeUserOrganization,
TreeUser,
} from "@/entities/user-organization";
import type { TreeMenu, TreeNode } from "@/entities/tree-menu";
import type { TreeMenu, TreeNode } from "../model/tree-menu-interface";
export const generateMenuTreeData = (
data: TreeMenu[],
@@ -19,7 +19,6 @@ export interface TreeMenu extends Omit<TreeNode, "name" | "icon" | "children"> {
qrcd?: string;
useYn: string;
otptYn: string;
menuSubKornNm?: string;
requiredUrlPath?: boolean;
}
+16 -29
View File
@@ -1,6 +1,5 @@
import type { ComponentProps } from "react";
import { Button, Input } from "@common/components";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
type EditableNodeProps = {
depth?: number;
@@ -21,34 +20,22 @@ function EditableNode({
>
<Input {...props} className="h-[2.5rem] !text-[1.3rem] text-foreground" />
<div className="flex items-center gap-1">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onSave}
aria-label="저장"
>
<i className="diveicon di-check text-2xl" />
</Button>
</TooltipTrigger>
<TooltipContent>저장</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onCancel}
aria-label="취소"
>
<i className="diveicon di-x" />
</Button>
</TooltipTrigger>
<TooltipContent>취소</TooltipContent>
</Tooltip>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onSave}
>
<i className="diveicon di-check text-2xl" />
</Button>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onCancel}
>
<i className="diveicon di-x" />
</Button>
</div>
</div>
);
+2 -1
View File
@@ -2,9 +2,10 @@ import { useEffect, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import type { NodeModel, TreeMethods } from "@minoru/react-dnd-treeview";
import { Button, cn } from "@common/components";
import { TreeMenuApi, type TreeMenu } from "@/entities/tree-menu";
import { useMutationWrapper } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { TreeMenuApi } from "../api/tree-menu-service";
import type { TreeMenu } from "../model/tree-menu-interface";
import { EditableNode } from "./editable-node";
type MenuNodeProps = {
+2 -1
View File
@@ -21,10 +21,11 @@ import {
type MenuFormData,
} from "@/entities/menu";
import { SiteApi } from "@/entities/site";
import { TreeMenuApi, type TreeMenu } from "@/entities/tree-menu";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
import { generateMenuTreeData } from "../lib/utils";
import { TreeMenuApi } from "../api/tree-menu-service";
import type { TreeMenu } from "../model/tree-menu-interface";
import { MenuTree } from "./menu-tree";
import { MenuForm } from "./menu-form";
+2 -5
View File
@@ -12,11 +12,8 @@ import { Button } from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { useMutationWrapper } from "@/shared/lib";
import type { MenuFormData } from "@/entities/menu";
import {
TreeMenuApi,
type TreeMenu,
type TreeNode,
} from "@/entities/tree-menu";
import { TreeMenuApi } from "../api/tree-menu-service";
import type { TreeMenu, TreeNode } from "../model/tree-menu-interface";
import { handleDropNode, handleSelectNode } from "../lib/utils";
import { MenuNode } from "./menu-node";
import { EditableNode } from "./editable-node";
@@ -72,7 +72,7 @@ function AddUserDialog({ onAddUsers, ...props }: AddUserDialogProps) {
</DialogTrigger>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>사용자 조회</DialogTitle>
<DialogTitle>콘텐츠 조회</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-4">
<div className="card card-border !m-0 flex w-full items-center gap-2">
@@ -242,16 +242,6 @@ function UserOrganizationSection({ ognzSn }: UserOrganizationSectionProps) {
</div>
<div className="card max-h-[calc(100vh-58rem)] overflow-auto !p-0">
<Table>
<colgroup>
<col width="5%" />
<col width="5%" />
<col width="auto" />
<col width="10%" />
<col width="10%" />
<col width="10%" />
<col width="15%" />
<col width="15%" />
</colgroup>
<TableHeader className="sticky top-0">
<TableRow>
<TableHead>
@@ -310,9 +300,6 @@ const UserCells = memo(
jobPositionCodeData,
jobGradeCodeData,
}: UserCellsProps) => {
const [jbpsSeCd, setJbpsSeCd] = useState(user.jbpsSeCd);
const [jbgdSeCd, setJbgdSeCd] = useState(user.jbgdSeCd);
const updateMode = (index: number) => {
const mode = form.watch(`userOrganization.${index}.mode`);
if (mode === "R") {
@@ -335,33 +322,17 @@ const UserCells = memo(
<CodeSelect
codeData={jobPositionCodeData}
defaultValue={user.jbpsSeCd}
onValueChange={(value) => {
setJbpsSeCd(value);
updateFormValue(index, "jbpsSeCd")(value);
}}
onValueChange={updateFormValue(index, "jbpsSeCd")}
{...form.register(`userOrganization.${index}.jbpsSeCd`)}
/>
{!jbpsSeCd && (
<p className="pt-1 text-base text-destructive">
직위를 선택해주세요.
</p>
)}
</TableCell>
<TableCell align="center">
<CodeSelect
codeData={jobGradeCodeData}
defaultValue={user.jbgdSeCd}
onValueChange={(value) => {
setJbgdSeCd(value);
updateFormValue(index, "jbgdSeCd")(value);
}}
onValueChange={updateFormValue(index, "jbgdSeCd")}
{...form.register(`userOrganization.${index}.jbgdSeCd`)}
/>
{!jbgdSeCd && (
<p className="pt-1 text-base text-destructive">
직급을 선택해주세요.
</p>
)}
</TableCell>
<TableCell align="center">
<Input
@@ -33,14 +33,9 @@ export const columns: ColumnDef<Post>[] = [
accessorKey: "pstgPrdSeNm",
header: "게시기간",
cell: ({ row }) => {
const pstgPrdSeCd = row.original.pstgPrdSeCd;
const pstgPrdSeNm = row.original.pstgPrdSeNm;
const pstgBgngDt = row.original.pstgBgngDt;
const pstgEndDt = row.original.pstgEndDt;
if (pstgPrdSeCd === "99") {
return pstgPrdSeNm;
}
return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`;
},
},
@@ -15,10 +15,8 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
import {
PrivacyLogPrivacy,
type PrivacyLogParams,
} from "@/entities/privacylog";
import { PrivacyLogPrivacy } from "@/entities/privacylog";
import type { PrivacyLogParams } from "@/entities/privacylog/model/privacylog-interface";
import { useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { columns } from "@/pages/privacylog/model/table-columns";
@@ -1,28 +0,0 @@
import { client } from "@/shared/api";
import type {
SiteAuthorityCreatePayload,
SiteAuthorityList,
SiteAuthoritySearchParams,
SiteAuthorityService,
} from "../model/site-authority-interface";
import { ENDPOINT } from "@/shared/config";
export class SiteAuthorityApiService implements SiteAuthorityService {
public async getSiteAuthorityList(
params: SiteAuthoritySearchParams,
): Promise<SiteAuthorityList> {
const response = await client.get<SiteAuthorityList>(
ENDPOINT.CMS_SERVICE.SITE_AUTHORITY,
{ params },
);
return response.data;
}
public async createSiteAuthority(
data: SiteAuthorityCreatePayload[],
): Promise<void> {
await client.post(ENDPOINT.CMS_SERVICE.SITE_AUTHORITY, data);
}
}
export const SiteAuthorityApi = new SiteAuthorityApiService();
@@ -1,24 +0,0 @@
import type { PageableList, PageableParams } from "@/shared/model";
export interface SiteAuthoritySearchParams extends PageableParams {
siteId?: string;
authrtId?: string;
}
export interface SiteAuthority {
siteId: string;
authrtId: string;
siteNm: string;
mode: string;
}
export type SiteAuthorityCreatePayload = Omit<SiteAuthority, "siteNm">;
export type SiteAuthorityList = PageableList<SiteAuthority>;
export interface SiteAuthorityService {
getSiteAuthorityList: (
params: SiteAuthoritySearchParams,
) => Promise<SiteAuthorityList>;
createSiteAuthority: (data: SiteAuthorityCreatePayload[]) => Promise<void>;
}
+2 -18
View File
@@ -3,13 +3,8 @@ import { useState } from "react";
import type { Role } from "@/entities/role";
import type { ColumnDef } from "@tanstack/react-table";
import { MappingRoleDialog } from "../ui/mapping-role-dialog";
import { Button } from "@common/components";
export const createColumns = ({
deleteRole,
}: {
deleteRole: (roleId: string) => void;
}): ColumnDef<Role>[] => [
export const columns: ColumnDef<Role>[] = [
{
id: "no",
header: "NO",
@@ -44,18 +39,7 @@ export const createColumns = ({
{
id: "action",
header: "관리",
cell: ({ row }) => (
<div className="flex items-center justify-center gap-2">
<MappingRoleButton roleId={row.original.roleId} />
<Button
size="sm"
color="red"
onClick={() => deleteRole(row.original.roleId)}
>
삭제
</Button>
</div>
),
cell: ({ row }) => <MappingRoleButton roleId={row.original.roleId} />,
},
];
@@ -1,6 +1,5 @@
import { useState, useRef } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { getCoreRowModel, type ColumnDef } from "@tanstack/react-table";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import {
Dialog,
DialogTrigger,
@@ -9,7 +8,6 @@ import {
DialogTitle,
Button,
Switch,
Input,
} from "@common/components";
import {
RoleAuthorizationsApi,
@@ -33,18 +31,12 @@ function MappingRoleDialog({
}: MappingRoleDialogProps) {
const queryClient = useQueryClient();
const alert = useAlertStore((state) => state.setMessage);
const inputRef = useRef<HTMLInputElement>(null);
const [searchParams, setSearchParams] = useState({
keyword: "",
keywordType: "authorizationName",
});
const query = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS, searchParams],
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS],
queryFn: () =>
RoleAuthorizationsApi.getRoleAuthorizationsListByRoleId({
roleId,
...searchParams,
}),
enabled: open,
});
@@ -54,7 +46,7 @@ function MappingRoleDialog({
RoleAuthorizationsApi.createRoleAuthorizations(data),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.USER_SERVICE.ROLE_AUTHORIZATIONS],
queryKey: [ENDPOINT.USER_SERVICE.AUTHORIZATIONS],
});
alert("권한 맵핑이 완료되었습니다.");
},
@@ -74,26 +66,6 @@ function MappingRoleDialog({
);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
const keyword = formData.get("keyword") as string;
setSearchParams({
...searchParams,
keyword,
});
};
const handleReset = () => {
if (inputRef.current) {
inputRef.current.value = "";
}
setSearchParams({
...searchParams,
keyword: "",
});
};
const { tableComponent } = useDataTable({
query,
tableOptions: {
@@ -107,31 +79,21 @@ function MappingRoleDialog({
<DialogTrigger asChild>
<Button size="sm">권한맵핑</Button>
</DialogTrigger>
<DialogContent className="max-w-[80rem]" aria-describedby={undefined}>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>모듈 목록</DialogTitle>
</DialogHeader>
<div className="flex items-center justify-between">
<form onSubmit={handleSubmit} className="flex items-center gap-2">
<Input
ref={inputRef}
type="text"
name="keyword"
className="w-[32rem]"
/>
<Button type="submit">검색</Button>
<Button type="button" color="white" onClick={handleReset}>
초기화
<div className="max-h-[40rem] overflow-y-auto">
<div className="mb-4 flex justify-end gap-2">
<Button size="sm" onClick={() => handleApplyAll("Y")}>
일괄적용
</Button>
</form>
<div className="flex items-center justify-end gap-2">
<Button onClick={() => handleApplyAll("Y")}>일괄적용</Button>
<Button color="gray" onClick={() => handleApplyAll("N")}>
<Button size="sm" color="gray" onClick={() => handleApplyAll("N")}>
일괄해제
</Button>
</div>
{tableComponent}
</div>
<div className="max-h-[60rem] overflow-y-auto">{tableComponent}</div>
</DialogContent>
</Dialog>
);
+6 -87
View File
@@ -1,9 +1,7 @@
import { useEffect, useState } from "react";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import type { UseFormReturn } from "react-hook-form";
import {
Button,
Checkbox,
Input,
Label,
RadioGroup,
@@ -18,9 +16,6 @@ import { FormWrapper } from "@/widgets/form-wrapper";
import { RoleApi, type RoleFormData } from "@/entities/role";
import { FormField, FormItem, FormControl } from "@/shared/ui/form";
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { SiteAuthorityApi } from "../api/site-authority-service";
import type { SiteAuthorityCreatePayload } from "../model/site-authority-interface";
type RoleFormProps = {
roleId?: string;
@@ -29,24 +24,12 @@ type RoleFormProps = {
handleDelete?: () => Promise<void>;
};
function RoleForm({ roleId, form, handleSave, ...props }: RoleFormProps) {
function RoleForm({ roleId, form, ...props }: RoleFormProps) {
const isModifyMode = !!roleId;
const queryClient = useQueryClient();
const alert = useAlertStore((state) => state.setMessage);
const [isValidRoleId, setIsValidRoleId] = useState(false);
const [checkedSiteAuthority, setCheckedSiteAuthority] = useState<string[]>(
[],
);
const { data: siteAuthorityList } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITE_AUTHORITY, roleId],
queryFn: () =>
SiteAuthorityApi.getSiteAuthorityList({
authrtId: roleId,
page: 0,
size: 100,
}),
});
const alert = useAlertStore((state) => state.setMessage);
const [isValidRoleId, setIsValidRoleId] = useState(false);
const { mutateAsync: checkDuplicateRoleId } = useMutationWrapper({
mutationFn: (roleId: string) => RoleApi.checkDuplicateRoleId(roleId),
@@ -60,49 +43,8 @@ function RoleForm({ roleId, form, handleSave, ...props }: RoleFormProps) {
},
});
const handleSiteAuthorityChecked = (checked: boolean, siteId: string) => {
if (checked) {
setCheckedSiteAuthority((prev) => [...prev, siteId]);
} else {
setCheckedSiteAuthority((prev) => prev.filter((id) => id !== siteId));
}
};
const saveInterceptor = async (data: RoleFormData) => {
const siteAuthorityPayload: SiteAuthorityCreatePayload[] =
checkedSiteAuthority.map((siteId) => ({
siteId,
authrtId: data.roleId,
mode: "C",
}));
try {
await handleSave(data);
if (data.mngrYn === "N") return;
await SiteAuthorityApi.createSiteAuthority(siteAuthorityPayload);
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.SITE_AUTHORITY],
});
} catch (error) {
console.error(error);
}
};
useEffect(() => {
if (!siteAuthorityList?.content.length) return;
const checkedSiteAuthority = siteAuthorityList.content.filter(
(item) => item.mode === "R",
);
setCheckedSiteAuthority(checkedSiteAuthority.map((item) => item.siteId));
}, [siteAuthorityList]);
return (
<FormWrapper
form={form}
isModifyMode={isModifyMode}
handleSave={saveInterceptor}
{...props}
>
<FormWrapper form={form} isModifyMode={isModifyMode} {...props}>
<div className="card">
<Table variant="secondary">
<colgroup>
@@ -223,29 +165,6 @@ function RoleForm({ roleId, form, handleSave, ...props }: RoleFormProps) {
/>
</TableCell>
</TableRow>
{form.watch("mngrYn") === "Y" && (
<TableRow>
<TableHead data-required>관리사이트</TableHead>
<TableCell className="border">
<div className="flex flex-wrap items-center gap-4">
{siteAuthorityList?.content.map((item) => (
<div
key={item.siteId}
className="flex items-center gap-2"
>
<Checkbox
defaultChecked={item.mode === "R"}
onCheckedChange={(checked: boolean) => {
handleSiteAuthorityChecked(checked, item.siteId);
}}
/>
<span className="whitespace-nowrap">{item.siteNm}</span>
</div>
))}
</div>
</TableCell>
</TableRow>
)}
<TableRow>
<TableHead data-required>사용여부</TableHead>
<TableCell className="border">
+3 -13
View File
@@ -7,12 +7,11 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
import { Button, Input, Label } from "@common/components";
import { RoleApi, type Role, type RoleParams } from "@/entities/role";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useDataTable, useMutationWrapper } from "@/shared/lib";
import { createColumns } from "../model/table-columns";
import { useDataTable } from "@/shared/lib";
import { columns } from "../model/table-columns";
function RolePage() {
const router = useRouter();
const alert = useAlertStore((state) => state.setMessage);
const [keyword, setKeyword] = useState("");
const [searchParams, setSearchParams] = useState<RoleParams>({
@@ -34,13 +33,6 @@ function RolePage() {
}),
});
const { mutateAsync: deleteRole } = useMutationWrapper({
mutationFn: (roleId: string) => RoleApi.deleteRole(roleId),
onSuccess: () => {
alert("삭제되었습니다.", query.refetch);
},
});
const handleRowClick = (row: Role) => {
router.push(`/role/register/${row.roleId}`);
};
@@ -48,9 +40,7 @@ function RolePage() {
const { tableComponent } = useDataTable({
query,
tableOptions: {
columns: createColumns({
deleteRole,
}),
columns,
state: {
pagination,
},
-24
View File
@@ -1,24 +0,0 @@
import type { NodeModel } from "@minoru/react-dnd-treeview";
import type { TreeMenu } from "@/entities/tree-menu";
export const generateMenuTreeData = (
data: TreeMenu[],
): NodeModel<TreeMenu>[] => {
return data.reduce<NodeModel<TreeMenu>[]>((acc, menu) => {
const node: NodeModel<TreeMenu> = {
id: menu.menuCd ?? 0,
parent: menu.parentId ?? 0,
text: menu.menuSubKornNm ?? "",
droppable: true,
data: menu,
};
acc.push(node);
if (menu.children?.length) {
acc.push(...generateMenuTreeData(menu.children));
}
return acc;
}, []);
};
@@ -1,32 +1,6 @@
import dayjs from "dayjs";
import { memo, useState } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import { type ColumnDef } from "@tanstack/react-table";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import {
Button,
Checkbox,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@common/components";
import type { Site } from "@/entities/site";
import { TreeMenuApi } from "@/entities/tree-menu";
import {
MenuApi,
siteMapFormSchema,
type SiteMapFormData,
} from "@/entities/menu";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
import { MenuTree } from "../ui/menu-tree";
import { generateMenuTreeData } from "../lib/utils";
export const columns: ColumnDef<Site>[] = [
{
@@ -71,81 +45,4 @@ export const columns: ColumnDef<Site>[] = [
return date.format("YYYY-MM-DD");
},
},
{
id: "action",
header: "관리",
cell: ({ row }) => {
return (
<div onClick={(e) => e.stopPropagation()}>
<ActionCell data={row.original} />
</div>
);
},
},
];
const ActionCell = memo(({ data }: { data: Site }) => {
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
const [open, setOpen] = useState(false);
const { data: menuTreeData, refetch } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, data.siteId],
queryFn: () => TreeMenuApi.getTreeMenuList(data.siteId),
select: (data) => generateMenuTreeData(data),
enabled: !!data.siteId && open,
});
const { mutate: publishSiteMap } = useMutationWrapper({
mutationFn: (data: SiteMapFormData["siteMap"]) =>
MenuApi.publishSiteMap(data),
onSuccess: () => {
alert("사이트맵이 저장되었습니다.", () => {
setOpen(false);
refetch();
});
},
});
const form = useForm<SiteMapFormData>({
resolver: zodResolver(siteMapFormSchema),
});
const fieldArray = useFieldArray({
control: form.control,
name: "siteMap",
});
const handlePublishSiteMap = async () => {
if (await confirm("저장하시겠습니까?")) {
publishSiteMap(form.getValues("siteMap"));
}
};
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button size="sm">사이트맵</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>{data.siteNm} - 사이트맵 관리</DialogTitle>
</DialogHeader>
<div>
<div className="flex items-center justify-end gap-2 p-2">
<span className="text-xl">표시여부</span>
<Checkbox checked />
</div>
<MenuTree tree={menuTreeData ?? []} fieldArray={fieldArray} />
</div>
<DialogFooter>
<Button onClick={handlePublishSiteMap}>저장</Button>
<DialogClose asChild>
<Button color="white">취소</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
);
});
ActionCell.displayName = "ActionCell";
@@ -1,58 +0,0 @@
import type { ComponentProps } from "react";
import { Button, Input } from "@common/components";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
type EditableNodeProps = {
depth?: number;
onSave: () => void;
onCancel: () => void;
} & ComponentProps<typeof Input>;
function EditableNode({
depth,
onSave,
onCancel,
...props
}: EditableNodeProps) {
return (
<div
className="flex items-center gap-2 rounded-sm bg-accent p-2"
style={{ marginInlineStart: !depth ? 0 : depth * 5 + 5 }}
onClick={(e) => e.stopPropagation()}
>
<Input {...props} className="h-[2.5rem] !text-[1.3rem] text-foreground" />
<div className="flex items-center gap-1">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onSave}
aria-label="저장"
>
<i className="diveicon di-check text-2xl" />
</Button>
</TooltipTrigger>
<TooltipContent>저장</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8 !bg-transparent"
onClick={onCancel}
aria-label="취소"
>
<i className="diveicon di-x" />
</Button>
</TooltipTrigger>
<TooltipContent>취소</TooltipContent>
</Tooltip>
</div>
</div>
);
}
export { EditableNode };
-128
View File
@@ -1,128 +0,0 @@
import { useState } from "react";
import type { NodeModel } from "@minoru/react-dnd-treeview";
import type { UseFieldArrayReturn } from "react-hook-form";
import { Button, Checkbox, cn } from "@common/components";
import { type TreeMenu } from "@/entities/tree-menu";
import type { SiteMapFormData, SiteMapPayloadData } from "@/entities/menu";
import { EditableNode } from "./editable-node";
type MenuNodeProps = {
node: NodeModel<TreeMenu>;
depth: number;
isOpen: boolean;
fieldArray: UseFieldArrayReturn<SiteMapFormData>;
onToggle: (id: NodeModel["id"]) => void;
};
function MenuNode({
node,
depth,
isOpen,
fieldArray,
onToggle,
}: MenuNodeProps) {
const { id, text, data } = node;
const indent = depth * 10 + 5;
const [editMode, setEditMode] = useState(false);
const [nodeName, setNodeName] = useState(text);
const [otptYn, setOtptYn] = useState(data?.otptYn ?? "N");
const updateField = (id: number, newField: SiteMapPayloadData) => {
const existingFieldIndex = fieldArray.fields.findIndex(
(field) => field.menuCd === id,
);
if (existingFieldIndex !== -1) {
fieldArray.update(existingFieldIndex, newField);
} else {
fieldArray.append(newField);
}
};
const handleToggle = () => onToggle(id);
const handleSave = async () => {
const newField = {
menuCd: id as number,
menuSubKornNm: nodeName,
otptYn,
};
updateField(id as number, newField);
setEditMode(false);
};
const handleCancel = () => {
setEditMode(false);
setNodeName(text);
};
const handleOtptYnChange = (checked: boolean) => {
const newField = {
menuCd: id as number,
menuSubKornNm: nodeName,
otptYn: checked ? "Y" : "N",
};
updateField(id as number, newField);
setOtptYn(checked ? "Y" : "N");
};
return (
<div
style={{ paddingInlineStart: indent }}
className={cn(
"group flex cursor-pointer items-center gap-2 rounded-sm p-2 hover:bg-accent",
editMode && "p-0 pr-2 hover:bg-transparent",
)}
onClick={(e) => {
e.stopPropagation();
handleToggle();
}}
>
{editMode ? (
<EditableNode
value={nodeName}
onChange={(e) => setNodeName(e.target.value)}
onSave={handleSave}
onCancel={handleCancel}
/>
) : (
<div className="flex w-full items-center justify-between">
<div className="flex items-center">
{data?.children?.length && (
<i
className={cn(
"diveicon di-arrow-forward mr-2 text-base text-label",
isOpen && "rotate-90",
)}
/>
)}
<span className="text-[1.4rem] font-medium text-label">
{nodeName}
</span>
</div>
<Button
variant="ghost"
size="icon"
className="invisible !bg-transparent group-hover:visible"
onClick={(e) => {
e.stopPropagation();
setEditMode(true);
}}
>
<i className="diveicon di-pencil" />
</Button>
</div>
)}
<Checkbox
className="ml-auto"
checked={otptYn === "Y"}
onClick={(e) => {
e.stopPropagation();
}}
onCheckedChange={handleOtptYnChange}
/>
</div>
);
}
export { MenuNode };
-70
View File
@@ -1,70 +0,0 @@
"use client";
import { useRef } from "react";
import type { UseFieldArrayReturn } from "react-hook-form";
import {
Tree,
type NodeModel,
type TreeMethods,
} from "@minoru/react-dnd-treeview";
import { Button } from "@common/components";
import type { SiteMapFormData } from "@/entities/menu";
import { type TreeMenu } from "@/entities/tree-menu";
import { MenuNode } from "./menu-node";
type MenuTreeProps = {
tree: NodeModel<TreeMenu>[];
fieldArray: UseFieldArrayReturn<SiteMapFormData>;
};
function MenuTree({ tree, fieldArray }: MenuTreeProps) {
const treeMethods = useRef<TreeMethods>(null);
return (
<>
<div className="max-h-[calc(100vh-33.5rem)] min-h-[40vh] overflow-auto">
<Tree
tree={tree}
rootId={0}
ref={treeMethods}
render={(node: NodeModel<TreeMenu>, props) => {
return <MenuNode node={node} fieldArray={fieldArray} {...props} />;
}}
onDrop={() => {}}
canDrag={() => false}
canDrop={() => false}
classes={{
container: "flex flex-col",
dropTarget: "!bg-[rgba(32,148,250,0.5)] rounded-sm",
placeholder: "relative",
}}
sort={false}
/>
</div>
<div className="flex gap-2">
<Button
color="lightGray"
className="flex-1 items-center px-0"
onClick={() => {
treeMethods.current?.openAll();
}}
>
<i className="diveicon di-arrows-angle-expand" />
전체펼침
</Button>
<Button
color="lightGray"
className="flex-1 items-center px-0"
onClick={() => {
treeMethods.current?.closeAll();
}}
>
<i className="diveicon di-arrows-angle-contract" />
전체접기
</Button>
</div>
</>
);
}
export { MenuTree };
@@ -36,14 +36,9 @@ export const columns: ColumnDef<Post>[] = [
accessorKey: "pstgPrdSeNm",
header: "게시기간",
cell: ({ row }) => {
const pstgPrdSeCd = row.original.pstgPrdSeCd;
const pstgPrdSeNm = row.original.pstgPrdSeNm;
const pstgBgngDt = row.original.pstgBgngDt;
const pstgEndDt = row.original.pstgEndDt;
if (pstgPrdSeCd === "99") {
return pstgPrdSeNm;
}
return `${pstgPrdSeNm} (${dayjs(pstgBgngDt).format("YYYY-MM-DD")} ~ ${dayjs(pstgEndDt).format("YYYY-MM-DD")})`;
},
},
@@ -1,11 +1,6 @@
import dayjs from "dayjs";
import type { ColumnDef } from "@tanstack/react-table";
import {
USER_STATE_CODE,
type UserListResponse,
type UserStateCode,
} from "@/entities/user";
import { Badge } from "@common/components";
import type { UserListResponse } from "@/entities/user";
export const columns: ColumnDef<UserListResponse>[] = [
{
@@ -47,11 +42,8 @@ export const columns: ColumnDef<UserListResponse>[] = [
header: "기관/부서",
},
{
accessorKey: "userStateCode",
accessorKey: "userStateCodeName",
header: "회원상태",
cell: ({ row }) => {
return getUserStateBadge(row.original.userStateCode);
},
},
{
accessorKey: "aprvYmd",
@@ -71,18 +63,3 @@ export const columns: ColumnDef<UserListResponse>[] = [
},
},
];
const getUserStateBadge = (userStateCode: UserStateCode) => {
switch (userStateCode) {
case USER_STATE_CODE.대기:
return <Badge color="gray">대기</Badge>;
case USER_STATE_CODE.정상:
return <Badge color="green">정상</Badge>;
case USER_STATE_CODE.정지:
return <Badge color="yellow">정지</Badge>;
case USER_STATE_CODE.탈퇴:
return <Badge color="purple">탈퇴</Badge>;
case USER_STATE_CODE.삭제:
return <Badge color="red">삭제</Badge>;
}
};
@@ -16,11 +16,20 @@ function CreatePage() {
defaultValues: {
userId: "",
userName: "",
email: "",
password: "",
pswdHnt: "",
pswdAns: "",
mphn: "",
telno: "",
zip: "",
addr: "",
daddr: "",
laeSeCd: "",
ogdpSeCd: "",
expryYmd: "",
instSeCd: "",
enoCd: "",
},
});
-1
View File
@@ -53,7 +53,6 @@ export const ENDPOINT = {
GROUP_USERS: "/cms-service/api/v1/group-users",
MANAGER_IP: "/cms-service/api/v1/mgrip",
APILOG: "/cms-service/api/v1/api-logs",
SITE_AUTHORITY: "/cms-service/api/v1/site-authrt",
},
BOARD_SERVICE: {
BBS: "/board-service/api/v1/bbs",
-12
View File
@@ -1,12 +0,0 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 11300
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
-13
View File
@@ -1,13 +0,0 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 PC 서버 API
-41
View File
@@ -1,41 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
#.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
-12
View File
@@ -1,12 +0,0 @@
const DEV_PORT_COMMON = "11081";
const DEV_PORT_FO = "11082";
const DEV_PORT_WR = "11080";
export const LOCAL_URL_COMMON = `http://192.168.1.129:${DEV_PORT_COMMON}/api/v1`;
export const DEV_URL_COMMON = `http://con.mind-one.co.kr:${DEV_PORT_COMMON}/api/v1`;
export const LOCAL_URL_FO = `http://192.168.1.129:${DEV_PORT_FO}/api/v1`;
export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`;
export const LOCAL_URL_WR = `http://192.168.1.129:${DEV_PORT_WR}/api/v1`;
export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT_WR}/api/v1`;
@@ -1 +0,0 @@
export * from './env';
-79
View File
@@ -1,79 +0,0 @@
import { DEV_URL_COMMON, DEV_URL_FO, DEV_URL_WR } from "./config/env";
export const getFetch = async (part : string, url: string) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : part === "wr" ? DEV_URL_WR+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl , {
method: "GET",
headers: {
accept: "application/json",
}
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
const res = await response.json()
return res;
}
export const getFetchAreaList = async (url: string) => {
const response = await fetch(url , {
method: "GET",
headers: {
accept: "application/json",
}
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
const res = await response.json()
return res;
}
export const postFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return await response.json();
}
export const putFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return response;
}
export const deleteFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return response;
}
@@ -1,46 +0,0 @@
type DataFormProps = {
type: string;
column: string;
item: Record<string, unknown>;
}
export default function DataForm({ type, column, item }: DataFormProps) {
if(item) {
const value = item[column];
switch(type){
case 'date':
if(value && typeof value === 'string' && value.length >= 8)
item[column] = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8);
break;
case 'number':
if(value && value != '') {
item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float1':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 10) / 10).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float2':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 100) / 100).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float3':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1000) / 1000).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
}
}
}
@@ -1,279 +0,0 @@
import { useFoStore } from '@/app/store';
import { DatePickerRange, SelectBox } from '@common/business_components/ui';
import ListDiv from '@common/business_components/ui/list_div';
import { Table, TableCell, TableHead, TableRow } from '@common/components/ui';
import { useCallback, useEffect, useRef, useState } from 'react';
type FacilityTermModalHeaderProps<T extends { msrDt: string }> = {
fcltyCd : string;
fcltyName: string;
tabName: string;
searchYear: string;
searchList: T[];
ref: React.RefObject<ReturnType<T>>;
queryClick:(returnType:ReturnType<T>) => void;
}
export type ReturnType<T> = {
startDate: Date;
endDate: Date;
tabName: string;
filteredList: T[];
}
export default function FacilityTermModalHeader<T extends { msrDt: string }>({
searchList,
fcltyName,
tabName,
searchYear,
queryClick,
ref
}: FacilityTermModalHeaderProps<T & { msrDt: string }>) {
const {Common} = useFoStore((state) => state);
const { selectFlowPanel } = Common;
const [startDate, setStartDate] = useState<Date>(new Date(Number(searchYear), 0, 1));
const [endDate, setEndDate] = useState<Date>(new Date(Number(searchYear), 11, 31));
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '00');
const [copySearchList, setCopySearchList] = useState<T[]>(searchList);
const [standSearchList, setStandSearchList] = useState<T[]>(searchList);
const isDateInRanges = (date: Date) => {
const standYear = parseInt(searchYear);
const dateRanges:{start:Date, end:Date}[] = [];
if (tabName.includes("_summer")) {
dateRanges.push({
start: new Date(standYear, 5, 1),
end: new Date(standYear, 7, 31)
});
} else if (tabName.includes("_winter")) {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 1, 28)
});
} else {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
});
}
return dateRanges.some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
};
useEffect(() => {
let startDate = new Date(Number(searchYear), 0, 1);
let endDate = new Date(Number(searchYear), 11, 31);
if(tabName.includes("_summer")) {
startDate = new Date(Number(searchYear), 5, 1);
endDate = new Date(Number(searchYear), 7, 31);
setStartDate(startDate);
setEndDate(endDate);
} else if (tabName.includes("_winter")) {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 1, 28);
setStartDate(startDate);
setEndDate(endDate);
} else {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 11, 31);
setStartDate(startDate);
setEndDate(endDate);
}
let filteredData: T[] = [];
if(tabName.includes("_rain")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay;
if(flowPanel === '1') {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3;
} else if(flowPanel === '2'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10;
} else if(flowPanel === '3'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20;
} else if(flowPanel === '4'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30;
} else if(flowPanel === '5'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40;
} else if(flowPanel === '6'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50;
} else if(flowPanel === '7'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60;
} else if(flowPanel === '8'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60;
} else {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1);
}
});
} else if(tabName.includes("_chuncheon")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0;
});
} else {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
}
setCopySearchList(filteredData);
setStandSearchList(filteredData);
}, [searchList, searchYear, tabName, flowPanel]);
const handleStartDateSelect = (day: Date | undefined) => {
if(day && day > endDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const selectedDate = day ?? startDate;
setStartDate(selectedDate);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = standSearchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const handleEndDateSelect = (day: Date | undefined) => {
if(day && day < startDate) {
alert('종료일은 시작일 이후일 수 없습니다.');
return;
}
const selectedDate = day ?? endDate;
setEndDate(selectedDate);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = standSearchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const fncSetFilteredList = useCallback(() => {
queryClick({
startDate: startDate,
endDate: endDate,
tabName: tabName,
filteredList: copySearchList
});
}, [queryClick, startDate, endDate, tabName, copySearchList]);
useEffect(() => {
fncSetFilteredList();
}, [fncSetFilteredList]);
useEffect(() => {
if(ref && ref.current) {
const customRef = ref.current;
customRef.startDate = startDate;
customRef.endDate = endDate;
customRef.tabName = tabName;
customRef.filteredList = copySearchList;
}
}, [startDate, endDate, tabName, copySearchList, ref]);
return (
<ListDiv className="!h-[80px]">
<div className="!m-[0.5rem] max-h-[40rem] overflow-auto">
<Table>
<thead>
<TableRow className="!h-[20px]">
<TableHead className="w-[120px]">{'시설명'}</TableHead>
{tabName === "tab_term_rain" ? <TableCell className="w-[300px]">{fcltyName}</TableCell> :
<TableCell className="w-[380px]">{fcltyName}</TableCell>}
{tabName === "tab_term_rain" && (
<>
<TableHead className="w-[120px]">{'강수구분'}</TableHead>
<TableCell className="w-[250px]">
<div className="justify-center">
<SelectBox
selectArray={selectFlowPanel}
value={flowPanel}
onSelectValue={(val) => setFlowPanel(val.toString())}
className="mx-3 w-[150px]"
label=""
selectClass="w-full p-1 border rounded bg-white"
/>
</div>
</TableCell>
</>
)}
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={startDate}
/>
</div>
<h3 className="justify-center px-5">~</h3>
<div ref={endPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
);
}
@@ -1,276 +0,0 @@
import { useFoStore } from '@/app/store';
import { SelectBox } from '@common/business_components/ui';
import ListDiv from '@common/business_components/ui/list_div';
import { DatePicker, Table, TableCell, TableHead, TableRow } from '@common/components/ui';
import { useCallback, useEffect, useRef, useState } from 'react';
type FacilityTermModalHeaderProps<T extends { msrDt: string }> = {
fcltyCd : string;
fcltyName: string;
tabName: string;
searchYear: string;
searchList: T[];
ref: React.RefObject<ReturnType<T>>;
queryClick:(returnType:ReturnType<T>) => void;
}
export type ReturnType<T> = {
startDate: Date;
endDate: Date;
tabName: string;
filteredList: T[];
}
export default function FacilityTermModalHeaderMonth<T extends { msrDt: string }>({
searchList,
fcltyName,
tabName,
searchYear,
queryClick,
ref
}: FacilityTermModalHeaderProps<T & { msrDt: string }>) {
const {Common} = useFoStore((state) => state);
const { selectFlowPanel } = Common;
const [startDate, setStartDate] = useState<Date>(new Date(Number(searchYear), 0, 1));
const [endDate, setEndDate] = useState<Date>(new Date(Number(searchYear), 11, 31));
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '00');
const [copySearchList, setCopySearchList] = useState<T[]>(searchList);
/* const isDateInRanges = (date: Date) => {
const standYear = parseInt(searchYear);
const dateRanges = [];
if (tabName.includes("_summer")) {
dateRanges.push({
start: new Date(standYear, 5, 1),
end: new Date(standYear, 7, 31)
});
} else if (tabName.includes("_winter")) {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 1, 28)
});
} else {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
});
}
return dateRanges.some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
}; */
useEffect(() => {
let startDate = new Date(Number(searchYear), 0, 1);
let endDate = new Date(Number(searchYear), 11, 31);
if(tabName.includes("_summer")) {
startDate = new Date(Number(searchYear), 5, 1);
endDate = new Date(Number(searchYear), 7, 31);
setStartDate(startDate);
setEndDate(endDate);
} else if (tabName.includes("_winter")) {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 1, 28);
setStartDate(startDate);
setEndDate(endDate);
} else {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 11, 31);
setStartDate(startDate);
setEndDate(endDate);
}
let filteredData: T[] = [];
if(tabName.includes("_rain")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay;
if(flowPanel === '1') {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3;
} else if(flowPanel === '2'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10;
} else if(flowPanel === '3'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20;
} else if(flowPanel === '4'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30;
} else if(flowPanel === '5'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40;
} else if(flowPanel === '6'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50;
} else if(flowPanel === '7'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60;
} else if(flowPanel === '8'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60;
} else {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1);
}
});
} else if(tabName.includes("_chuncheon")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0;
});
} else {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
}
setCopySearchList(filteredData);
}, [searchList, searchYear, tabName, flowPanel]);
const handleStartDateSelect = (day: Date | undefined) => {
if(day && day > endDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const selectedDate = day ?? startDate;
setStartDate(selectedDate);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const handleEndDateSelect = (day: Date | undefined) => {
if(day && day < startDate) {
alert('종료일은 시작일 이후일 수 없습니다.');
return;
}
const selectedDate = day ?? endDate;
setEndDate(selectedDate);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const fncSetFilteredList = useCallback(() => {
queryClick({
startDate: startDate,
endDate: endDate,
tabName: tabName,
filteredList: copySearchList
});
}, [queryClick, startDate, endDate, tabName, copySearchList]);
useEffect(() => {
fncSetFilteredList();
}, [fncSetFilteredList]);
useEffect(() => {
if(ref && ref.current) {
const customRef = ref.current;
customRef.startDate = startDate;
customRef.endDate = endDate;
customRef.tabName = tabName;
customRef.filteredList = copySearchList;
}
}, [startDate, endDate, tabName, copySearchList, ref]);
return (
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
{tabName === "tab_term_rain" ? <TableCell className="w-[300px]">{fcltyName}</TableCell> :
<TableCell className="w-[380px]">{fcltyName}</TableCell>}
{tabName === "tab_term_rain" && (
<>
<TableHead className="w-[120px]">{'강수구분'}</TableHead>
<TableCell className="w-[250px]">
<div className="justify-center">
<SelectBox
selectArray={selectFlowPanel}
value={flowPanel}
onSelectValue={(val) => setFlowPanel(val.toString())}
className="mx-3 w-[150px]"
label=""
selectClass="w-full p-1 border rounded bg-white"
/>
</div>
</TableCell>
</>
)}
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePicker
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
defaultMonth={startDate}
/>
</div>
<h3 className="justify-center px-5">~</h3>
<div ref={endPickerRef} className="flex justify-center">
<DatePicker
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
);
}
@@ -1,65 +0,0 @@
import { TableHead, TableRow } from "@common/components/ui";
import type { TableUpperProps } from "../../store/dashboard";
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: string }) => {
return (
layoutNum === 'A' ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'행정구역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : layoutNum === 'R' ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'유역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe1' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'권역'}</TableHead> :
head.upName === 'upChangeRe2' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'행정구역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
);
}
export { HeadMakeColSpan };
@@ -1,108 +0,0 @@
import { useFoStore } from '@/app/store';
import type { facilityListProps } from '@/app/store/processFacility';
import { Label } from '@common/components/ui';
import React, { useEffect, useState, useRef, useCallback } from 'react'
type thisProps = {
label: string | undefined;
setValue : (fcltyCd?: string, fcltyNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => void;
sidoSigun? : string | undefined;
fcltyName? : string | undefined;
}
export default function SearchInputText({label=undefined, setValue=()=>{}, sidoSigun=undefined, fcltyName=undefined} :thisProps) {
const { ProcessFacilityList, searchProcessFacilitySearching } = useFoStore((state) => state);
const [isOpen, setIsOpen] = useState(false);
const [textValue, setTextValue] = useState(fcltyName || '');
const wrapperRef = useRef<HTMLDivElement>(null);
const processFacilityList = ProcessFacilityList?.processFacilityList || [];
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setTextValue(value);
if (value) {
setIsOpen(true);
if(sidoSigun) {
searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0', value);
} else {
searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0', value);
}
} else {
setIsOpen(false);
}
}, [searchProcessFacilitySearching, sidoSigun]);
const onInputFocus = useCallback(() => {
setIsOpen(true);
if(sidoSigun) {
searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0');
} else {
searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0');
}
}, [searchProcessFacilitySearching, sidoSigun]);
useEffect(() => {
setTextValue(fcltyName || '');
}, [fcltyName]);
const handleSuggestionClick = useCallback((bscFctCd: string, bscFctNm: string, fctCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '') => {
console.log('bscFctNm : ', bscFctNm);
console.log('bscFctCd : ', bscFctCd);
console.log('fctCpc : ', fctCpc);
console.log('trtFrfNm : ', trtFrfNm);
console.log('efflWqltStndrdNm : ', efflWqltStndrdNm);
setTextValue(bscFctNm);
setValue(bscFctCd, bscFctNm, fctCpc, trtFrfNm, efflWqltStndrdNm);
setIsOpen(false);
}, [setValue]);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
return (
<div className={'relative'} ref={wrapperRef}>
{label && <Label className="w-20 text-xl text-gray-600 justify-self-center">{label}</Label>}
<input
type="text"
value={textValue}
onChange={handleInputChange}
onFocus={onInputFocus}
className="w-[200px] ml-6 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder={`${label}을(를) 입력하세요`}
/>
{isOpen && processFacilityList.length > 0 && (
<div className="absolute z-10 w-[300px] px-2 py-1 !mt-5 ml-20 bg-white border border-gray-300 rounded-md shadow-lg h-90 overflow-auto">
<div className="max-h-72">
{processFacilityList.map((item : facilityListProps, index : number) => (
<div
key={index}
className="w-full text-left pl-1 pr-10 py-2 hover:bg-gray-100 cursor-pointer overflow-auto"
onClick={() => handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm);
}
}}
>
{item.sido}&nbsp;{item.gugun}&nbsp;<span className="float-right">{item.bscFctNm}</span>
</div>
))}
</div>
</div>
)}
</div>
)
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

-7
View File
@@ -1,7 +0,0 @@
@import "../../../packages/common/components/src/base.css";
html,
body {
font-family: "Noto Sans KR", sans-serif;
font-size: 10px;
}
-24
View File
@@ -1,24 +0,0 @@
"use client"
import { BaseLayout } from "@common/components/ui";
import "./globals.css";
import { StoreProvider } from "./store";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<StoreProvider>
<BaseLayout>
{children}
</BaseLayout>
</StoreProvider>
</body>
</html>
);
}
-44
View File
@@ -1,44 +0,0 @@
"use client"
import Link from "next/link";
import { Button } from "@common/components/ui";
export default function Home() {
return (
<div className="grid grid-cols-12 gap-2">
<div className="col-span-2">
<Link href={"./pages/processFacility"}><Button>처리시설 시설정보</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/flowRate"}><Button>시설별 유량현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/dashboard"}><Button>대시보드</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/processArea"}><Button>처리구역정보</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/facilityPrecipitation"}><Button>시설별 강수현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/facilityWaterQlty"}><Button>시설별 수질현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/planSewage"}><Button>계획하수량 산정지표 현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/sewageOccur"}><Button>오수발생량 추정</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/underWaterInput"}><Button>지하수 유입량 추정</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/rdi"}><Button>RDII 조회</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/map"}><Button>지도보기</Button></Link>
</div>
</div>
);
}
@@ -1,98 +0,0 @@
"use client"
import { useFoStore } from '@/app/store';
import type { DrainageEquipmentListProps, TableUpperProps } from '@/app/store/dashboard';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@common/components';
import { useEffect, useState } from 'react';
export type DrainageEquipmentProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function DrainageEquipment({searchYear = '', fcltyList = []}: DrainageEquipmentProps) {
const {
Common,
DrainageEquipment,
searchDrainageEquipment,
} = useFoStore((state) => state);
const {
isInit,
} = Common;
const {
upHeadListDrainageEquipment,
drainageEquipmentList,
} = DrainageEquipment;
const [hasSearched, setHasSearched] = useState(false);
// 이전 시설코드 저장
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const tableData = fcltyList;
useEffect(() => {
if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchDrainageEquipment(searchYear, tableData[0].bscFctCd, false);
setHasSearched(true);
setPrevFacilityCode(tableData[0].bscFctCd);
}
}, [fcltyList, isInit, searchDrainageEquipment, searchYear, tableData, hasSearched, prevFacilityCode]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
// 시설계획 table header 변경
const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => {
return (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'part1' ? <TableHead rowSpan={3} className='w-60' key={head.id}>구분</TableHead> :
head.id === 'part2' ? <TableHead rowSpan={3} className='w-60' key={head.id}>구분</TableHead> :
head.id == 'nowSum' ? <TableHead rowSpan={2} colSpan={3} key={head.id} style={{ whiteSpace: 'pre-line' }}>{'현재\n(기준년도)'}</TableHead> :
head.id == 'stepOneSum' ? <TableHead rowSpan={1} colSpan={12} key={head.id} style={{ whiteSpace: 'pre-line' }}>{'변경(금회계획)'}</TableHead> :
head.id == 'etc' ? <TableHead rowSpan={3} key={head.id}>비고</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'stepOneSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>1단계</TableHead> :
head.id === 'stepTwoSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>2단계</TableHead> :
head.id === 'stepThreeSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>3단계</TableHead> :
head.id === 'stepFourSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>4단계</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
}
const upHeadList = HeadMakeColSpan({upHeadList:upHeadListDrainageEquipment});
return (
<div className="overflow-x-auto">
<Table variant="secondary" className='w-[130%]'>
<TableHeader>
{upHeadList}
</TableHeader>
<TableBody>
{drainageEquipmentList.map((el: DrainageEquipmentListProps, index) => (
<TableRow key={index+"row"}>
{upHeadListDrainageEquipment.map((head) => (
(head.id==='nowSum')?
<TableCell className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{333}</TableCell>
:<TableCell className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id as keyof DrainageEquipmentListProps]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}
@@ -1,184 +0,0 @@
"use client"
import type { OperationalStatusListProps } from "@/app/store/dashboard";
import { CHART_FORM, MainChart, type ChartConfig } from "@common/business_components";
import { Button } from "@common/business_components/ui/table/lib/button";
import html2canvas from "html2canvas";
import { useEffect, useState, useRef } from "react";
type yearProps = {
label: string;
평균 : number | string | '';
최대 : number | string | '';
최소 : number | string | '';
}
type summerProps = {
전기간 : number | string | '';
강우일3mm이상 : number | string | '';
비강우시 : number | string | '';
}
type winterProps = {
전기간 : number | string | '';
청천시최대 : number | string | '';
청천시평균 : number | string | '';
청천시최소 : number | string | '';
}
type dashBoardChartModalProps = {
title : string;
item : string;
period : string;
modalData : OperationalStatusListProps[];
waterQlty : string;
}
export default function DashBoardChartModal({item='', period='', title='',modalData=[], waterQlty=''} : dashBoardChartModalProps) {
const [chartData, setChartData] = useState<yearProps[] | summerProps[] | winterProps[]>([]);
const [chartConfig, setChartConfig] = useState<ChartConfig>({});
const chartRef = useRef<HTMLDivElement>(null);
const chartOption = {
chartForm : CHART_FORM.BAR,
chartConfig : chartConfig,
chartData : chartData,
chartTitle : title,
chartClassName : "h-[calc(100%-15%)] w-[90%] mt-5",
graphSize : 40,
yFieldText : item === 'flowRate' ? '유량(㎥/일)' : item === 'waterQlty' ? '수질(mg/L)' : item === 'chongin' ? '수질(mg/L)' : '',
showLegend : true
}
const handleDownload = () => {
if (!chartRef.current) {
return alert('결과 저장에 실패했습니다.');
}
html2canvas(chartRef.current).then((canvas) => {
const link = document.createElement('a');
document.body.appendChild(link);
link.href = canvas.toDataURL('image/png');
link.download = 'result.png';
link.click();
document.body.removeChild(link);
});
}
/**
* useEffect
*/
useEffect(() => {
if(period === 'year'){
const filteredData: yearProps[] = modalData.map((item) => {
if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){
return {
label: item.waterQlty,
평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(3) : (item.yearAvg || 0),
최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(3) : (item.yearMax || 0),
최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(3) : (item.yearMin || 0),
}
}
else{
return {
label: item.waterQlty,
평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(1) : (item.yearAvg || 0),
최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(1) : (item.yearMax || 0),
최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(1) : (item.yearMin || 0),
}
}
})
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
}
else if (period === 'summer') {
const filteredData: summerProps[] = modalData.map((item) => {
if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){
return {
label: item.waterQlty,
전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(3) : (item.summerAll || 0),
강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(3) : (item.summerThree || 0),
비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(3) : (item.summerRain || 0),
}
}else{
return {
label: item.waterQlty,
전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(1) : (item.summerAll || 0),
강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(1) : (item.summerThree || 0),
비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(1) : (item.summerRain || 0),
}
}
})
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
}
else if (period === 'winter') {
const filteredData: winterProps[] = modalData.map((item) => {
return {
label: item.waterQlty,
전기간: typeof item.winterAll === 'string' ? Number(item.winterAll.replace(/,/g, '')).toFixed(3) : (item.winterAll || 0),
청천시최대: typeof item.winterSunnyMax === 'string' ? Number(item.winterSunnyMax.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMax || 0),
청천시평균: typeof item.winterSunnyAvg === 'string' ? Number(item.winterSunnyAvg.replace(/,/g, '')).toFixed(3) : (item.winterSunnyAvg || 0),
청천시최소: typeof item.winterSunnyMin === 'string' ? Number(item.winterSunnyMin.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMin || 0),
}
})
setChartData(filteredData);
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
} else {
setChartData([]);
}
}, [item, modalData, period, waterQlty]);
useEffect(() => {
const newChartConfig = chartData.reduce((config, element) => {
const label = 'label' in element ? element.label : '';
if (label) {
config[label] = {
label: label,
color : "hsl(var(--chart-1))"
};
}
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setChartConfig(newChartConfig);
}, [chartData]);
return (
<>
<div className="flex justify-end">
<span className="text-[13px] font-bold mt-2 w-[260px] h-[30px] -mb-9">
(하절기 : 6/1~8/31 동절기 : 1/1~2/28)
</span>
<Button className="text-[13px] w-[80px] h-[30px] -mb-9 mr-20" onClick={handleDownload}>캡처</Button>
</div>
<div ref={chartRef}>
<MainChart
chartForm={chartOption.chartForm}
chartConfig={chartOption.chartConfig}
chartData={chartOption.chartData}
chartTitle={chartOption.chartTitle}
chartClassName={chartOption.chartClassName}
graphSize={chartOption.graphSize}
yFieldText={chartOption.yFieldText}
showLegend={chartOption.showLegend}
/>
</div>
</>
);
}
@@ -1,277 +0,0 @@
"use client"
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 { useEffect, useState } from "react";
import DashBoardChartModal from "./modal/page";
export type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
// 시설계획 table header 변경
const headMakeColSpanPlanFclty = (upHeadList: TableUpperProps[], planYearList: string[]) => {
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={7} style={{ whiteSpace: 'pre-line' }}>{'시설계획\n(금회)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps, index: number) =>
(head.upName === 'upChangeRe'?<TableHead key={head.id} style={{ whiteSpace: 'pre-line' }}>{index===0?head.title:head.title+'\n('+planYearList[index-1]+')'}</TableHead>:null))}
</TableRow>
</> )
}
// 시설계획 table header 변경
const headMakeColSpanOperationStatus = (upHeadList: TableUpperProps[], searchYear: string) => {
return ( <>
<TableRow>
<TableHead rowSpan={1} colSpan={16} 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.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))}
</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))}
</TableRow>
</> )
}
export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: facilityInfoProps & { onFacilityChange: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }) => void }) {
// 입력값 상태 관리
const {
Common,
FcltyMain,
PlanFclty,
OperationStatus,
searchOperationStatus,
searchPlanInfo
} = useFoStore((state) => state);
const [modalItem, setModalItem] = useState<{item: string, period: string, title: string, waterQlty: string} | null>(null);
const [hasSearched, setHasSearched] = useState(false);
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const {
isInit,
} = Common;
// Tab1
// fclty 관련
const {
upHeadListFclty,
} = FcltyMain;
// 시설계획 관련
const {
upHeadListPlanFclty,
planFcltyList,
planYearList
} = PlanFclty;
// 그리드 관련
const {
upHeadListOperationStatus,
operationalStatusList
} = OperationStatus;
const closeModal = () => {
setModalItem(null);
};
// const fcltyListArr = useMemo(() => [fcltyList], [fcltyList]);
const tableData = fcltyList;
// key 변환
const mappedTableData = tableData.map(item => ({
fcltyNm: item.bscFctNm,
facilityCapacity: item.fcltyCpc,
publicMethod: item.trtFrfNm,
location: item.efflWqltStndrdNm,
}));
const fncLoadModal = (item: string, period: string, waterQlty: string) => {
// 유량
if(item === 'flowRate'){
if(period === 'year'){
setModalItem({item: 'flowRate', period: 'year', title: '연간 유량', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'flowRate', period: 'summer', title: '하절기 유량', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'flowRate', period: 'winter', title: '동절기 유량', waterQlty: waterQlty});
}
}
// 수질
else if (item == 'waterQlty'){
if(period === 'year'){
setModalItem({item: 'waterQlty', period: 'year', title: '연간 수질', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'waterQlty', period: 'summer', title: '하절기 수질', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'waterQlty', period: 'winter', title: '동절기 수질', waterQlty: waterQlty});
}
}
// 총인
else {
if(period === 'year'){
setModalItem({item: 'chongin', period: 'year', title: '총인설비 연간 수질', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'chongin', period: 'summer', title: '총인설비 하절기 수질', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'chongin', period: 'winter', title: '총인설비 동절기 수질', waterQlty: waterQlty});
}
}
}
useEffect(() => {
if (!isInit && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchOperationStatus(false, searchYear, fcltyList[0].bscFctCd);
searchPlanInfo(searchYear, fcltyList[0].bscFctCd);
setHasSearched(true);
setPrevFacilityCode(fcltyList[0].bscFctCd);
planFcltyList.forEach((el: { [key: string]: string | number | boolean }, index) => {
if (index === 6) {
el['item'] = (Number(searchYear)-2).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
} else if (index === 7) {
el['item'] = (Number(searchYear)-1).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
} else if (index === 8) {
el['item'] = (Number(searchYear)-0).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
}
});
}
}, [isInit, searchOperationStatus, searchYear, fcltyList, hasSearched, prevFacilityCode, searchPlanInfo, planFcltyList]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
useEffect(() => {
operationalStatusList.map((item: { [key: string]: string | number | boolean },index) => {
if(index===8 || index===9 || index===10 || index===11 || index===12 || index===13 || index===14 || index===15){
item['yearAvg'] = Number(item['yearAvg']).toFixed(3);
item['yearMax'] = item['yearMax']+'%';
item['yearMin'] = item['yearMin']+'%';
item['summerAll'] = item['summerAll']+'%';
item['summerThree'] = item['summerThree']+'%';
item['summerRain'] = item['summerRain']+'%';
item['winterAll'] = item['winterAll']+'%';
item['winterSunnyAvg'] = item['winterSunnyAvg']+'%';
item['winterSunnyMax'] = item['winterSunnyMax']+'%';
item['winterSunnyMin'] = item['winterSunnyMin']+'%';
}
});
}, [operationalStatusList]);
const upChangeRePlanFclty = headMakeColSpanPlanFclty(upHeadListPlanFclty, planYearList);
const upChangeHeadListOperationStatus = headMakeColSpanOperationStatus(upHeadListOperationStatus, searchYear);
return (
<>
<UiTable headName={""} tableData={mappedTableData} headlist={upHeadListFclty} pageSize={0} total={0} cellClick={ undefined } onPageChange={() => { } } />
<ContentsDiv>
<div className="grid grid-cols-12 gap-1">
<div className="col-span-5">
<Table variant="secondary">
<TableHeader>
{upChangeRePlanFclty}
</TableHeader>
<TableBody>
{planFcltyList.map((el: { [key: string]: string | number | boolean },index) => (
<TableRow key={index+"row"} >
{upHeadListPlanFclty.map((head) => (
((index === 6 || index === 7 || index === 8) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))?
<TableCell className="text-right" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:((index === 6 || index === 7 || index === 8) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree' || head.id === 'stepFour'))?
null
:((index === 5) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))?
<TableCell colSpan={3} className="text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:((index === 5) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree'|| head.id === 'stepFour'))?
null
:(index < 5 || index > 5)&& (index!= 4) && (head.id ==='provisionalApproval'||head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour')
?<TableCell className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:(head.id === "provisionalApproval" && index === 4)
?<TableCell colSpan={6} className="text-right border border-[var(--border)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{(Number(fcltyList[0]?.fcltyCpc)* 0.6).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 })} </TableCell>
:((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') && index === 4)
?null
:(head.id === 'item' && index != 5)?
<TableCell className="text-center text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:<TableCell className="text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="col-span-7 h-[600px] overflow-x-auto">
<div className="w-[200%]">
<Table variant="secondary">
<TableHeader>
{upChangeHeadListOperationStatus}
</TableHeader>
<TableBody>
{operationalStatusList.map((el: { [key: string]: string | number | boolean },index) => (
<TableRow key={index+"row"} >
{upHeadListOperationStatus.map((head) => (
(head.id === 'flowRate' && index === 0)?<TableCell rowSpan={5} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'flowRate' && index === 5)?<TableCell rowSpan={6} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(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 === 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>:
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=11 && index<=12))?<TableCell className="bg-blue-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=0 && index<=4))?<TableCell className="bg-yellow-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=5 && index<=10))?<TableCell className="bg-green-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=11 && index<=12))?<TableCell className="bg-blue-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=0 && index<=4))?<TableCell className="bg-yellow-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=5 && index<=10))?<TableCell className="bg-green-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=11 && index<=12))?<TableCell className="bg-blue-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
null
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</div>
</ContentsDiv>
{modalItem && (modalItem.item === 'flowRate' || modalItem.item === 'waterQlty' || modalItem.item === 'chongin') && (
<UiModal
body={<DashBoardChartModal item={modalItem.item} period={modalItem.period} title={modalItem.title} modalData={operationalStatusList} waterQlty={modalItem.waterQlty}></DashBoardChartModal>}
bodyClass="sm:max-w-[80%] h-[88%]"
open={true}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</>
);
}
@@ -1,243 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { lineChartDataProps } from "@/app/store/dashboard";
import { CHART_FORM, type ChartConfig, ContentsDiv, MainChart } from "@common/business_components";
import { Card, CardContent } from "@common/business_components/ui/chart/lib/card";
import ListDiv from "@common/business_components/ui/list_div";
import { useEffect, useState } from "react";
type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function DashBoardChart({searchYear = '', fcltyList = []}: facilityInfoProps) {
const {MainGraph
,searchMainGraph
} = useFoStore((state) => state);
const [hasSearched, setHasSearched] = useState(false);
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const [chart_tpTp, setChart_tpTp] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_tntn, setChart_tntn] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_inflBodInflTn, setChart_inflBodInflTn] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_inflBodInflTp, setChart_inflBodInflTp] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const {
chart_inflowRain,
chart_bodRain,
chart_inflTp,
chart_chongInTp,
chart_efflTp,
chart_inflTn,
chart_efflTn,
chart_inflBod,
} = MainGraph;
/**
* 차트 Config
*/
const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#00b50c", "#D4A5A5"];
//유입하수량-강수량
const chartConfig_inflowRain = {
barVal0: {
label: '강수량',
color: colors[2],
},
lineVal0: {
label: '시설용량',
color: colors[1],
},
lineVal1: {
label: '유입량',
color: colors[4],
}
} satisfies ChartConfig;
//강우량-유입BOD
const chartConfig_rainBod = {
barVal0: {
label: '강수량',
color: colors[2],
},
lineVal0: {
label: '유입BOD',
color: colors[0],
},
} satisfies ChartConfig;
//총인유입T-P-방류T-P
const chartConfig_tptp = {
chartData1: {
label: '총인유입T-P',
color: colors[0],
},
chartData2: {
label: '방류T-P',
color: colors[1],
},
} satisfies ChartConfig;
// 유입T-N-방류T-N
const chartConfig_tntn = {
chartData1: {
label: '유입T-N',
color: colors[0],
},
chartData2: {
label: '방류T-N',
color: colors[1],
},
} satisfies ChartConfig;
// 유입BOD-유입T-N
const chartConfig_inflBodInflTn = {
chartData1: {
label: '유입BOD',
color: colors[0],
},
chartData2: {
label: '유입T-N',
color: colors[1],
},
} satisfies ChartConfig;
// 유입BOD-유입T-P
const chartConfig_inflBodInflTp = {
chartData1: {
label: '유입BOD',
color: colors[0],
},
chartData2: {
label: '유입T-P',
color: colors[1],
},
} satisfies ChartConfig;
useEffect(() => {
if (fcltyList.length > 0 && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchMainGraph(searchYear, fcltyList[0].bscFctCd);
setHasSearched(true);
setPrevFacilityCode(fcltyList[0].bscFctCd);
}
}, [searchYear, fcltyList, searchMainGraph, hasSearched, chart_inflTp, chart_efflTp, prevFacilityCode]);
useEffect(() => {
if (!chart_chongInTp || !chart_efflTp) return;
const chartTpTpData = {
chartData1: chart_chongInTp,
chartData2: chart_efflTp
};
setChart_tpTp(chartTpTpData);
}, [chart_chongInTp, chart_efflTp]);
useEffect(() => {
if (!chart_inflTn || !chart_efflTn) return;
const chartTntnData = {
chartData1: chart_inflTn,
chartData2: chart_efflTn
};
setChart_tntn(chartTntnData);
}, [chart_inflTn, chart_efflTn]);
useEffect(() => {
if (!chart_inflBod || !chart_inflTn) return;
const chartInflBodInflTnData = {
chartData1: chart_inflBod,
chartData2: chart_inflTn
};
setChart_inflBodInflTn(chartInflBodInflTnData);
}, [chart_inflBod, chart_inflTn]);
useEffect(() => {
if (!chart_inflBod || !chart_inflTp) return;
const chartInflBodInflTpData = {
chartData1: chart_inflBod,
chartData2: chart_inflTp
};
setChart_inflBodInflTp(chartInflBodInflTpData);
}, [chart_inflBod, chart_inflTp]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
return (
<>
<ContentsDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig_inflowRain} chartData={chart_inflowRain} chartTitle="유입하수량-강수량" yFieldText="유입하수량(m³/일)" yFieldText2="강수량(mm)"></MainChart>
</CardContent>
</Card>
</ListDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig_rainBod} chartData={chart_bodRain} chartTitle="유입BOD-강수량" yFieldText="BOD(mg/L)" yFieldText2="강수량(mm)"></MainChart>
</CardContent>
</Card>
</ListDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_tptp} chartData={chart_tpTp} chartTitle="총인유입T-P-방류T-P" yFieldText="T-P(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입T-N-방류T-N */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_tntn} chartData={chart_tntn} chartTitle="유입T-N-방류T-N" yFieldText="T-N(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입BOD-유입T-N */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_inflBodInflTn} chartData={chart_inflBodInflTn} chartTitle="유입BOD-유입T-N" yFieldText="농도(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입BOD-유입T-P */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_inflBodInflTp} chartData={chart_inflBodInflTp} chartTitle="유입BOD-유입T-P" yFieldText="농도(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
</ContentsDiv>
</>
);
{/* <div>>
<MainChart chartForm={CHART_FORM.DN} chartConfig={chartConfig} chartData={chartData} chartTitle="유입하수량-강수량"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig} chartData={chart_inflowRain} chartTitle="유입BOD-강수량"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig} chartData={chartData} chartTitle="총인유입T-P-방류T-P"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig} chartData={chartData} chartTitle="유입T-N-방류T-N"></MainChart>
</div>
</div> */}
}
-306
View File
@@ -1,306 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, SelectBox, UITab, type SelectDataType } from "@common/business_components";
import { Button, TabsContent } from "@common/components/ui";
import { useEffect, useState } from "react";
import DashBoardFclty from "../dashboardMain/page";
/* import DrainageEquipment from "./drainageEquipment/page"; */
import ProcessFacilitySearch from "./facilityInfo/page";
import DashBoardChart from "./maingraph/page";
import RainManageTab from "./rainManage/page";
import DrainageEquipment from "./drainageEquipment/page";
const tabList = [
{ value: 'facilityInfo', label: '시설정보 및 운영현황' },
{ value: 'mainGraph', label: '주요그래프' },
{ value: 'rainManage', label: '강우일 운영현황' },
{ value: 'drainageEquipment', label: '배수설비 정비현황' }
];
export default function DashBoard() {
// 입력값 상태 관리
const { Common, initializeCommonData,searchMainGraph, searchRainManage, searchOperationStatus, OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state);
const [activeTab, setActiveTab] = useState<string>('facilityInfo');
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [sidoValue, setSidoValue] = useState<string>('0');
const [sigunValue, setSigunValue] = useState<string>('0');
const {
isInit,
selectSearchYear,
btnClass,
selectSidoData,
selectSigunData,
} = Common;
const {
planFcltyList
} = PlanFclty;
const {
operationalStatusList
} = OperationStatus;
const {
excelList
} = RainManage;
const {
chart_inflowRain,
chart_bodRain,
chart_inflTp,
chart_chongInTp,
chart_efflTp,
chart_inflTn,
chart_efflTn,
} = MainGraph;
/******************선택된 데이터******************/
// 연도
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
// 시설명
const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
bscFctCd: '',
bscFctNm: '',
fcltyCpc: '',
trtFrfNm: '',
efflWqltStndrdNm: ''
});
useEffect(() => {
if (!isInit) {
initializeCommonData();
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
}, [isInit, initializeCommonData]);
const handleFacilityChange = (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => {
setFcltyList(facility);
};
const onSelectValue = (val: string, type: string) => {
switch(type) {
case 'sido':
setSidoValue(val);
initializeSigunData(val);
onSelectValue('0', 'sigun');
break;
case 'sigun':
setSigunValue(val);
break;
}
}
// 시군구 세팅
const initializeSigunData = (sidoCd: string) => {
if (sidoCd === '0') {
setSigunValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd)
];
setSigunValue('0');
setFilteredSigunData(newFilteredData);
}
}
const excelDownload = () => {
if (!fcltyList.bscFctCd) return;
switch (activeTab) {
case 'facilityInfo':
searchOperationStatus(false, searchYear, fcltyList.bscFctCd);
fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = planFcltyList.map((item) => {
return [
item.item,
item.provisionalApproval,
item.now,
item.stepOne,
item.stepTwo,
item.stepThree,
item.stepFour,
];
});
// operationalStatusList를 2차원 배열로 변환
const excelData2 = operationalStatusList.map((item, index) => {
const baseData = [
item.yearAvg,
item.yearMax,
item.yearMin,
item.summerAll,
item.summerThree,
item.summerRain,
item.winterAll,
item.winterSunnyAvg,
item.winterSunnyMax,
item.winterSunnyMin,
item.groundWaterAvg,
item.groundWaterSummer,
item.groundWaterWinter
];
// index가 0일 때만 rdi와 rdiRain 추가
return index === 0 ? [...baseData, item.rdi, item.rdiRain] : baseData;
});
const sheetParameter = [
{
data: excelData1,
headerNum: 3,
headerRange: 'A1:G2',
sheetName: '운영실태',
startCol: 'A'
},
{
data: excelData2,
headerNum: 4,
headerRange: 'A1:Q3',
sheetName: '운영실태',
startCol: 'C'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
break;
case 'mainGraph':
searchMainGraph(searchYear, fcltyList.bscFctCd);
const sheetParameter = [
{
data: [['날짜', '강수량'],...chart_inflowRain.map((item) => [item.label, item.barVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '강수량'
},
{
data: [['날짜', '유입하수량'],...chart_inflowRain.map((item) => [item.label, item.lineVal1])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입하수량'
},
{
data: [['날짜', '시설용량'],...chart_inflowRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '시설용량'
},
{
data: [['날짜', '유입BOD'],...chart_bodRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입BOD'
},
{
data: [['날짜', '유입T-P'],...chart_inflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-P'
},
{
data: [['날짜', '총인유입T-P'],...chart_chongInTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '총인유입T-P'
},
{
data: [['날짜', '방류T-P'],...chart_efflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-P'
},
{
data: [['날짜', '유입T-N'],...chart_inflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-N'
},
{
data: [['날짜', '방류T-N'],...chart_efflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-N'
}
];
downloadMultipleExcel('주요그래프', sheetParameter);
break;
case 'rainManage':
searchRainManage(searchYear, fcltyList.bscFctCd, true);
if (excelList && excelList.length > 0) {
downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' });
}
break;
// Add other cases for different tabs as needed
default:
break;
}
}
const initVal = () => {
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
return (
<MainDiv>
<ContentsDiv>
<div className="flex card card-border !mt-6">
<div className="grid grid-cols-12 gap-5 w-8/12 m-4">
<SelectBox className="col-span-2" label={'연도'} selectArray={selectSearchYear} onSelectValue={ (val) => setSearchYear(val.toString()) } value={searchYear} selectClass={undefined}/>
<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>
</div>
</div>
<div className="w-4/12 mx-5 flex items-center justify-end space-x-2">
<Button className={btnClass} onClick={initVal}>
초기화
</Button>
<Button className={btnClass} color="green" onClick={excelDownload}>
엑셀다운로드
</Button>
</div>
</div>
<UITab tabList={tabList} defaultValue="facilityInfo" onValueChange={setActiveTab}>
<TabsContent value="facilityInfo">
<ProcessFacilitySearch searchYear={searchYear} fcltyList={[fcltyList]} onFacilityChange={handleFacilityChange} />
</TabsContent>
<TabsContent value="mainGraph">
<DashBoardChart searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="rainManage">
<RainManageTab searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="drainageEquipment">
<DrainageEquipment searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
</UITab>
</ContentsDiv>
</MainDiv>
);
}
@@ -1,59 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import { UiTable } from "@common/business_components/ui/table";
import { useEffect, useState } from "react";
export type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function RainManageTab({searchYear = '', fcltyList = []}: facilityInfoProps) {
const {
Common,
searchRainManage,
RainManage
} = useFoStore((state) => state);
const [hasSearched, setHasSearched] = useState(false);
// 이전 시설코드 저장
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const {
isInit,
} = Common;
const {
upHeadListRainManage,
rainManageList,
} = RainManage;
const tableData = fcltyList;
useEffect(() => {
if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchRainManage(searchYear, tableData[0].bscFctCd, false);
setHasSearched(true);
setPrevFacilityCode(tableData[0].bscFctCd);
}
}, [fcltyList, isInit, searchRainManage, searchYear, tableData, hasSearched, prevFacilityCode]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
return (
<UiTable
headName=""
tableData={rainManageList}
headlist={upHeadListRainManage}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
>
</UiTable>
)
}
-58
View File
@@ -1,58 +0,0 @@
"use client"
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 }) {
// 입력값 상태 관리
const { Common, initializeCommonData } = useFoStore((state) => state);
const {
isInit,
} = Common;
// 시설명
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');
const [efflWqltStndrdNm, setEfflWqltStndrdNm] = useState<string | ''>('500톤 이상(Ⅲ지역)');
useEffect(() => {
if (!isInit) {
initializeCommonData();
}
}, [isInit, initializeCommonData ]);
useEffect(() => {
if (facilityNm) {
setFcltyName(facilityNm);
}
}, [facilityNm]);
const fncNextPage = (bscFctCd?: string, bscFctNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => {
setbscFctCd(bscFctCd || '');
setFcltyName(bscFctNm || '');
setFcltyCpc(fcltyCpc || '');
setTrtFrfNm(trtFrfNm || '');
setEfflWqltStndrdNm(efflWqltStndrdNm || '');
if (bscFctCd && onFacilityChange) {
onFacilityChange({
bscFctCd: bscFctCd || '',
bscFctNm: bscFctNm || '',
fcltyCpc: fcltyCpc || '',
trtFrfNm: trtFrfNm || '',
efflWqltStndrdNm: efflWqltStndrdNm || ''
});
}
}
console.log('bscFctNm : ', bscFctNm);
return (
<>
<SearchInput label={'시설명'} setValue={fncNextPage} sidoSigun={sidoSigun} fcltyName={bscFctNm}/>
</>
);
}
@@ -1,203 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { PrecipitationPropsDetailProps } from "@/app/store/facilityPrecipitation";
import { DatePickerRange, 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, useRef, useState } from "react";
type PrecipitationModalProps = {
searchYear: string;
fcltyName: string;
facilityCd: string;
detailList: PrecipitationPropsDetailProps[];
}
type DateRange = {
start: Date;
end: Date;
}
export default function PrecipitationModal(props: PrecipitationModalProps) {
const { PrecipitationDetail } = useFoStore((state) => state);
const { upHeadListPrecipitationDetail } = PrecipitationDetail;
// Refs
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
// States
const [startDate, setStartDate] = useState<Date | null>(null);
const [endDate, setEndDate] = useState<Date | null>(null);
const [filteredDetailList, setFilteredDetailList] = useState<PrecipitationPropsDetailProps[]>([]);
const [copyDetailList, setCopyDetailList] = useState<PrecipitationPropsDetailProps[]>([]);
const getDateRanges = (): DateRange[] => {
const standYear = parseInt(props.searchYear);
return [{
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
}];
};
const isDateInRanges = (date: Date): boolean => {
return getDateRanges().some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
};
const filterDataByDateRange = (start: Date | null, end: Date | null): PrecipitationPropsDetailProps[] => {
if (!start || !end) return filteredDetailList;
return filteredDetailList.filter((item) => {
const itemDate = new Date(item.rainDt);
return itemDate >= start && itemDate <= end;
});
};
const handleStartDateSelect = (date: Date | null) => {
if (!date || !endDate) return;
if (date > endDate) {
alert('시작일은 종료일 이후일 수 없습니다.');
return;
}
const filteredData = filterDataByDateRange(date, endDate);
setCopyDetailList(filteredData);
setStartDate(date);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
};
const handleEndDateSelect = (date: Date | null) => {
if (!date || !startDate) return;
if (date < startDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const filteredData = filterDataByDateRange(startDate, date);
setCopyDetailList(filteredData);
setEndDate(date);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
};
const calculateTotals = () => {
return copyDetailList.reduce((acc, item) => ({
infoRnDay: acc.infoRnDay + Number(item.infoRnDay),
rainDay: acc.rainDay + Number(item.rainDay),
rainEffectDay: acc.rainEffectDay + Number(item.rainEffectDay)
}), { infoRnDay: 0, rainDay: 0, rainEffectDay: 0 });
};
const getTableData = () => {
const totals = calculateTotals();
return [
{
bscFctCd: "",
rainDt: '합계',
...totals
},
...copyDetailList
];
};
useEffect(() => {
if (props.detailList.length > 0) {
const filteredList = props.detailList.filter((item) =>
props.facilityCd === item.bscFctCd
);
setFilteredDetailList(filteredList);
setCopyDetailList(filteredList);
if (filteredList.length > 0) {
const firstDateStr = filteredList[0]?.rainDt;
const lastDateStr = filteredList[filteredList.length - 1]?.rainDt;
if (firstDateStr) {
const firstDate = new Date(firstDateStr);
if (!isNaN(firstDate.getTime())) {
setStartDate(firstDate);
}
}
if (lastDateStr) {
const lastDate = new Date(lastDateStr);
if (!isNaN(lastDate.getTime())) {
setEndDate(lastDate);
}
}
}
}
}, [props.detailList, props.facilityCd]);
if (!startDate || !endDate) {
return <div>로딩 중...</div>;
}
return (
<>
<ListDiv className="!h-[80px] !mt-10">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'년월'}</TableHead>
<TableCell className="w-1/3">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={startDate}
/>
</div>
<span className="mx-3">~</span>
<div ref={endPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<ListDiv className="h-[450px] overflow-y-auto">
<UiTable
headName={"강수현황"}
tableData={getTableData()}
headlist={upHeadListPrecipitationDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
boldArray={[0]}
/>
</ListDiv>
</>
);
}
@@ -1,249 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import PrecipitationModal from "./modal/page";
export default function FacilityPrecipitation() {
const {Common
,FacilityPrecipitation
,FacilityPrecipitationSearch
,PrecipitationDetail
,initializeCommonData
,searchFacilityPrecipitationList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectUpdownData,
selectOperationData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = FacilityPrecipitationSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
excelList,
facilityPrecipitation,
facilityPrecipitationCount
} = FacilityPrecipitation;
const {
gridListPrecipitationDetail
} = PrecipitationDetail;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const facilityPrecipitationList = FacilityPrecipitation?.facilityPrecipitation || [];
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (facilityPrecipitation[index]) {
setFcltyNm(facilityPrecipitation[index].bscFctNm);
setFcltyCd(facilityPrecipitation[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '시설별 강수현황',
sheetNameArray: ['시설별 강수현황'],
headerRange: 'A1:N1'
});
}
}, [excelList]);
useEffect(() => {
if (facilityPrecipitation.length > 0) {
}
}, [facilityPrecipitation]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
console.log('partValue : ', partValue);
console.log('regionValue : ', regionValue);
console.log('unitWaterShedValue : ', unitWaterShedValue);
console.log('middleRegionValue : ', middleRegionValue);
console.log('envPartValue : ', envPartValue);
console.log('sidoValue : ', sidoValue);
console.log('sigunValue : ', sigunValue);
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || operationValue!== '0' || fcltCpct !== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFacilityPrecipitationList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFacilityPrecipitationList(pageNo, pageCnt, searchYear);
}
}, [operationValue, fcltyName, searchFacilityPrecipitationList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback((searchReturn: ReturnType) => {
searchFacilityPrecipitation(searchReturn, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList = facilityPrecipitationList || [];
const total = facilityPrecipitationCount || 0;
return (
<MainDiv>
<Title title="시설별 강수현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 강수현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
<span className="text-2xl font-bold text-gray-500">☞ 강우일은 3mm 이상만 해당 / 강우영향일은 강우일 및 강우일 후 연속 2일간으로 정의</span>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<PrecipitationModal searchYear={searchYear} fcltyName={fcltyNm} facilityCd={fcltyCd} detailList={gridListPrecipitationDetail}></PrecipitationModal>}
bodyClass="sm:max-w-[90vw] w-[72vw] h-[40vw]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
@@ -1,176 +0,0 @@
import { TableHead, TableRow } from "@common/components/ui";
interface TableUpperProps {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => {
return (
layoutNum === 0 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 1 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 2 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead>
}else if(head.upName==="upChangeRe2"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 3 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe2"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 4 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={4} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 5 ? ( // 총인설비 수질현황 헤더
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="w-[180px] bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1" && head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe2" && head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
): (
<>
</>
)
)
}
export { HeadMakeColSpan };
@@ -1,571 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { FacilityWaterQltyProps, modalExcelType } from "@/app/store/facilityWaterQlty";
import { downloadExcel, UITab, UiTable } from "@common/business_components/ui";
import { Button, TabsContent } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import type { ReturnType } from "../../../components/common/FacilityTermModalHeader";
import FacilityTermModalHeader from "../../../components/common/FacilityTermModalHeader";
import { HeadMakeColSpan } from "../HeadMakeColSpan";
type ModalProps ={
fcltyName: string;
facilityCd : string;
searchYear : string;
}
const tabList1 = [
{ value: 'tab_num1', label: '처리시설 수질현황' },
{ value: 'tab_num2', label: '총인설비 수질현황' },
];
const tabList2 = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
{ value: 'tab_term_chuncheon', label: '청천시' },
{ value: 'tab_term_rain', label: '강우시' }
];
const tabList3 = [
{ value: 'tab_term_year_chongin', label: '연간' },
{ value: 'tab_term_summer_chongin', label: '하절기' },
{ value: 'tab_term_winter_chongin', label: '동절기' }
];
export default function WaterQualityModal({fcltyName='', facilityCd='', searchYear=new Date().getFullYear().toString()} : ModalProps) {
// 그리드 관련
const {FacilityWaterQltyModal,
searchFacilityWaterQltyListHist,
Common
} = useFoStore((state) => state);
const {btnClass} = Common;
const {
upHeadListModalYear,
facilityWaterQltyYearList,
upHeadListModalTms,
tmsList,
msrmtDate,
upHeadListModalChongIn,
} = FacilityWaterQltyModal;
const prevExcelListRef = useRef<modalExcelType[] | null>(null);
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: FacilityWaterQltyProps[]) => {
// 쉼표가 포함된 문자열을 숫자로 변환하는 함수
const parseNumber = (value: string | number | null | undefined): number => {
if (value === null || value === undefined || value === '') return 0;
if (typeof value === 'number') return value;
return Number(value.replace(/,/g, ''));
};
let maxEfflWqltBod = 0;
let maxEfflWqltTn = 0;
let maxEfflWqltTp = 0;
let maxefflWqltCod = 0;
let maxEfflWqltSs = 0;
let maxInflAssctAftWqltBod = 0;
let maxInflAssctAftWqltTn = 0;
let maxInflAssctBfrWqltTp = 0;
let maxInflAssctAftWqltToc = 0;
let maxInflAssctAftWqltSs = 0;
let maxTpInflWqltBod = 0;
let maxTpInflWqltTp = 0;
let maxInfoRnDay = 0;
// 초기값 설정
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 minEfflWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltSs) : 0;
let minInflAssctAftWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltBod) : 0;
let minInflAssctAftWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltTn) : 0;
let minInflAssctBfrWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltTp) : 0;
let minInflAssctAftWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltToc) : 0;
let minInflAssctAftWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltSs) : 0;
let minTpInflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltBod) : 0;
let minTpInflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltTp) : 0;
let minInfoRnDay = filteredData.length > 0 ? parseNumber(filteredData[0]?.infoRnDay) : 0;
let avgEfflWqltBod = 0;
let avgEfflWqltTn = 0;
let avgEfflWqltTp = 0;
let avgefflWqltCod = 0;
let avgEfflWqltSs = 0;
let avgInflAssctAftWqltBod = 0;
let avgInflAssctAftWqltTn = 0;
let avgInflAssctBfrWqltTp = 0;
let avgInflAssctAftWqltToc = 0;
let avgInflAssctAftWqltSs = 0;
let avgTpInflWqltBod = 0;
let avgTpInflWqltTp = 0;
let avgInfoRnDay = 0;
filteredData.forEach((item: FacilityWaterQltyProps) => {
const efflWqltBod = parseNumber(item.efflWqltBod);
const efflWqltTn = parseNumber(item.efflWqltTn);
const efflWqltTp = parseNumber(item.efflWqltTp);
const efflWqltCod = parseNumber(item.efflWqltCod);
const efflWqltSs = parseNumber(item.efflWqltSs);
const inflAssctAftWqltBod = parseNumber(item.inflAssctAftWqltBod);
const inflAssctAftWqltTn = parseNumber(item.inflAssctAftWqltTn);
const inflAssctBfrWqltTp = parseNumber(item.inflAssctBfrWqltTp);
const inflAssctAftWqltToc = parseNumber(item.inflAssctAftWqltToc);
const inflAssctAftWqltSs = parseNumber(item.inflAssctAftWqltSs);
const tpInflWqltBod = parseNumber(item.tpInflWqltBod);
const tpInflWqltTp = parseNumber(item.tpInflWqltTp);
const infoRnDay = parseNumber(item.infoRnDay);
maxEfflWqltBod = Math.max(maxEfflWqltBod, efflWqltBod);
maxEfflWqltTn = Math.max(maxEfflWqltTn, efflWqltTn);
maxEfflWqltTp = Math.max(maxEfflWqltTp, efflWqltTp);
maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod);
maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs);
maxInflAssctAftWqltBod = Math.max(maxInflAssctAftWqltBod, inflAssctAftWqltBod);
maxInflAssctAftWqltTn = Math.max(maxInflAssctAftWqltTn, inflAssctAftWqltTn);
maxInflAssctBfrWqltTp = Math.max(maxInflAssctBfrWqltTp, inflAssctBfrWqltTp);
maxInflAssctAftWqltToc = Math.max(maxInflAssctAftWqltToc, inflAssctAftWqltToc);
maxInflAssctAftWqltSs = Math.max(maxInflAssctAftWqltSs, inflAssctAftWqltSs);
maxTpInflWqltBod = Math.max(maxTpInflWqltBod, tpInflWqltBod);
maxTpInflWqltTp = Math.max(maxTpInflWqltTp, tpInflWqltTp);
maxInfoRnDay = Math.max(maxInfoRnDay, infoRnDay);
minEfflWqltBod = Math.min(minEfflWqltBod, efflWqltBod);
minEfflWqltTn = Math.min(minEfflWqltTn, efflWqltTn);
minEfflWqltTp = Math.min(minEfflWqltTp, efflWqltTp);
minefflWqltCod = Math.min(minefflWqltCod, efflWqltCod);
minEfflWqltSs = Math.min(minEfflWqltSs, efflWqltSs);
minInflAssctAftWqltBod = Math.min(minInflAssctAftWqltBod, inflAssctAftWqltBod);
minInflAssctAftWqltTn = Math.min(minInflAssctAftWqltTn, inflAssctAftWqltTn);
minInflAssctBfrWqltTp = Math.min(minInflAssctBfrWqltTp, inflAssctBfrWqltTp);
minInflAssctAftWqltToc = Math.min(minInflAssctAftWqltToc, inflAssctAftWqltToc);
minInflAssctAftWqltSs = Math.min(minInflAssctAftWqltSs, inflAssctAftWqltSs);
minTpInflWqltBod = Math.min(minTpInflWqltBod, tpInflWqltBod);
minTpInflWqltTp = Math.min(minTpInflWqltTp, tpInflWqltTp);
minInfoRnDay = Math.min(minInfoRnDay, infoRnDay);
avgEfflWqltBod += efflWqltBod;
avgEfflWqltTn += efflWqltTn;
avgEfflWqltTp += efflWqltTp;
avgefflWqltCod += efflWqltCod;
avgEfflWqltSs += efflWqltSs;
avgInflAssctAftWqltBod += inflAssctAftWqltBod;
avgInflAssctAftWqltTn += inflAssctAftWqltTn;
avgInflAssctBfrWqltTp += inflAssctBfrWqltTp;
avgInflAssctAftWqltToc += inflAssctAftWqltToc;
avgInflAssctAftWqltSs += inflAssctAftWqltSs;
avgTpInflWqltBod += tpInflWqltBod;
avgTpInflWqltTp += tpInflWqltTp;
avgInfoRnDay += infoRnDay;
});
const newFilteredData = [
{
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),
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),
inflAssctAftWqltBod: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltBod / filteredData.length * 10) / 10).toFixed(1),
inflAssctAftWqltTn: filteredData.length==0?'0.000':(Math.round(avgInflAssctAftWqltTn / filteredData.length * 1000) / 1000).toFixed(3),
inflAssctBfrWqltTp: filteredData.length==0?'0.000':(Math.round(avgInflAssctBfrWqltTp / filteredData.length * 1000) / 1000).toFixed(3),
inflAssctAftWqltToc: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltToc / filteredData.length * 10) / 10).toFixed(1),
inflAssctAftWqltSs: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltSs / filteredData.length * 10) / 10).toFixed(1),
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: filteredData.length==0?'0.0':(Math.round(avgInfoRnDay / filteredData.length * 10) / 10).toFixed(1),
tpInflWqltBod: filteredData.length==0?'0.0':(Math.round(avgTpInflWqltBod / filteredData.length * 10) / 10).toFixed(1),
tpInflWqltTp: filteredData.length==0?'0.000':(Math.round(avgTpInflWqltTp / filteredData.length * 1000) / 1000).toFixed(3)
},
{
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',
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',
inflAssctAftWqltBod : Math.round(maxInflAssctAftWqltBod * 10) / 10?(Math.round(maxInflAssctAftWqltBod * 10) / 10).toFixed(1):'0.0',
inflAssctAftWqltTn : Math.round(maxInflAssctAftWqltTn * 1000) / 1000?(Math.round(maxInflAssctAftWqltTn * 1000) / 1000).toFixed(3):'0.000',
inflAssctBfrWqltTp : Math.round(maxInflAssctBfrWqltTp * 1000) / 1000?(Math.round(maxInflAssctBfrWqltTp * 1000) / 1000).toFixed(3):'0.000',
inflAssctAftWqltToc : Math.round(maxInflAssctAftWqltToc * 10) / 10?(Math.round(maxInflAssctAftWqltToc * 10) / 10).toFixed(1):'0.0',
inflAssctAftWqltSs : Math.round(maxInflAssctAftWqltSs * 10) / 10?(Math.round(maxInflAssctAftWqltSs * 10) / 10).toFixed(1):'0.0',
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: 0,
tpInflWqltBod: Math.round(maxTpInflWqltBod * 10) / 10?(Math.round(maxTpInflWqltBod * 10) / 10).toFixed(1):'0.0',
tpInflWqltTp: Math.round(maxTpInflWqltTp * 1000) / 1000?(Math.round(maxTpInflWqltTp * 1000) / 1000).toFixed(3):'0.000'
},
{
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',
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',
inflAssctAftWqltBod : Math.round(minInflAssctAftWqltBod * 10) / 10?(Math.round(minInflAssctAftWqltBod * 10) / 10).toFixed(1) : '0.0',
inflAssctAftWqltTn : Math.round(minInflAssctAftWqltTn * 1000) / 1000?(Math.round(minInflAssctAftWqltTn * 1000) / 1000).toFixed(3) : '0.000',
inflAssctBfrWqltTp : Math.round(minInflAssctBfrWqltTp * 1000) / 1000?(Math.round(minInflAssctBfrWqltTp * 1000) / 1000).toFixed(3) : '0.000',
inflAssctAftWqltToc : Math.round(minInflAssctAftWqltToc * 10) / 10?(Math.round(minInflAssctAftWqltToc * 10) / 10).toFixed(1) : '0.0',
inflAssctAftWqltSs : Math.round(minInflAssctAftWqltSs * 10) / 10?(Math.round(minInflAssctAftWqltSs * 10) / 10).toFixed(1) : '0.0',
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: 0,
tpInflWqltBod: Math.round(minTpInflWqltBod * 10) / 10?'0.0':(Math.round(minTpInflWqltBod * 10) / 10).toFixed(1),
tpInflWqltTp: Math.round(minTpInflWqltTp * 1000) / 1000?'0.000':(Math.round(minTpInflWqltTp * 1000) / 1000).toFixed(3)
},
...filteredData
];
return newFilteredData;
}
// 날짜 필터링 관련 상태
const [copyYearList, setCopyYearList] = useState<FacilityWaterQltyProps[]>([]);
const [chongInWaterQltyYearList, setChongInWaterQltyYearList] = useState<FacilityWaterQltyProps[]>([]);
useEffect(() => {
searchFacilityWaterQltyListHist(searchYear, facilityCd);
}, [searchYear, facilityCd, searchFacilityWaterQltyListHist]);
useEffect(() => {
const filteredYearData = setFilterAfterData(facilityWaterQltyYearList);
setCopyYearList(filteredYearData as FacilityWaterQltyProps[]);
setChongInWaterQltyYearList(filteredYearData as FacilityWaterQltyProps[]);
}, [facilityWaterQltyYearList]);
const yesterDay = new Date();
yesterDay.setDate(yesterDay.getDate() - 1);
const yesterDayStr = yesterDay.toISOString().split('T')[0];
const ref = useRef<ReturnType<FacilityWaterQltyProps>>({
startDate: new Date(),
endDate: new Date(),
tabName: '',
filteredList: [],
});
const filterData = useCallback((returnType: ReturnType<FacilityWaterQltyProps>) => {
const filteredData = setFilterAfterData(returnType.filteredList);
setCopyYearList(filteredData as FacilityWaterQltyProps[]);
}, []);
const excelDownload = () => {
const excelDownList: modalExcelType[] = [];
if(copyYearList.length > 0){
copyYearList.forEach((item) => {
excelDownList.push({
날짜: item.msrDt,
"유입BOD(mg/L)": item.inflAssctAftWqltBod,
"유입TOC(COD)(mg/L)": item.inflAssctAftWqltToc,
"유입SS(mg/L)": item.inflAssctAftWqltSs,
"유입TN(mg/L)": item.inflAssctAftWqltTn,
"유입TP(mg/L)": item.inflAssctBfrWqltTp,
"유출BOD(mg/L)": item.efflWqltBod,
"유출TOC(COD)(mg/L)": item.efflWqltCod,
"유출SS(mg/L)": item.efflWqltSs,
"유출TN(mg/L)": item.efflWqltTn,
"유출TP(mg/L)": item.efflWqltTp,
"강우량(일)": 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' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelDownList;
}
return (
<>
<UITab tabList={tabList1} defaultValue="tab_num1">
<Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}>
{"엑셀다운로드"}
</Button>
{/* 처리시설 수질현황 */}
<TabsContent value="tab_num1">
<UITab tabList={tabList2} defaultValue="tab_term_year">
<TabsContent value="tab_term_year">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName={"tab_term_year"}
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[350px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[310px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</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>
</div>
<div className="overflow-y-auto h-[250px]">
<UiTable
headName={undefined}
tableData={tmsList}
headlist={upHeadListModalTms}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalTms} layoutNum={4} />
</UiTable>
{msrmtDate != yesterDayStr?.replaceAll('-','')?<h3 className="text-2xl text-left text-gray-500">※전날 데이터 결측으로 인해 가장 최근 데이터 표출함</h3> : ''}
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => { } }
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_winter"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_chuncheon">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_chuncheon"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_rain">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_rain"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
</UITab>
</TabsContent>
{/* 총인설비 수질현황 */}
<TabsContent value="tab_num2">
<UITab tabList={tabList3} defaultValue="tab_term_year_chongin">
<TabsContent value="tab_term_year_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_year"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_summer_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_winter_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_winter"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
</UITab>
</TabsContent>
</UITab>
</>
);
}
@@ -1,208 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import WaterQualityModal from "./modal/page";
import type { FacilityWaterQltyProps } from "@/app/store/facilityWaterQlty";
export default function ProcessFacilitySearch() {
const { Common
, FacilityWaterQltyMain
, initializeCommonData
, searchFacilityWaterQltyList
} = useFoStore((state) => state) || { FacilityWaterQltyList: { facilityWaterQltyList: [], facilityWaterQltyCount: 0 } };
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
mainLabelArray,
upHeadList1,
upHeadList2,
upHeadList3,
facilityWaterQltyList,
facilityWaterQltyCount,
excelList
} = FacilityWaterQltyMain;
const mainList: FacilityWaterQltyProps[] = facilityWaterQltyList || [];
// 그리드 관련
const [resetPage, setResetPage] = useState<boolean>(false);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
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 [fcltCpct, setFcltCpct] = useState<string>("0");
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const [modalYear, setModalYear] = useState<string>("");
const searchFcltyQltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr;
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue);
}
}, [operationValue, fcltyName, searchFacilityWaterQltyList, searchYearValue, fcltCpct]);
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYearValue(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0");
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
const facility = facilityWaterQltyList[index];
if (facility) {
setFcltyNm(facility.bscFctNm);
setFcltyCd(facility.bscFctCd);
setModalYear(searchYearValue);
setSelectedRow(index); // 클릭한 행의 index 저장
}
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
}
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFacilityWaterQltyList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '시설별 수질현황',
sheetNameArray: ['시설별 수질현황'],
headerRange: 'A1:T1'
});
}
}, [excelList]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
const excelDownload = useCallback(() => {
searchFcltyQltyList(ref.current, 1, 10000);
}, [searchFcltyQltyList]);
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFcltyQltyList(ref.current, page, 10);
}, [searchFcltyQltyList]);
const total = facilityWaterQltyCount || 1;
console.log('total : ', total);
return (
<MainDiv>
<Title title="시설별 수질현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={mainLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={mainLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={mainLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={mainLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 수질현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total<10?facilityWaterQltyList.length:total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>}
bodyClass="sm:max-w-[90vw] w-[1200px] h-[920px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
-310
View File
@@ -1,310 +0,0 @@
"use client"
import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHeader";
import { useFoStore } from "@/app/store";
import type { FlowRateModalInterface, modalExcelType } from "@/app/store/flowRate";
import { downloadExcel, UITab, UiTable } from "@common/business_components/ui";
import { Button, TabsContent } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import type { ReturnType } from "../../../components/common/FacilityTermModalHeader";
const tabList = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
{ value: 'tab_term_chuncheon', label: '청천시' },
{ value: 'tab_term_rain', label: '강우시' }
];
export default function FlowRateModal(props: { fcltyName: string; facilityCd: string; searchYear: string }) {
// 그리드 관련
const { Common, FlowRateModal, searchFlowRateModalList } = useFoStore((state) => state);
const { btnClass } = Common;
const [copyYearList, setCopyYearList] = useState<FlowRateModalInterface[]>([]);
const [isLoading, setIsLoading] = useState(true);
const prevExcelListRef = useRef<modalExcelType[] | null>(null);
const {
modalFlowRateListYear,
upHeadListFlowRateModalYear,
} = FlowRateModal;
const ref = useRef<ReturnType<FlowRateModalInterface>>({
startDate: new Date(),
endDate: new Date(),
tabName: '',
filteredList: [],
});
const filterData = useCallback((returnType: ReturnType<FlowRateModalInterface>) => {
if (!returnType.filteredList) return;
// 타입을 FlowRateModalInterface[]로 강제 변환하여 타입 오류 해결
const filteredData = setFilterAfterData(returnType.filteredList as FlowRateModalInterface[]);
setCopyYearList(filteredData);
}, []);
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: FlowRateModalInterface[]) => {
if (!filteredData || filteredData.length === 0) return [];
// 쉼표가 포함된 문자열을 숫자로 변환하는 함수
const parseNumber = (value: string | number | null | undefined): number => {
if (value === null || value === undefined || value === '') return 0;
if (typeof value === 'number') return value;
return Number(value.replace(/,/g, ''));
};
let maxtotInflAmnt = 0;
let maxinflAmnt = 0;
let maxsumFlow = 0;
let maxreturnAmnt = 0;
let maxefflAmnt = 0;
let maxinfoRnDay = 0;
// 초기값 설정
let mintotInflAmnt = parseNumber(filteredData[0]?.totInflAmnt);
let mininflAmnt = parseNumber(filteredData[0]?.inflAmnt);
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 avgtotInflAmnt = 0;
let avginflAmnt = 0;
let avgsumFlow = 0;
let avgreturnAmnt = 0;
let avgefflAmnt = 0;
let avginfoRnDay = 0;
filteredData.forEach((item: FlowRateModalInterface) => {
const totInflAmnt = parseNumber(item.totInflAmnt);
const inflAmnt = parseNumber(item.inflAmnt);
const sumFlow = parseNumber(item.sumFlow);
const returnAmnt = parseNumber(item.returnAmnt);
const efflAmnt = parseNumber(item.efflAmnt);
const infoRnDay = parseNumber(item.infoRnDay);
maxtotInflAmnt = Math.max(maxtotInflAmnt, totInflAmnt);
maxinflAmnt = Math.max(maxinflAmnt, inflAmnt);
maxsumFlow = Math.max(maxsumFlow, sumFlow);
maxreturnAmnt = Math.max(maxreturnAmnt, returnAmnt);
maxefflAmnt = Math.max(maxefflAmnt, efflAmnt);
maxinfoRnDay = Math.max(maxinfoRnDay, infoRnDay);
mintotInflAmnt = Math.min(mintotInflAmnt, totInflAmnt);
mininflAmnt = Math.min(mininflAmnt, inflAmnt);
minsumFlow = Math.min(minsumFlow, sumFlow);
minreturnAmnt = Math.min(minreturnAmnt, returnAmnt);
minefflAmnt = Math.min(minefflAmnt, efflAmnt);
mininfoRnDay = Math.min(mininfoRnDay, infoRnDay);
avgtotInflAmnt += totInflAmnt;
avginflAmnt += inflAmnt;
avgsumFlow += sumFlow;
avgreturnAmnt += returnAmnt;
avgefflAmnt += efflAmnt;
avginfoRnDay += infoRnDay;
});
const newFilteredData = [
{
msrDt: '평균',
totInflAmnt: Math.round(avgtotInflAmnt / filteredData.length * 10) / 10,
inflAmnt: Math.round(avginflAmnt / filteredData.length * 10) / 10,
sumFlow: Math.round(avgsumFlow / filteredData.length * 10) / 10,
returnAmnt: Math.round(avgreturnAmnt / filteredData.length * 10) / 10,
efflAmnt: Math.round(avgefflAmnt / filteredData.length * 10) / 10,
infoRnDay: avginfoRnDay > 0 ? Math.round(avginfoRnDay / filteredData.length * 10) / 10 : 0,
rainEffectDay: 0,
},
{
msrDt: '최대',
totInflAmnt: Math.round(maxtotInflAmnt * 10) / 10,
inflAmnt: Math.round(maxinflAmnt * 10) / 10,
sumFlow: Math.round(maxsumFlow * 10) / 10,
returnAmnt: Math.round(maxreturnAmnt * 10) / 10,
efflAmnt: Math.round(maxefflAmnt * 10) / 10,
infoRnDay: maxinfoRnDay > 0 ? (Math.round(maxinfoRnDay * 10) / 10).toFixed(1) : '0.0',
rainEffectDay: 0,
},
{
msrDt: '최소',
totInflAmnt: Math.round(mintotInflAmnt * 10) / 10,
inflAmnt: Math.round(mininflAmnt * 10) / 10,
sumFlow: Math.round(minsumFlow * 10) / 10,
returnAmnt: Math.round(minreturnAmnt * 10) / 10,
efflAmnt: Math.round(minefflAmnt * 10) / 10,
infoRnDay: mininfoRnDay > 0 ? (Math.round(mininfoRnDay * 10) / 10).toFixed(1) : '0.0',
rainEffectDay: 0,
},
...filteredData
];
return newFilteredData;
}
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
await searchFlowRateModalList(props.facilityCd, props.searchYear);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [props.facilityCd, props.searchYear, searchFlowRateModalList]);
if (isLoading) {
return <div>로딩 중...</div>;
}
const excelDownload = () => {
const excelDownList: modalExcelType[] = [];
if(copyYearList.length > 0){
copyYearList.forEach((item) => {
excelDownList.push({
"날짜": item.msrDt,
"총유입량(m³/일)": item.totInflAmnt,
"하수유입량(m³/일)": item.inflAmnt,
"연계처리량(m³/일)": item.sumFlow,
"총인반류수량(m³/일)": item.returnAmnt,
"방류량(m³/일)": item.efflAmnt,
"강우량": item.infoRnDay,
});
});
}
if (excelDownList && excelDownList.length > 0) {
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 유량현황', sheetNameArray: [props.fcltyName + ' 유량현황'], headerRange: 'A1:G1' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelDownList;
}
return (
<>
<UITab tabList={tabList} defaultValue="tab_term_year">
<Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}>
{"엑셀다운로드"}
</Button>
<TabsContent value="tab_term_year">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_year"}
searchYear={props.searchYear}
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>
</TabsContent>
<TabsContent value="tab_term_summer">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_summer"}
searchYear={props.searchYear}
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>
</TabsContent>
<TabsContent value="tab_term_winter">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_winter"}
searchYear={props.searchYear}
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>
</TabsContent>
<TabsContent value="tab_term_chuncheon">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_chuncheon"}
searchYear={props.searchYear}
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>
</TabsContent>
<TabsContent value="tab_term_rain">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_rain"}
searchYear={props.searchYear}
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>
</TabsContent>
</UITab>
</>
);
}
-207
View File
@@ -1,207 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import FlowRateModal from "./modal/page";
import type { ExcelType } from "@/app/store/flowRate";
export default function ProcessFacilitySearch() {
const {Common
,FlowRateSearch
,FlowRateList
,initializeCommonData
,searchFlowRateList
} = useFoStore((state) => state);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const {
isInit,
selectUpdownData,
selectSearchYear,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
btnClass
} = Common;
const {
flowRateSearchLabelArray
} = FlowRateSearch;
// 그리드 관련
const {
upHeadList1,
upHeadList2,
upHeadList3,
flowRateList,
flowRateCount,
excelList
} = FlowRateList;
// 입력값 상태 관리
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 [widthSize, setWidthSize] = useState<string>("w-[120%]");
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
const [resetPage, setResetPage] = useState<boolean>(false);
const prevExcelListRef = useRef<ExcelType[] | null>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const searchFlowRate = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFlowRateList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFlowRateList(pageNo, pageCnt, searchYearValue);
}
}, [operationValue, fcltyName, searchFlowRateList, searchYearValue, fcltCpct]);
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYearValue(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (flowRateList[index]) {
setFcltyNm(flowRateList[index].bscFctNm);
setFcltyCd(flowRateList[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
// 페이지 네비게이션션
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFlowRate(ref.current, page, 10);
}, [searchFlowRate]);
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 시도 데이터 초기화
useEffect(() => {
if(!isInit) {
initializeCommonData();
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({ data: JSON.parse(JSON.stringify(excelList)), fileName: '시설별 유량현황', sheetNameArray: ['시설별 유량현황'], headerRange: 'A1:O1' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelList;
}, [excelList]);
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
const excelDownload = useCallback(() => {
searchFlowRate(ref.current, 1, 10000);
}, [searchFlowRate]);
const total = flowRateCount;
return (
<MainDiv>
<Title title="시설별 유량현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={flowRateSearchLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 유량현황"} tableData={flowRateList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>}
bodyClass="sm:max-w-[90vw] w-[1050px] h-[700px]"
open={selectedRow !== null}
onOpenChange={(open: boolean) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
-145
View File
@@ -1,145 +0,0 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { useEffect, useRef, useState } from "react";
interface FeatureData {
fid?: string;
ftrIdn?: string;
id?: string;
getGeometry?: () => { getExtent: () => number[] };
setStyle?: () => void;
}
interface MapInstance {
getLayers: () => { getArray: () => Array<{ getSource?: () => { getFeatureById?: (id: string) => FeatureData | null } }> };
getView: () => { fit: (extent: number[], options: object) => void };
}
const Map = () => {
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
const gisMaps = useRef<MapInstance | null>(null);
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
const [featureData, setFeatureData] = useState<FeatureData | null>(null); // 선택된 피쳐 데이터
const fetchData = async () => {
try {
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
const reqUrl = `${API_URL}/lyrStngs/FO_NETWORK`;
const response = await fetch(reqUrl, {
method: "GET",
headers: {
accept: "application/json",
}
});
if (!response.ok) {
throw new Error(`HTTP 오류! 상태: ${response.status}`);
}
const data = await response.json(); // 여기서 json 파싱
setTreeData(data); // 데이터 설정
} catch (error) {
console.error("데이터 불러오기 실패:", error);
}
};
// 컴포넌트 마운트 시 1회 실행
useEffect(() => {
fetchData();
}, []);
// SearchModal, GisContent 등에서 호출
const onFeatureSelect = (data: FeatureData) => {
const map = gisMaps.current;
if (!map) {
console.warn("지도가 초기화되지 않았습니다.");
return;
}
// 항상 같은 select 객체 사용
const select = (GisApp.Module.select as unknown) as { resetSelectedFeature: () => void; selectFeature: (feature: FeatureData, selected: boolean) => void } | undefined;
if (!select) return;
// 1. 기존 선택 초기화
select.resetSelectedFeature();
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature: FeatureData | null = null;
if (
data &&
typeof data.getGeometry === "function" &&
typeof data.setStyle === "function"
) {
// 이미 Feature 인스턴스라면 그대로 사용
foundFeature = data;
} else {
// 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
const featureId = data.fid || data.ftrIdn || data.id;
map
.getLayers()
.getArray()
.forEach((layer) => {
const source = layer.getSource?.();
if (source?.getFeatureById) {
const feature = source.getFeatureById(String(featureId).trim());
if (feature) foundFeature = feature;
}
});
}
// 3. 피처 선택
if (foundFeature && foundFeature.getGeometry) {
const geometry = foundFeature.getGeometry();
if (geometry) {
const extent = geometry.getExtent();
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16,
constrainResolution: true,
nearest: true,
});
}
select.selectFeature(foundFeature, true);
} else {
console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
}
setFeatureData(data);
setIsOpen(true);
setIsSearchModalOpen(false);
};
// onCheckedLayersChange handler
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: unknown[]) => {
// Handle checked layers change if needed
console.log('Checked layers changed:', checkedIds, checkedNodesData);
};
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={false}
gisMaps={gisMaps}
isMiniMap={true}
searchView={null}
detailView={null}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={handleCheckedLayersChange}
/>
</div>
);
};
export default Map;
-197
View File
@@ -1,197 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
export default function PlanSewage() {
const {Common
,PlanSewage
,PlanSewageSearch
,initializeCommonData
,searchPlanSewageList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = PlanSewageSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
planSewageList,
planSewageCount,
excelList
} = PlanSewage;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1);
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchPlanSewageList(1, 10, searchYear, ref.current.partValue);
}
}, [isInit, initializeCommonData, searchYear, searchPlanSewageList]);
useEffect(() => {
console.log('excelList' , excelList);
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '계획하수량 산정지표 현황',
sheetNameArray: ['계획하수량 산정지표 현황'],
headerRange: 'A1:M1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchPlanSewage = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchPlanSewageList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchPlanSewageList(pageNo, pageCnt, searchYear, partValue);
}
}, [operationValue, fcltyName, searchPlanSewageList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback((searchReturn: ReturnType) => {
searchPlanSewage(searchReturn, 1, 10000);
}, [searchPlanSewage]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchPlanSewage(ref.current, page, 10);
}, [searchPlanSewage]);
const mainList = planSewageList || [];
const total = planSewageCount || 0;
return (
<MainDiv>
<Title title="계획하수량 산정지표 현황" />
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false}/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</MainDiv>
);
}
@@ -1,87 +0,0 @@
import { TableHead, TableRow } from "@common/components/ui";
import type { TableUpperProps } from "../../store/dashboard";
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => {
return (
layoutNum === 0 ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={2}>{'행정구역'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> :
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={19}>{'시설계획'}</TableHead> :
!head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => (
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
null
))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : layoutNum === 1 ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={3}>{'유역'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> :
!head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => (
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
null
))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe1' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'권역'}</TableHead> :
head.upName === 'upChangeRe2' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'환경청'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' ||head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
);
}
export { HeadMakeColSpan };
@@ -1,55 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import { UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui";
import { useEffect } from "react";
export default function ProcessFacilityInfoModal(props: { fcltyName: string; facilityCd : string;}) {
// 그리드 관련
const {ProcessAreaDetail, initModalData} = useFoStore((state) => state);
useEffect(() => {
initModalData(props.facilityCd);
}, [initModalData, props.facilityCd]);
const {
upHeadListFcltyImproveInfo,
gridListFcltyImproveInfo,
upHeadListProcessPopulation,
gridListProcessPopulation,
upHeadListProcessAreaPlan,
gridListProcessAreaPlan,
} = ProcessAreaDetail
return (
<>
<ListDiv>
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설코드'}</TableHead>
<TableCell className="w-1/3">{props.facilityCd}</TableCell>
</TableRow>
</TableHeader>
</Table>
</div>
</ListDiv>
<ListDiv>
<UiTable headName={"시설증설 계획"} tableData={gridListFcltyImproveInfo} headlist={upHeadListFcltyImproveInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
<ListDiv>
<UiTable headName={"처리인구 계획"} tableData={gridListProcessPopulation} headlist={upHeadListProcessPopulation} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
<ListDiv>
<UiTable headName={"처리구역 계획"} tableData={gridListProcessAreaPlan} headlist={upHeadListProcessAreaPlan} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
</>
);
}
-172
View File
@@ -1,172 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import ProcessFacilityInfoModal from "./modal/page";
export default function ProcessArea() {
const {Common,
ProcessArea,
ProcessAreaSearch,
initializeCommonData,
searchProcessAreaList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
ProcessArea: { processAreaList: [], processAreaCount: 0, excelList: [], upHeadList1: [] },
ProcessAreaSearch: { processFacilityLabelArray: [] },
initializeCommonData: () => {},
searchProcessAreaList: () => {}
};
const {
isInit,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass,
} = Common;
const {
processFacilityLabelArray,
} = ProcessAreaSearch;
const {
upHeadList1,
processAreaList,
processAreaCount,
excelList
} = ProcessArea;
const mainList: Record<string, unknown>[] = processAreaList || [];
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const [resetPage, setResetPage] = useState<boolean>(false);
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const searchProcessArea = useCallback(({ sidoText, sigunText}: ReturnType, pageNo: number, pageCnt:number=20) => {
searchProcessAreaList(pageNo, pageCnt, sidoText, sigunText, searchYearValue);
}, [searchProcessAreaList, searchYearValue]);
// 조회
const chkVal = useCallback(() => {
setResetPage(true);
}, []);
// 초기화
const resetVal = () => {
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
setSearchYearValue(selectSearchYear[0]?.val || "0");
searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val);
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (processAreaList[index]) {
setFcltyNm(processAreaList[index].swtrmtFcltNm);
setFcltyCd(processAreaList[index].swtrmtFcltCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val);
}
}, [isInit, initializeCommonData, searchProcessAreaList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '처리구역정보',
sheetNameArray: ['처리구역정보'],
headerRange: 'A1:W1'
});
}
}, [excelList]);
const excelDownload = useCallback(() => {
searchProcessArea(ref.current, 1, 10000);
}, [searchProcessArea]);
const handlePageChange = useCallback((pageNo: number) => {
setResetPage(false);
searchProcessArea(ref.current, pageNo, 10);
}, [searchProcessArea]);
const total = processAreaCount;
return (
<MainDiv>
<Title title="처리구역정보" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined}/>
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={true}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className="w-[180%]">
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"처리구역정보"} tableData={mainList} headlist={upHeadList1} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<ProcessFacilityInfoModal fcltyName={fcltyNm} facilityCd={fcltyCd}></ProcessFacilityInfoModal>}
bodyClass="sm:max-w-[90vw] w-[1200px] h-[900px]"
open={selectedRow !== null}
onOpenChange={(open: boolean) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
@@ -1,211 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/dashboard";
import { UITab, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import {
TabsContent
} from "@common/components";
import { Table, TableCell, TableHead, TableRow } from "@common/components/ui";
type ModalType = {
fcltyName: string;
facilityCd : string;
sido : string;
gugun : string;
daddr : string;
fctCpc : number | '';
trtFrfNm : string;
rnDt : string;
cmplDt : string;
trstOprEtpNm : string;
bscFctClsNm : string;
mnstr : string;
efflWtrrgn : string;
lgbsn : string;
efflWqltStndrdNm : string;
rgnNm : string;
rvbsn : string;
}
// 시설계획 table header 변경
const headMakeList = (upHeadList: TableUpperProps[], gridNum:number) => {
if(gridNum===1){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'증설계획(m³/일)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe'?<TableHead className="w-150" key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===2){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===3){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} colSpan={2} style={{ whiteSpace: 'pre-line' }}>{'사업기간'}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===4){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe3'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획방류수질(mg/L)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
}
export default function ProcessFacilityInfoModal(props: ModalType) {
// 그리드 관련
const {ProcessFacilityDetail} = useFoStore((state) => state);
const {
upHeadListPlanInfo,
gridListPlanInfo,
upHeadListFcltyAreaInfo,
gridListFcltyAreaInfo,
upHeadListFcltyHistInfo,
gridListFcltyHistInfo,
upHeadListFcltyWaterQltyInfo,
gridListFcltyWaterQltyInfo,
upHeadListLegalWaterQlty,
gridListLegalWaterQlty,
} = ProcessFacilityDetail;
gridListFcltyAreaInfo.map((item) => {
item.efflWtrrgn = props.efflWtrrgn;
item.lgbsn = props.lgbsn;
item.efflWqltStndrdNm = props.efflWqltStndrdNm;
item.rgnNm = props.rgnNm;
item.rvbsn = props.rvbsn;
})
const tabList = [
{ value: 'common_info', label: '기본정보' },
{ value: 'history_info', label: '이력정보' },
];
const fctCpc = props.fctCpc ? props.fctCpc.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
const upHeadListPlanInfoChange = headMakeList(upHeadListPlanInfo, 2);
const upHeadListFcltyHistInfoChange = headMakeList(upHeadListFcltyHistInfo, 3);
const upHeadListFcltyWaterQltyInfoChange = headMakeList(upHeadListFcltyWaterQltyInfo, 4);
return (
<>
<ListDiv className="!h-[80px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설코드'}</TableHead>
<TableCell className="w-1/3">{props.facilityCd}</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto">
<div className="!h-[600px]">
<ListDiv className="">
<UITab tabList={tabList} defaultValue="common_info">
<TabsContent value="common_info">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">시설 기본정보</h3>
</div>
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/6">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설구분'}</TableHead>
<TableCell className="w-1/6">{props.bscFctClsNm}</TableCell>
<TableHead className="w-1/6">{'시설용량(m³/일)'}</TableHead>
<TableCell className="w-1/6">{fctCpc}</TableCell>
</TableRow>
</thead>
<tbody>
<TableRow className="">
<TableHead className="w-1/6">{'시도'}</TableHead>
<TableCell className="w-1/6">{props.sido}</TableCell>
<TableHead className="w-1/6">{'시군구'}</TableHead>
<TableCell className="w-1/6">{props.gugun}</TableCell>
<TableHead className="w-1/6">{'가동개시일'}</TableHead>
<TableCell className="w-1/6">{props.rnDt}</TableCell>
</TableRow>
<TableRow className="">
<TableHead className="w-1/6">{'주소'}</TableHead>
<TableCell className="w-1/6" colSpan={3}>{props.daddr}</TableCell>
<TableHead className="w-1/6">{'준공일'}</TableHead>
<TableCell className="w-1/6">{props.cmplDt}</TableCell>
</TableRow>
<TableRow className="">
<TableHead className="w-1/6">{'공법'}</TableHead>
<TableCell className="w-1/6" colSpan={3}>{props.trtFrfNm}</TableCell>
<TableHead className="w-1/6">{'관리대행업자'}</TableHead>
<TableCell className="w-1/6">{props.trstOprEtpNm}</TableCell>
</TableRow>
</tbody>
</Table>
</div>
<UiTable headName={"시설 계획정보"} tableData={gridListPlanInfo} headlist={upHeadListPlanInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListPlanInfoChange}
</UiTable>
<UiTable headName={"시설 법적방류수질"} tableData={gridListLegalWaterQlty} headlist={upHeadListLegalWaterQlty} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} />
<UiTable headName={"시설 수역 및 구역정보"} tableData={gridListFcltyAreaInfo} headlist={upHeadListFcltyAreaInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} />
</TabsContent>
<TabsContent value="history_info">
<ListDiv>
<div className="h-[270px] w-full overflow-y-auto overflow-x-hidden">
<div className="h-[130%]">
<UiTable headName={"시설 이력관리"} tableData={gridListFcltyHistInfo} headlist={upHeadListFcltyHistInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListFcltyHistInfoChange}
</UiTable>
</div>
</div>
</ListDiv>
<ListDiv>
<div className="h-[270px] w-full overflow-y-auto overflow-x-hidden">
<div className="h-[130%]">
<UiTable headName={"시설 수질정보"} tableData={gridListFcltyWaterQltyInfo} headlist={upHeadListFcltyWaterQltyInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListFcltyWaterQltyInfoChange}
</UiTable>
</div>
</div>
</ListDiv>
</TabsContent>
</UITab>
</ListDiv>
</div>
</div>
</>
);
}
-294
View File
@@ -1,294 +0,0 @@
"use client"
import { HeadMakeColSpan } from "@/app/components/common/HeadMakeColSpan";
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import ProcessFacilityInfoModal from "./modal/page";
export default function ProcessFacilitySearch() {
const {Common
,ProcessFacility
,ProcessFacilityList
,initializeCommonData
//시설데이터 조회
,searchProcessFacility
//시설데이터 이력정보 조회
,searchProcessFacilityHist
//시설데이터 수질정보 조회
,searchProcessFacilityWaterQlty
} = useFoStore((state) => state) || { ProcessFacilityList: { processFacilityList: [], processFacilityCount: 0 }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectFacilityPartData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
btnClass,
} = Common;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
useEffect(() => {
if(!isInit) {
initializeCommonData();
searchProcessFacility(1, 10, 'A', '0', '0', '0', '0');
}
}, [isInit
,initializeCommonData
,searchProcessFacility
]);
const {
processFacilityLabelArray,
} = ProcessFacility;
const {
upHeadList1,
upHeadList2,
upHeadList3,
excelList
} = ProcessFacilityList;
const processFacilityList = ProcessFacilityList?.processFacilityList || [];
// 그리드 관련
const inputRef = useRef<HTMLInputElement>(null);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const [fcltCpct, setFcltCpct] = useState<string>("0");
const [fcltyName, setFcltyName] = useState<string>("");
const [operationValue, setOperationValue] = useState<string>("0");
const [facilityPartValue, setFacilityPartValue] = useState<string>("0");
const [resetPage, setResetPage] = useState<boolean>(false);
const [partValue, setPartValue] = useState<string>("A");
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
const [upHeadList, setUpheadList] = useState(upHeadList1);
/**
* 모달 상태 변수
* selectedRow : 클릭한 행의 index
* modalFcltyNm : 시설명
* modalFcltyCd : 시설코드
* modalSido : 시도
* modalGugun : 시군구
* modalLaeNm : 주소
* modalFctCpc : 시설용량
* modalTrtFrfNm : 공법
* modalRnDt : 가동개시일
* modalCmplDt : 준공일
* modalTrstOprEtpNm : 관리대행업자
* modalBscFctClsNm : 시설구분
*/
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [modalFcltyNm, setModalFcltyNm] = useState<string>("");
const [modalFcltyCd, setModalFcltyCd] = useState<string>("");
const [modalSido, setModalSido] = useState<string>("");
const [modalGugun, setModalGugun] = useState<string>("");
const [modalAddr, setModalAddr] = useState<string>("");
const [modalFctCpc, setModalFctCpc] = useState<number | ''>("");
const [modalTrtFrfNm, setModalTrtFrfNm] = useState<string>("");
const [modalRnDt, setModalRnDt] = useState<string>("");
const [modalCmplDt, setModalCmplDt] = useState<string>("");
const [modalTrstOprEtpNm, setModalTrstOprEtpNm] = useState<string>("");
const [modalBscFctClsNm, setModalBscFctClsNm] = useState<string>("");
const [modalMnstr, setModalMnstr] = useState<string>("");
const [modalEfflWtrrgn, setModalEfflWtrrgn] = useState<string>("");
const [modalLgbsn, setModalLgbsn] = useState<string>("");
const [modalEfflWqltStndrdNm, setModalEfflWqltStndrdNm] = useState<string>("");
const [modalRgnNm, setModalRgnNm] = useState<string>("");
const [modalRvbsn, setModalRvbsn] = useState<string>("");
const [isSearch, setIsSearch] = useState<boolean>(false);
// 표 레이아웃 상태
const chkVal = useCallback((result:ReturnType) => {
setPartValue(result.partValue);
setResetPage(true);
setIsSearch(true);
}, []);
useEffect(() => {
switch (partValue) {
case 'A':
setWidthSize("w-[130%]");
setUpheadList(upHeadList1);
break;
case 'R':
setWidthSize("w-[140%]");
setUpheadList(upHeadList2);
break;
case 'G':
setWidthSize("w-[150%]");
setUpheadList(upHeadList3);
break;
}
}, [partValue, upHeadList1, upHeadList2, upHeadList3]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({ data: excelList as unknown as JSON[], fileName: '처리시설 시설정보', sheetNameArray: ['처리시설 시설정보'], headerRange: 'A1:P1' });
}
}, [excelList]);
const searchFcltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=10) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
searchProcessFacility(pageNo, pageCnt, partStr, nameCd || '0', fcltCpct, operationValue, facilityPartValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
if(isSearch) {
setIsSearch(false);
}
}, [facilityPartValue, fcltCpct, fcltyName, isSearch, operationValue, searchProcessFacility])
const changePage = useCallback((page: number) => {
searchFcltyList(ref.current, page);
},[searchFcltyList])
const resetVal = () => {
// SelectBox 초기화 - 각 selectArray의 첫 번째 옵션 값 사용
searchProcessFacility(1, 10, 'A', '0', '0', '0', '0', '');
setFcltCpct("0");
setOperationValue("0");
setFacilityPartValue("0");
setPartValue('A');
setUpheadList(upHeadList1);
// SearchInputSelect 초기화
setFcltyName("");
// ref를 사용하여 input 값 초기화
if (inputRef.current) {
inputRef.current.value = "";
}
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
const facility = processFacilityList[index];
if (facility) {
setModalFcltyNm(facility.bscFctNm);
setModalFcltyCd(facility.bscFctCd);
setModalSido(facility.sido);
setModalGugun(facility.gugun);
setModalAddr(facility.daddr);
setModalFctCpc(Number(facility.fctCpc));
setModalTrtFrfNm(facility.trtFrfNm);
setModalRnDt(facility.rnDt);
setModalCmplDt(facility.cmplDt);
setModalTrstOprEtpNm(facility.trstOprEtpNm);
setModalBscFctClsNm(facility.bscFctClsNm);
setModalMnstr(facility.mnstr);
setModalEfflWtrrgn(facility.efflWtrrgn);
setModalLgbsn(facility.lgbsn);
setModalEfflWqltStndrdNm(facility.efflWqltStndrdNm);
setModalRgnNm(facility.rgnNm);
setModalRvbsn(facility.rvbsn);
searchProcessFacilityHist(facility.bscFctCd);
searchProcessFacilityWaterQlty(facility.bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
}
const excelDownload = useCallback(() => {
searchFcltyList(ref.current, 1, 10000);
}, [searchFcltyList]);
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
}
const total = ProcessFacilityList?.processFacilityCount || 1;
return (
<MainDiv>
<Title title="처리시설 시설정보" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={processFacilityLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={val => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={processFacilityLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={processFacilityLabelArray[5] as string} selectArray={selectOperationData} onSelectValue={val => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectFacilityPartData} onSelectValue={val => setFacilityPartValue(val.toString())} value={facilityPartValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable
cellClick={(index: number | undefined) => index !== undefined && cellClick(index)}
headName={"처리시설 시설정보"}
tableData={processFacilityList}
headlist={upHeadList}
pageSize={10}
total={total}
resetPageNo={resetPage}
onPageChange={changePage}
>
<HeadMakeColSpan upHeadList={upHeadList} layoutNum={partValue}/>
</UiTable>
</div>
</div>
</ListDiv>
<span className="text-2xl text-gray-500">※계획유입수질 및 설계유입수질은 해당 BOD를 나타냄</span>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<ProcessFacilityInfoModal fcltyName={modalFcltyNm} facilityCd={modalFcltyCd} sido={modalSido} gugun={modalGugun} daddr={modalAddr} fctCpc={modalFctCpc} trtFrfNm={modalTrtFrfNm} rnDt={modalRnDt} cmplDt={modalCmplDt} trstOprEtpNm={modalTrstOprEtpNm} bscFctClsNm={modalBscFctClsNm} mnstr={modalMnstr} efflWtrrgn={modalEfflWtrrgn} lgbsn={modalLgbsn} efflWqltStndrdNm={modalEfflWqltStndrdNm} rgnNm={modalRgnNm} rvbsn={modalRvbsn}></ProcessFacilityInfoModal>}
bodyClass="sm:max-w-[70vw] sm:max-h-[95vh]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
-200
View File
@@ -1,200 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
export default function Rdi() {
const {Common
,initializeCommonData
,Rdi
,RdiSearch
,searchRdiList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = RdiSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
rdiList,
rdiCount,
excelList,
} = Rdi;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchRdiList(1, 10, selectSearchYear[0]?.val || "0");
}
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchRdiList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchRdiList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: 'RDII추정',
sheetNameArray: ['RDII추정'],
headerRange: 'A1:O1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchRdi = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchRdiList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchRdiList(pageNo, pageCnt, searchYear);
}
}, [operationValue, fcltyName, searchRdiList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchRdi(ref.current, 1, 10000);
}, [searchRdi]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchRdi(ref.current, page, 10);
}, [searchRdi]);
const mainList: Record<string, string | number | boolean>[] = rdiList || [];
const total = rdiCount || 0;
return (
<MainDiv>
<Title title="RDII추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput ref={searchInputRef} className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={ undefined } headName={"RDII추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
<span className="text-2xl text-gray-500">※주) 처리장 유입부에서 측정된 유량 및 수질조사 자료를 기준으로 산정된 결과임</span>
</div>
</ListDiv>
</ContentsDiv>
</MainDiv>
);
}
@@ -1,161 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { SewageOccurDetailProps } 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);
// 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 {
upHeadListSewageOccurDetail,
gridListSewageOccurDetail
} = SewageOccurDetail;
useEffect(() => {
setDetailList(gridListSewageOccurDetail);
}, [gridListSewageOccurDetail]);
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]);
return (
<>
<ListDiv className="!h-[80px] !mt-10">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'년월'}</TableHead>
<TableCell className="w-1/3">
<DateMonthPicker
field={{
value: startYearMonth || (() => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}${month}`;
})(),
onChange: (value: string) => {
if (value && /^\d{6}$/.test(value)) {
setStartYearMonth(value);
}
}
}}
selectClass="ml-5 w-[100px]"
startYear={1900}
/>~
<DateMonthPicker
field={{
value: endYearMonth || (() => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}${month}`;
})(),
onChange: (value: string) => {
if (value && /^\d{6}$/.test(value)) {
setEndYearMonth(value);
}
}
}}
selectClass="ml-5 w-[100px]"
startYear={1900}
/>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<ListDiv className="!h-[570px] overflow-y-auto">
<div className="h-[120%]">
<UiTable headName={"오수발생량 추정"} tableData={detailList} headlist={upHeadListSewageOccurDetail} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} boldArray={[0, 1, 2]}>
</UiTable>
</div>
</ListDiv>
</>
);
}
-237
View File
@@ -1,237 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import SewageOccurModal from "./modal/page";
import type { SewageOccurProps } from "@/app/store/sewageOccur";
export default function PlanSewage() {
const {Common
,SewageOccur
,SewageOccurSearch
,initializeCommonData
,searchSewageOccurList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
SewageOccurSearch: { labelArray: [] },
initializeCommonData: () => {},
searchSewageOccurList: () => {}
};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = SewageOccurSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
sewageOccurList,
sewageOccurCount,
excelList
} = SewageOccur;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const sewageOccur = SewageOccur?.sewageOccurList || [];
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (sewageOccur[index]) {
setFcltyNm(sewageOccur[index].bscFctNm);
setFcltyCd(sewageOccur[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '오수발생량 추정',
sheetNameArray: ['오수발생량 추정'],
headerRange: 'A1:O1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchSewageOccurList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchSewageOccurList(pageNo, pageCnt, searchYear);
}
}, [fcltCpct, operationValue, fcltyName, searchSewageOccurList, searchYear]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList: SewageOccurProps[] = sewageOccurList || [];
const total = sewageOccurCount || 0;
return (
<MainDiv>
<Title title="오수발생량 추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={false}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"오수발생량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<SewageOccurModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYear}></SewageOccurModal>}
bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
@@ -1,317 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { UnderWaterInputModalProps } from "@/app/store/underWaterInput";
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";
import { useEffect, useState } from "react";
type ModalProps ={
fcltyName: string;
searchYear : string;
}
const tabList1 = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
];
export default function UnderWaterInputModal({fcltyName='', searchYear=new Date().getFullYear().toString()} : ModalProps) {
// 그리드 관련
const {UnderWaterDetail} = useFoStore((state) => state);
const [startYmDate, setStartYmDate] = useState<string>(searchYear+'01');
const [endYmDate, setEndYmDate] = useState<string>(searchYear+'12');
const [tabTerm, setTabTerm] = useState<string>('tab_term_year');
// 이전 값들을 저장하기 위한 상태
const [prevStartYmDate, setPrevStartYmDate] = useState<string>(searchYear+'01');
const [prevEndYmDate, setPrevEndYmDate] = useState<string>(searchYear+'12');
const {
upHeadListUnderWaterDetail,
gridListUnderWaterDetail,
} = UnderWaterDetail;
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: UnderWaterInputModalProps[]) => {
let maxRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0);
let maxSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0);
let maxUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0);
let minRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0);
let minSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0);
let minUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0);
let avgRealSewageInput = 0;
let avgSewageOccur = 0;
let avgUnderWaterInput = 0;
filteredData.forEach((item: UnderWaterInputModalProps) => {
maxRealSewageInput = Math.max(Number(maxRealSewageInput), Number(item.realSewageInput));
maxSewageOccur = Math.max(Number(maxSewageOccur), Number(item.sewageOccur));
maxUnderWaterInput = Math.max(Number(maxUnderWaterInput), Number(item.underWaterInput));
minRealSewageInput = Math.min(Number(minRealSewageInput) || 0, Number(item.realSewageInput));
minSewageOccur = Math.min(Number(minSewageOccur) || 0, Number(item.sewageOccur));
minUnderWaterInput = Math.min(Number(minUnderWaterInput) || 0, Number(item.underWaterInput));
avgRealSewageInput += Number(item.realSewageInput ?? 0);
avgSewageOccur += Number(item.sewageOccur ?? 0);
avgUnderWaterInput += Number(item.underWaterInput ?? 0);
})
const newFilteredData = [
{
msrDt: '평균',
realSewageInput: filteredData.length==0?0:Math.round(Number(avgRealSewageInput) / filteredData.length * 10) / 10,
sewageOccur: filteredData.length==0?0:Math.round(Number(avgSewageOccur) / filteredData.length * 1000) / 1000 ,
underWaterInput: filteredData.length==0?0:Math.round(Number(avgUnderWaterInput) / filteredData.length * 1000) / 1000,
},
{
msrDt : '최대',
realSewageInput : Math.round(maxRealSewageInput * 10) / 10,
sewageOccur : Math.round(maxSewageOccur * 1000) / 1000 ,
underWaterInput : Math.round(maxUnderWaterInput * 1000) / 1000,
},
{
msrDt : '최소',
realSewageInput : Math.round(minRealSewageInput * 10) / 10,
sewageOccur : Math.round(minSewageOccur * 1000) / 1000 ,
underWaterInput : Math.round(minUnderWaterInput * 1000) / 1000,
},
...filteredData
];
return newFilteredData;
}
// 날짜 필터링 관련 상태
const [copyYearList, setCopyYearList] = useState<UnderWaterInputModalProps[]>([]);
useEffect(() => {
const filteredYearData = setFilterAfterData(gridListUnderWaterDetail);
setCopyYearList(filteredYearData as UnderWaterInputModalProps[]);
}, [gridListUnderWaterDetail]);
useEffect(() => {
switch (tabTerm) {
case 'tab_term_summer':
setStartYmDate(searchYear+'06');
setEndYmDate(searchYear+'08');
setPrevStartYmDate(searchYear+'06');
setPrevEndYmDate(searchYear+'08');
break;
case 'tab_term_winter':
setStartYmDate(searchYear+'01');
setEndYmDate(searchYear+'02');
setPrevStartYmDate(searchYear+'01');
setPrevEndYmDate(searchYear+'02');
break;
default:
setStartYmDate(searchYear+'01');
setEndYmDate(searchYear+'12');
setPrevStartYmDate(searchYear+'01');
setPrevEndYmDate(searchYear+'12');
}
}, [searchYear, tabTerm]);
useEffect(() => {
if(endYmDate < startYmDate) {
alert('종료일이 시작일보다 작습니다.')
// 이전 값으로 되돌리기
setStartYmDate(prevStartYmDate);
setEndYmDate(prevEndYmDate);
return;
}
// 유효한 경우 현재 값을 이전 값으로 저장
setPrevStartYmDate(startYmDate);
setPrevEndYmDate(endYmDate);
const filteredData = gridListUnderWaterDetail.filter((item: UnderWaterInputModalProps) => {
// msrDt가 "2024-01" 형태이므로 "202401" 형태로 변환
const itemDate = item.msrDt.replace('-', '');
return itemDate >= startYmDate && itemDate <= endYmDate;
});
const filteredYearData = setFilterAfterData(filteredData);
setCopyYearList(filteredYearData as UnderWaterInputModalProps[]);
}, [startYmDate, endYmDate, gridListUnderWaterDetail, prevStartYmDate, prevEndYmDate]);
return (
<>
<UITab tabList={tabList1} defaultValue="tab_term_year" onValueChange={(val) => {setTabTerm(val)}}>
<TabsContent value="tab_term_year">
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[200px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
{/* <FacilityTermModalHeader<UnderWaterInputModalProps>
searchList={gridListUnderWaterDetail}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/> */}
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => { } }
className="h-[500px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
</UITab>
</>
);
}
-237
View File
@@ -1,237 +0,0 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
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";
export default function UnderWaterInput() {
const {Common
,UnderWater
,UnderWaterSearch
,initializeCommonData
,searchUnderWaterList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
UnderWater: { underWaterList: [], underWaterCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
UnderWaterSearch: { labelArray: [] },
initializeCommonData: () => {},
searchUnderWaterList: () => {}
};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = UnderWaterSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
underWaterList,
underWaterCount,
excelList,
} = UnderWater;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const underWater = UnderWater?.underWaterList || [];
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0");
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (underWater[index]) {
setFcltyNm(underWater[index].bscFctNm);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '지하수유입량 추정',
sheetNameArray: ['지하수유입량 추정'],
headerRange: 'A1:V1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchUnderWaterList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchUnderWaterList(pageNo, pageCnt, searchYear);
}
}, [fcltCpct, operationValue, fcltyName, searchUnderWaterList, searchYear]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList: UnderWaterProps[] = underWaterList || [];
const total = underWaterCount || 0;
return (
<MainDiv>
<Title title="지하수유입량 추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={false}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"지하수유입량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
<span className="text-2xl text-gray-500">※지하수 유입량 = 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율)</span>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<UnderWaterInputModal fcltyName={fcltyNm} searchYear={searchYear}></UnderWaterInputModal>}
bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
-374
View File
@@ -1,374 +0,0 @@
/**====================================
* 보편적으로 쓰는 store 공간
====================================**/
import { getFetch } from "@/app/components/api";
/**
* STORE - TYPE
*/
// selectBox
export type SelectDataType = {
text: string;
val: string;
}
// grid header
export type TableUpperProps = {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
// 시도 데이터
export type sidoDataType = {
adstDstrCd: number,
sido: string,
}
// 시군구 데이터
export type sigunDataType = {
adstDstrCd: number,
gugun: string
}
// 권역 데이터
export type regionDataType = {
rvbsnCd: string,
rvbsn: string,
}
// 단위유역 데이터
export type unitWaterShedDataType = {
rvbsnCd: string,
lgbsn: string,
}
// 중권역 데이터
export type middleRegionDataType = {
rvbsnCd: string,
mdmbsn: string,
}
// 환경청 데이터
export type envPartDataType = {
lclgvrnmntCd: string,
lclgvrnmntNm: string,
adstDliStrCd: string,
adstDstrCd: string,
}
export type envPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
/**
* STORE - DATA
*/
// 시도 데이터
let selectSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 시군구 데이터
let selectSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 권역 데이터
const selectRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 단위유역 데이터
const selectUnitWaterShedData: SelectDataType[] = [
];
// 환경청 데이터
let selectEnvPartData: envPartSelectDataType[] = [
{ text: "전체", val: '0', adstDliStrCd: '' }
];
// 환경청 내 시도 데이터
const selectEnvPartSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 환경청 내 시군구 데이터
const selectEnvPartSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 구분 데이터
// const selectPartData:SelectDataType[] = [
// { text: "행정별", val: '0' },
// { text: "유역별", val: '01' },
// { text: "환경청별", val: '02' },
// ];
// 상향 하향 데이터
const selectUpdownData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "500이하", val: '400' },
{ text: "500이상", val: '500' },
];
// 운영 상태 데이터
const selectOperationData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "가동중", val: '1' },
{ text: "재가동", val: '2' },
{ text: "가동중지", val: '3' },
{ text: "시설폐쇄", val: '4' },
];
// 시설 구분 데이터
const selectFacilityPartData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "공공하수처리시설", val: 'SW' },
{ text: "오수처리장", val: 'SS' },
{ text: "개인하수처리시설", val: 'PB' },
{ text: "분뇨처리장", val: 'NS' },
{ text: "폐수처리장", val: 'LW' },
{ text: "침출수처리장", val: 'LC' },
{ text: "기타기초시설", val: 'ET' },
];
// 년도 데이터
const getYearData = () => {
const year = new Date().getFullYear();
const year1 = year - 1;
const year2 = year - 2;
const year3 = year - 3;
const year4 = year - 4;
const year5 = year - 5;
return [
{ text: year1 + "년", val: year1.toString() },
{ text: year2 + "년", val: year2.toString() },
{ text: year3 + "년", val: year3.toString() },
{ text: year4 + "년", val: year4.toString() },
{ text: year5 + "년", val: year5.toString() },
]
}
const selectSearchYear:SelectDataType[] = getYearData();
// 유량 기간 구분
const selectFlowTerm : SelectDataType[] = [
{ text: "연간", val: '0' },
{ text: "하절기", val: '01' },
{ text: "동절기", val: '02' },
{ text: "청천시", val: '03' },
{ text: "강우시", val: '04' },
];
// 유량 기간 구분
const selectFlowPanel : SelectDataType[] = [
{ text: "전체", val: '0'},
{ text: "3~10mm미만", val: '2'},
{ text: "10~20mm미만", val: '3'},
{ text: "20~30mm미만", val: '4'},
{ text: "30~40mm미만", val: '5'},
{ text: "40~50mm미만", val: '6'},
{ text: "50~60mm미만", val: '7'},
{ text: "60mm이상", val: '8'},
];
// 중권역 데이터
const selectMiddleRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
/**
* STORE - API
*/
// 시도 API 호출 함수 추가
const fetchSidoData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/city');
const data = res.data;
selectSidoData = [
{ text: "전체", val: '0'},
...data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
}))
];
return selectSidoData;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
return selectSidoData;
}
};
// 시군구 API 호출 함수 추가
const fetchSigunData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sgg');
const data = res.data;
selectSigunData = [
{ text: "전체", val: '0' },
...data
.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
}))
];
return selectSigunData;
} catch (error) {
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
return selectSigunData;
}
};
// 권역 API 호출 함수 추가
const fetchRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sarea');
const data = res.data;
return [
{ text: "전체", val: '0'},
...data.map((item: regionDataType) => ({
text: item.rvbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
return selectRegionData;
}
};
// 중권역 API 호출 함수 추가
const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/midArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: middleRegionDataType) => ({
text: item.mdmbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('중권역 데이터를 가져오는데 실패했습니다:', error);
return selectMiddleRegionData;
}
};
// 단위유역 API 호출 함수 추가
const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/largeArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: unitWaterShedDataType) => ({
text: item.lgbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('단위유역 데이터를 가져오는데 실패했습니다:', error);
return selectUnitWaterShedData;
}
};
// 환경청 데이터
const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/govArea');
const data = res.data;
selectEnvPartData = [
{ text: "전체", val: '0', adstDliStrCd: '' },
...data
.map((item: envPartDataType) => ({
text: item.lclgvrnmntNm || '',
val: String(item.lclgvrnmntCd) || '',
adstDliStrCd: item.adstDliStrCd || ''
}))
];
return selectEnvPartData;
} catch (error) {
console.error('환경청 데이터를 가져오는데 실패했습니다:', error);
return selectEnvPartData;
}
};
/**====================================
* Store - expaort
====================================*/
export type CommonAction = {
initializeCommonData: () => Promise<void>;
}
export type CommonType = {
Common: {
isInit: boolean;
selectSidoData: SelectDataType[];
selectSigunData: SelectDataType[];
selectUpdownData: SelectDataType[];
selectOperationData: SelectDataType[];
selectFacilityPartData: SelectDataType[];
selectSearchYear: SelectDataType[];
selectUnitWaterShedData: SelectDataType[];
selectRegionData: SelectDataType[];
selectMiddleRegionData: SelectDataType[];
selectEnvPartSidoData: SelectDataType[];
selectEnvPartSigunData: SelectDataType[];
selectFlowTerm: SelectDataType[];
selectFlowPanel: SelectDataType[];
selectEnvPartData: envPartSelectDataType[];
btnClass: string;
}
}
export const commonState:CommonType = {
Common: {
isInit: false,
selectSidoData: selectSidoData,
selectSigunData: selectSigunData,
selectUpdownData: selectUpdownData,
selectOperationData: selectOperationData,
selectFacilityPartData: selectFacilityPartData,
selectSearchYear: selectSearchYear,
selectRegionData: selectRegionData,
selectUnitWaterShedData: selectUnitWaterShedData,
selectMiddleRegionData: selectMiddleRegionData,
selectEnvPartSidoData : selectEnvPartSidoData,
selectEnvPartSigunData : selectEnvPartSigunData,
selectFlowTerm: selectFlowTerm,
selectFlowPanel: selectFlowPanel,
selectEnvPartData : selectEnvPartData,
btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium"
}
}
export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)=>CommonAction=(set: (fn: (state: CommonType) => CommonType) => void) => {
return {
// 시도 변경 이벤트
initializeCommonData: async () => {
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
set((state: CommonType) => ({
Common: {
...state.Common,
selectSidoData: sidoData,
selectSigunData: sggData,
selectRegionData: regionData,
selectMiddleRegionData: midData,
selectUnitWaterShedData: unitData,
selectEnvPartData: envData
}
}))
},
}
}
export type CommonStore = CommonType & CommonAction;
File diff suppressed because it is too large Load Diff
@@ -1,307 +0,0 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
type FacilityPrecipitationProps = {
bscFctCd : string,
sido : string,
gugun : string,
bscFctNm : string,
addr : string,
fctCpc : number,
stnKo : string,
infoRnDay : number,
rainEffectDay : number,
totalYearRain : number,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
rgnNm : string,
}
/*
* (2) 강수현황 팝업
*/
// 강수현황
export type PrecipitationPropsDetailProps = {
bscFctCd : string,
rainDt : string,
infoRnDay : number,
rainEffectDay : number,
rainDay : number
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
시설용량: number,
관측소정보 : string,
강우일 : number,
강우영향일 : number,
연간총강수량 : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '조회기간', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "행정구역", row: 0, colSpan: 2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row: 1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row: 1 },
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "유역", row: 0, colSpan: 3} ,
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row: 1} ,
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row: 1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row: 1},
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "유역", row: 0, colSpan: 3} ,
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row: 1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row: 1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row: 1},
{ id: "rgnNm", title: "환경청", row: 0, rowSpan: 2},
{ id: "B", title: "행정구역", upName:"upChangeRe2", row: 0, colSpan: 2},
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row: 1},
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row: 1},
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const facilityPrecipitation:FacilityPrecipitationProps[] = [
]
/**
* 강수현황 상세팝업
*/
// 강수현황 (헤더)
const upHeadListPrecipitationDetail:TableUpperProps[] = [
{ id: "rainDt", title: "날짜"},
{ id: "infoRnDay", title: "강수량(mm/일)"},
{ id: "rainDay", title: "강우일(일)"},
{ id: "rainEffectDay", title: "강우영향일(일)"}
]
// 강수현황 (리스트)
const gridListPrecipitationDetail:PrecipitationPropsDetailProps[] = [
]
/**====================================
* Store - api
====================================*/
const fetchFacilityPrecipitationList = async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const tailStr = '/'+pageNo + '/' + pageSize + '/' + searchYear + (part?'/' + part:'' ) + (nameCd?'/' + nameCd:'' ) + (fcltCpct?'/' + fcltCpct:'' ) + (operationValue?'/' + operationValue:'' ) + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') ;
console.log('tailStr', tailStr);
const url = '/fclt/rain' + tailStr;
const res = await getFetch('fo', url);
const tmpData:PrecipitationPropsDetailProps[] = [];
if(res!= null && res.data.length > 0){
for(let idx=0; idx<res.data.length; idx++){
for(let idx2=0; idx2<res.data[idx].rainyDayObjList.length; idx2++){
const bscFctCd = res.data[idx].rainyDayObjList[idx2].bscFctCd;
const rainDt = res.data[idx].rainyDayObjList[idx2].rainDt;
const infoRnDay = res.data[idx].rainyDayObjList[idx2].infoRnDay;
const rainDay = res.data[idx].rainyDayObjList[idx2].infoRnDay>=3?1:0;
let rainEffectDay = 0;
if(idx2 < res.data[idx].rainyDayObjList.length-2){
if(rainDay==1){
rainEffectDay = 1;
} else {
if(idx2 >= 2){
if(res.data[idx].rainyDayObjList[idx2-1].infoRnDay>=3 || res.data[idx].rainyDayObjList[idx2-2].infoRnDay>=3){
rainEffectDay = 1;
}
}
}
} else if(idx2 < res.data[idx].rainyDayObjList.length-1){
if(rainDay==1 || res.data[idx].rainyDayObjList[idx2+1].infoRnDay>=3){
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
bscFctCd : bscFctCd,
rainDt : rainDt,
infoRnDay : infoRnDay,
rainDay : rainDay,
rainEffectDay : rainEffectDay
})
}
}
res.data.map((item: FacilityPrecipitationProps) => {
const filteredList = tmpData.filter((gridItem) => {
return gridItem.bscFctCd === item.bscFctCd;
});
item.rainEffectDay = filteredList.reduce((acc, curr) => acc + curr.rainEffectDay, 0);
item.totalYearRain = filteredList.reduce((acc, curr) => acc + curr.infoRnDay, 0);
item.infoRnDay = filteredList.reduce((acc, curr) => acc + curr.rainDay, 0);
})
}
return { data: res.data, count: res.count, detailData: tmpData };
}
/**====================================
* Store - expaort
====================================*/
export type FacilityPrecipitationActions = {
searchFacilityPrecipitationList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
const dataFormSetting=(item : FacilityPrecipitationProps)=> {
DataForm({ type: 'number', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'totalYearRain', item: item as unknown as Record<string, unknown>});
};
export type FacilityPrecipitationType = {
FacilityPrecipitationSearch: {
isInit: boolean;
labelArray:string[];
},
FacilityPrecipitation : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
facilityPrecipitation: FacilityPrecipitationProps[];
excelList: ExcelType[];
facilityPrecipitationCount: number;
},
PrecipitationDetail : {
isInit : boolean;
upHeadListPrecipitationDetail : TableUpperProps[];
gridListPrecipitationDetail : PrecipitationPropsDetailProps[];
}
}
export const facilityPrecipitationInitState: FacilityPrecipitationType = {
FacilityPrecipitationSearch: {
isInit: false,
labelArray: LabelArray,
},
FacilityPrecipitation : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
facilityPrecipitation : facilityPrecipitation,
excelList : [],
facilityPrecipitationCount : 0
},
PrecipitationDetail : {
isInit : false,
upHeadListPrecipitationDetail : upHeadListPrecipitationDetail,
gridListPrecipitationDetail : gridListPrecipitationDetail
}
}
export type FacilityPrecipitationStore = FacilityPrecipitationType & FacilityPrecipitationActions;
export const facilityPrecipitationReducer:(set:(fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void)=>FacilityPrecipitationActions=(set: (fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void) => {
return {
searchFacilityPrecipitationList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchFacilityPrecipitationList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
data.forEach((item: FacilityPrecipitationProps) => {
dataFormSetting(item)
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0;
item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0;
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sido,
시군구: item.gugun,
시설이름: item.bscFctNm,
주소: item.addr,
시설용량: item.fctCpc,
관측소정보: item.stnKo,
강우일: item.infoRnDay,
강우영향일: item.rainEffectDay,
연간총강수량: item.totalYearRain,
})
})
}else{
data.forEach((item: FacilityPrecipitationProps) => {
dataFormSetting(item)
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0;
item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0;
})
}
set((state:FacilityPrecipitationType) => ({
...state,
FacilityPrecipitation: pageSize<5000?{
...state.FacilityPrecipitation,
facilityPrecipitation: res.data,
facilityPrecipitationCount: res.count,
}:{
...state.FacilityPrecipitation,
excelList: excelDownList,
},
PrecipitationDetail:{
...state.PrecipitationDetail,
gridListPrecipitationDetail: res.detailData
}
}))
}
}
}
@@ -1,659 +0,0 @@
import { getFetch } from "@/app/components/api";
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
export type { TableUpperProps };
/**====================================
* Store - 타입
*
* tn, tp는 소수점 3자리까지, 나머지는 소수점 1자리까지
====================================*/
/**
* (1) 메인페이지
*/
// 시설수질리스트 타입
export interface FacilityWaterQltyProps {
bscFctCd: string,
bscFctNm: string,
msrDt: string
sidoNm: string,
sggNm: string,
addr: string,
fctCpc: number,
inflAssctAftWqltBod: string | '',
inflAssctAftWqltToc: string | '',
inflAssctAftWqltSs: string | '',
inflAssctAftWqltTn: string | '',
inflAssctAftWqltTp: string | '',
inflAssctBfrWqltTp: string | '',
efflWqltBod: string | '',
efflWqltCod: string | '',
efflWqltSs: string | '',
efflWqltTn: string | '',
efflWqltTp: string | '',
rgnNm:string|'',
rvbsn:string|'',
lgbsn:string|'',
mdmbsn:string|'',
inflAmnt: number | '',
lclgvrnmntNm: string,
infoRnDay: number | '',
rainEffectDay: number | '',
tpInflWqltTmprtr: number | '',
tpInflWqltPh: string | '',
tpInflWqltBod: string | '',
tpInflWqltCod: string | '',
tpInflWqltSs: string | '',
tpInflWqltTn: string | '',
tpInflWqltTp: string | '',
tpInflWqltEclgyTox: string | '',
tpInflWqltClfgrpcnt: string | '',
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
용량: number,
유입BOD: string | '',
유입TOC: string | '',
유입SS: string | '',
유입TN: string | '',
유입TP: string | '',
유출BOD: string | '',
유출TOC: string | '',
유출SS: string | '',
유출TN: string | '',
유출TP: string | '',
}
/**
* (2) 모달페이지
*/
// 시설수질리스트 타입
export type FacilityWaterQltyTmsProps = {
part: string,
toc: number,
ss: number,
tn: number,
tp: number,
}
// API 응답 데이터 타입
export interface WaterQualityHistoryResponse {
data: {
wqltObjs: FacilityWaterQltyProps[];
};
}
export type modalExcelType = {
날짜: string;
"유입BOD(mg/L)": string;
"유입TOC(COD)(mg/L)": string;
"유입SS(mg/L)": string;
"유입TN(mg/L)": string;
"유입TP(mg/L)": string;
"유출BOD(mg/L)": string;
"유출TOC(COD)(mg/L)": string;
"유출SS(mg/L)": string;
"유출TN(mg/L)": string;
"유출TP(mg/L)": string;
"강우량(일)": string;
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 메인 라벨 배열
const mainLabelArray: string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
// 시설수질리스트 헤더(행정별)
const upHeadList1: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", rowSpan: 2, row:0 },
{ id: "C", title: "행정명", colSpan: 2, row: 0 },
{ id: "sidoNm", title: "시도", row: 1 },
{ id: "sggNm", title: "시군구", row: 1 },
{ id: "bscFctNm", title: "시설명", rowSpan: 2, row: 0 },
{ id: "addr", title: "주소", rowSpan: 2, row: 0 },
{ id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 2, row: 0 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 },
{ 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: "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 },
];
// 시설수질리스트 헤더(권역별)
const upHeadList2: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "C", title: "유역", colSpan: 3, row: 0 },
{ id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row:1 },
{ id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row:1 },
{ id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "addr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ 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: "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 },
];
// 시설수질리스트 헤더(환경청별)
const upHeadList3: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "C", title: "유역", colSpan: 3, row: 0 },
{ id: "rvbsn", title: "권역", row:1 },
{ id: "lgbsn", title: "단위유역", row:1 },
{ id: "mdmbsn", title: "중권역", row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "D", title: "행정명", colSpan: 2, row: 0 },
{ id: "sidoNm", title: "시도", upName: "upChangeRe2", row:1 },
{ id: "sggNm", title: "시군구", upName: "upChangeRe2", row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "addr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ 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: "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 },
];
/**
* (2) 모달페이지
*/
// 처리시설 수질현황 헤더(모달-연간)
const upHeadListModalYear: TableUpperProps[] = [
{ id: "msrDt", title: "날짜" },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" },
{ id: "inflAssctAftWqltToc", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" },
{ 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: "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" },
];
// 처리시설 수질현황 헤더(모달-수질TMS)
const upHeadListModalTms: TableUpperProps[] = [
{ id: "part", title: "구분" },
{ id: "toc", title: "TOC(mg/L)", upName: "upChangeRe", upSequnce: 1, align: "right" },
{ id: "ss", title: "SS(mg/L)", upName: "upChangeRe", upSequnce: 2, align: "right" },
{ id: "tn", title: "T-N(mg/L)", upName: "upChangeRe", upSequnce: 3, align: "right" },
{ id: "tp", title: "T-P(mg/L)", upName: "upChangeRe", upSequnce: 4, align: "right" },
];
// 처리시설 수질현황 데이터(모달-연간)
const facilityWaterQltyYearList: FacilityWaterQltyProps[] = [
];
// 처리시설 수질현황 데이터(모달-수질TMS)
const facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[] = [
];
// 총인설비 수질현황 헤더 데이터
const upHeadListModalChongIn: TableUpperProps[] = [
{ id: "msrDt", title: "날짜" },
{ id: "tpInflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" },
{ id: "tpInflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
{ id: "efflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" },
{ id: "efflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
];
/**====================================
* Store - api (데이터 호출)
====================================*/
// 메인페이지
// 시설정보 수질 리스트 데이터 호출
const fetchFacilityWaterQltyList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string|undefined, nameCd?: string|undefined, fcltCpct?: string|undefined, operationValue?: string|undefined, fcltyNm?: string | undefined): Promise<{ data: FacilityWaterQltyProps[], count: number }> => {
try {
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
} catch (error) {
console.error('시설정보 수질현황 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
}
// 시설정보 수질 리스트 데이터 호출(모달-수질TMS)
const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => {
try {
const res = await getFetch('wr', '/il/getTms/' + facilityCd);
return { data: res.data, count: res.count };
} catch (error) {
console.error('시설정보 수질현황 TMS 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
};
// 시설정보 수질 리스트 데이터 호출(모달-이력)
const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise<WaterQualityHistoryResponse | null> => {
try {//
const res = await getFetch('fo', '/fclt/qlty/hist/' + searchYear + '/' + facilityCd);
return res;
} catch (error) {
console.error('시설정보 수질현황 이력 데이터를 가져오는데 실패했습니다:', error);
return null;
}
};
/**====================================
* Store - export
====================================*/
export type FacilityWaterQltyType = {
FacilityWaterQltyMain: {
facilityWaterQltyList: FacilityWaterQltyProps[];
excelList: ExcelType[];
facilityWaterQltyCount: number;
isInit: boolean;
mainLabelArray: string[];
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
},
FacilityWaterQltyModal: {
// 처리시설 수질현황
upHeadListModalYear: TableUpperProps[];
facilityWaterQltyYearList: FacilityWaterQltyProps[];
upHeadListModalTms: TableUpperProps[];
facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[];
// 수질tms
tmsList: FacilityWaterQltyTmsProps[];
msrmtDate: string;
// 총인시설 수질현황
upHeadListModalChongIn: TableUpperProps[];
}
}
export const facilityWaterQltyInitState: FacilityWaterQltyType = {
FacilityWaterQltyMain: {
facilityWaterQltyCount: 0,
facilityWaterQltyList: [],
excelList: [],
isInit: false,
mainLabelArray: mainLabelArray,
upHeadList1: upHeadList1,
upHeadList2: upHeadList2,
upHeadList3: upHeadList3,
},
FacilityWaterQltyModal: {
// 처리시설 수질현황
upHeadListModalYear: upHeadListModalYear,
facilityWaterQltyYearList: facilityWaterQltyYearList,
upHeadListModalTms: upHeadListModalTms,
facilityWaterQltyTmsList: facilityWaterQltyTmsList,
// 수질tms
tmsList: [],
msrmtDate: '',
// 총인시설 수질현황
upHeadListModalChongIn: upHeadListModalChongIn,
}
}
export type FacilityWaterQltyActions = {
/**
* 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param searchYear : 검색 년도
* @param part : 구분
* @param nameCd : 시설명
* @param fcltCpct : 시설용량
* @param operationValue : 운영상태코드
* @param fcltyNm : 시설명
*/
searchFacilityWaterQltyList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => Promise<void>;
/**
* 시설정보 수질현황 이력 데이터 호출
* @param searchYear : 검색 년도
* @param facilityCd : 시설코드
*/
searchFacilityWaterQltyListHist: (searchYear: string, facilityCd: string) => Promise<void>;
}
/**
* @param item : 시설수질리스트 타입
* @returns : 없음
* @description : 시설수질리스트 타입의 데이터를 표현 방식으로 바꿔주는 함수
* - msrDt : YYYY-MM-DD 형식으로 변환
* - inflAssctAftWqltBod : 소수점 1자리까지 표현
* - inflAssctAftWqltTn : 소수점 1자리까지 표현
* - inflAssctBfrWqltTp : 소수점 1자리까지 표현
* - efflWqltBod : 소수점 2자리까지 표현
* - efflWqltTn : 소수점 2자리까지 표현
* - efflWqltTp : 소수점 2자리까지 표현
* @author : 박진우
* @date : 2025-04-24
*/
const dataFormSetting = (item: FacilityWaterQltyProps) => {
DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> });
}
export type FacilityWaterQltyStore = FacilityWaterQltyType & FacilityWaterQltyActions;
export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType) => FacilityWaterQltyType) => void): FacilityWaterQltyActions => {
return {
// 메인페이지
/**
* 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param searchYear : 검색 년도
* @param part : 구분
* @param nameCd : 시설명
* @param fcltCpct : 시설용량
* @param operationValue : 운영상태코드
* @param fcltyNm : 시설명
*/
searchFacilityWaterQltyList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const rtnData = res.data.map(item => {
dataFormSetting(item)
if(item.inflAssctAftWqltTn) {
item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTn){
item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3);
}
if(item.inflAssctAftWqltTp){
item.inflAssctAftWqltTp = Number(Math.round(Number(item.inflAssctAftWqltTp) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTp){
item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3);
}
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.inflAssctAftWqltBod){
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
}
if(item.efflWqltBod){
item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltSs){
item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1);
}
if(item.efflWqltSs){
item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1);
}
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);
}
return item;
});
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.sidoNm = 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.addr,
용량: item.fctCpc,
유입BOD: item.inflAssctAftWqltBod,
유입TOC: item.inflAssctAftWqltToc,
유입SS: item.inflAssctAftWqltSs,
유입TN: item.inflAssctAftWqltTn,
유입TP: item.inflAssctAftWqltTp,
유출BOD: item.efflWqltBod,
유출TOC: item.efflWqltCod,
유출SS: item.efflWqltSs,
유출TN: item.efflWqltTn,
유출TP: item.efflWqltTp,
});
});
}else{
res.data.forEach((item: FacilityWaterQltyProps) => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
});
}
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyMain: pageSize<5000?{
...state.FacilityWaterQltyMain,
facilityWaterQltyList: rtnData,
facilityWaterQltyCount: res.count,
}:{
...state.FacilityWaterQltyMain,
excelList: excelDownList,
}
}))
},
searchFacilityWaterQltyListHist: async (searchYear: string, facilityCd: string) => {
const res = await fetchFacilityWaterQltyListHist(searchYear, facilityCd);
const res1 = await fetchFacilityWaterQltyTmsList(facilityCd);
let rtnMsrmtDate = '';
const rtnTmsList: FacilityWaterQltyTmsProps[] = [];
if(res1.data){
rtnTmsList.push({
part: '평균',
toc: res1.data.tocAvgVl,
ss: res1.data.ssAvgVl,
tn: res1.data.tnAvgVl,
tp: res1.data.tpAvgVl,
});
rtnTmsList.push({
part: '최대',
toc: res1.data.tocMaxVl,
ss: res1.data.ssMaxVl,
tn: res1.data.tnMaxVl,
tp: res1.data.tpMaxVl,
});
rtnTmsList.push({
part: '최소',
toc: res1.data.tocMinVl,
ss: res1.data.ssMinVl,
tn: res1.data.tnMinVl,
tp: res1.data.tpMinVl,
});
rtnMsrmtDate = res1.data.msrmtDate;
}else{
rtnTmsList.push({
part: '평균',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnTmsList.push({
part: '최대',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnTmsList.push({
part: '최소',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnMsrmtDate = '';
}
if(Array.isArray(res) || !res || !('data' in res) || !res.data || !res.data.wqltObjs || res.data.wqltObjs.length === 0) {
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyModal: {
...state.FacilityWaterQltyModal,
facilityWaterQltyYearList: [],
tmsList : rtnTmsList,
msrmtDate : rtnMsrmtDate
}
}))
return;
}
const tmpData:FacilityWaterQltyProps[] = [];
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
for(let idx = 0; idx < res.data.wqltObjs.length; idx++) {
const item = res.data.wqltObjs[idx];
if (!item) continue;
const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0;
let rainEffectDay = 0;
if(idx < res.data.wqltObjs.length - 2) {
if(rainDay == 1) {
rainEffectDay = 1;
} else {
if(idx >= 2) {
const prevItem1 = res.data.wqltObjs[idx-1];
const prevItem2 = res.data.wqltObjs[idx-2];
if((prevItem1 && Number(prevItem1.infoRnDay) >= 3) || (prevItem2 && Number(prevItem2.infoRnDay) >= 3)) {
rainEffectDay = 1;
}
}
}
} else if(idx < res.data.wqltObjs.length - 1) {
const nextItem = res.data.wqltObjs[idx+1];
if(rainDay == 1 || (nextItem && Number(nextItem.infoRnDay) >= 3)) {
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
...item,
rainEffectDay: rainEffectDay,
} as FacilityWaterQltyProps);
}
const rtnData = tmpData.map((item: FacilityWaterQltyProps) => {
dataFormSetting(item)
if(item.inflAssctAftWqltTn) {
item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTn){
item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3);
}
if(item.inflAssctBfrWqltTp){
item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTp){
item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3);
}
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.inflAssctAftWqltBod){
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
}
if(item.efflWqltBod){
item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltSs){
item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1);
}
if(item.efflWqltSs){
item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1);
}
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.tpInflWqltBod){
item.tpInflWqltBod = item.tpInflWqltBod?Number(Math.round(Number(item.tpInflWqltBod) * 10)/10).toFixed(1):Number(item.tpInflWqltBod)===0?'0.0':'';
}else{
item.tpInflWqltBod = '';
}
if(item.tpInflWqltTp){
item.tpInflWqltTp = item.tpInflWqltTp?Number(Math.round(Number(item.tpInflWqltTp) * 1000)/1000).toFixed(3): Number(item.tpInflWqltTp)===0?'0.000':'';
}else{
item.tpInflWqltTp = '';
}
return item;
})
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyModal: {
...state.FacilityWaterQltyModal,
facilityWaterQltyYearList: rtnData,
tmsList : rtnTmsList,
msrmtDate : rtnMsrmtDate
}
}))
},
}
}
-366
View File
@@ -1,366 +0,0 @@
import { getFetch } from "@/app/components/api";
import type { TableUpperProps } from "../dashboard";
import DataForm from "@/app/components/common/DataForm";
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export type flowRateListProps = {
bscFctCd : string,
sidoNm : string,
sggNm : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
lclgvrnmntNm : string,
totInflAmnt : number | '',
inflAmnt : number | '',
sumFlow : number | '',
returnAmnt : number | '',
efflAmnt : number | '',
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
infoRnDay : number | '',
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
용량: number,
총유입량: number | '',
하수유입량: number | '',
연계처리량: number | '',
총인반류수량: number | '',
방류량: number | '',
환경청:string|'',
}
export type modalExcelType = {
날짜: string,
"총유입량(m³/일)": number | '',
"하수유입량(m³/일)": number | '',
"연계처리량(m³/일)": number | '',
"총인반류수량(m³/일)": number | '',
"방류량(m³/일)": number | '',
"강우량": number | string | '',
}
/**
* (2) 모달
*/
export interface FlowRateModalInterface {
msrDt : string,
totInflAmnt : number | '',
inflAmnt : number | '',
sumFlow : number | '',
returnAmnt : number | '',
efflAmnt : number | '',
infoRnDay : number | string | '',
rainEffectDay : number | string | '' ,
}
export type FlowRateModalProps = {
msrDt : string,
totInflAmnt : number,
inflAmnt : number,
sumFlow : number,
returnAmnt : number,
efflAmnt : number,
infoRnDay : number | string | '',
rainEffectDay : number | string | '',
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 리스트 (행정별)
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: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "daddr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2}
];
// 리스트 (권역별)
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2},
{ id: "A", title: "유역", row: 0, colSpan: 3},
{ id: "rvbsn", title: "권역", upName:"upChangeRe", row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", row:1 },
{ id: "mdmbsn", 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},
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2}
];
// 리스트 (환경청별별)
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row: 0, colSpan: 3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", row:1 },
{ 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: "bscFctNm", title: "시설명", row: 0, rowSpan: 2 },
{ id: "daddr", title: "주소", row: 0, rowSpan: 2 },
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2 },
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2 },
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2 }
];
const flowRateList:flowRateListProps[] = [
]
/**
* (2) 모달
*/
const upHeadListFlowRateModal:TableUpperProps[] = [
{ id: "msrDt", title: "날짜"},
{ id: "totInflAmnt", title: "총 유입량(m³/일)", align:"right"},
{ id: "inflAmnt", title: "하수유입량(m³/일)", align:"right"},
{ id: "sumFlow", title: "연계처리량(m³/일)", align:"right"},
{ id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"},
{ id: "efflAmnt", title: "방류량(m³/일)", align:"right"},
{ id: "infoRnDay", title: "강우량", align:"right"}
]
const modalFlowRateListYear:FlowRateModalProps[] = [
]
/**====================================
* Store - API
====================================*/
const fetchFlowRateList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
try {
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
console.log(url);
return { data: res.data, count: res.count }
} catch (error) {
console.error('시설정보 유량현황 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
}
const fetchFlowRateModalList = async (bscFctCd: string, searchYear: string) => {
try {
const url = '/fclt/flow/' + bscFctCd + '/' + searchYear;
const res = await getFetch('fo', url);
// msrDt 오름차순으로 정렬
const sortedData = res.data.sort((a: FlowRateModalInterface, b: FlowRateModalInterface) => {
return new Date(a.msrDt).getTime() - new Date(b.msrDt).getTime();
});
return { data: sortedData }
} catch (error) {
console.error('시설정보 유량현황 모달 데이터를 가져오는데 실패했습니다:', error);
return { data: [] }
}
}
/**====================================
* Store - expaort
====================================*/
export type FlowRateSearchActions = {
searchFlowRateList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
searchFlowRateModalList : (bscFctCd: string, searchYear: string) => void;
}
export type FlowRateSearchType = {
FlowRateSearch: {
isInit: boolean;
flowRateSearchLabelArray:string[];
},
FlowRateList : {
isInit : boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
flowRateList:flowRateListProps[];
flowRateCount: number;
excelList: ExcelType[];
},
FlowRateModal : {
isInit : boolean;
modalFlowRateListYear : FlowRateModalProps[];
upHeadListFlowRateModalYear : TableUpperProps[];
}
}
export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions;
const dataFormSetting=(item : flowRateListProps | FlowRateModalProps)=> {
DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'totInflAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'inflAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'sumFlow', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'efflWqltBod', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'returnAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'efflAmnt', item: item as unknown as Record<string, unknown>});
}
export const flowRateSearchInitState:FlowRateSearchType = {
FlowRateSearch: {
isInit: false,
flowRateSearchLabelArray: FlowRateSearchLabelArray,
},
FlowRateList : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
flowRateList : flowRateList,
flowRateCount : 0,
excelList: []
},
FlowRateModal : {
isInit : false,
upHeadListFlowRateModalYear : upHeadListFlowRateModal,
modalFlowRateListYear : modalFlowRateListYear,
}
}
export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => FlowRateSearchType) => void)=>FlowRateSearchActions=(set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => {
return {
searchFlowRateList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
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;
});
if(pageSize>5000){
rtnData.forEach((item: flowRateListProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sidoNm,
시군구: item.sggNm,
시설이름: item.bscFctNm,
주소: item.daddr,
용량: item.fctCpc,
총유입량: item.totInflAmnt,
하수유입량: item.inflAmnt,
연계처리량: item.sumFlow,
총인반류수량: item.returnAmnt,
방류량: item.efflAmnt,
});
});
}
console.log('excelDownList' , excelDownList);
set((state: FlowRateSearchType) => ({
...state,
FlowRateList: pageSize<5000?{
...state.FlowRateList,
flowRateList: rtnData,
flowRateCount: res.count,
}:{
...state.FlowRateList,
excelList: excelDownList,
}
}))
},
searchFlowRateModalList: async (bscFctCd: string, searchYear: string) => {
const res = await fetchFlowRateModalList(bscFctCd, searchYear);
const data = res.data;
const tmpData:FlowRateModalProps[] = [];
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
for(let idx = 0; idx < data.length; idx++) {
const item = data[idx];
const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0;
let rainEffectDay = 0;
if(idx < data.length - 2) {
if(rainDay == 1) {
rainEffectDay = 1;
} else {
if(idx >= 2) {
if(Number(data[idx-1].infoRnDay) >= 3 || Number(data[idx-2].infoRnDay) >= 3) {
rainEffectDay = 1;
}
}
}
} else if(idx < data.length - 1) {
if(rainDay == 1 || Number(data[idx+1].infoRnDay) >= 3) {
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
...item,
rainEffectDay: rainEffectDay,
});
}
const rtnData = tmpData.map((item: FlowRateModalProps) => {
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0)
dataFormSetting(item);
return item;
});
set((state: FlowRateSearchType) => ({
...state,
FlowRateModal: {
...state.FlowRateModal,
modalFlowRateListYear: rtnData || [],
}
}))
}
}
}
-45
View File
@@ -1,45 +0,0 @@
'use client'
import { type ReactNode, createContext, useContext, useRef } from 'react'
import { useStore } from 'zustand'
import { type FoStore, StoreZus } from './store'
export type StoreApi = ReturnType<typeof StoreZus>
export const StoreContext = createContext<StoreApi | undefined>(
undefined,
)
export interface StoreProviderProps {
children: ReactNode
}
export const StoreProvider = ({
children,
}: StoreProviderProps) => {
const storeRef = useRef<StoreApi>()
if (!storeRef.current) {
const store = StoreZus()
storeRef.current = store
}
return (
<StoreContext.Provider value={storeRef.current}>
{children}
</StoreContext.Provider>
)
}
export const useFoStore = <T,>(
selector: (store: FoStore) => T,
): T => {
const storeContext = useContext(StoreContext)
if (!storeContext) {
throw new Error(`useStore must be used within StoreProvider`)
}
return useStore(storeContext, selector)
}
-226
View File
@@ -1,226 +0,0 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
type PlanSewageProps = {
bscFctCd : string,
sido : string,
gugun : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
spareCapacity : number,
variableLoadPercent : number,
classifiedSewerPercent : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
sprtProcZoneAr : number,
procZoneSm : number,
avgInflAmnt : number,
avgInflAmntSunny : number,
maxInflAmntSunny : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", upSequnce:1, row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1},
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1},
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const planSewageList:PlanSewageProps[] = [
]
/**====================================
* Store - api
====================================*/
const fetchPlanSewageList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const tailStr = partCd ? '/'+ partCd + '/' + (nameCd || '0') + '/' + (fcltCpct || '0') + '/' + (operationValue || '0') +'/0'+ (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/prcsFclt/info/plan/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
console.log(url);
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
}
/**====================================
* Store - expaort
====================================*/
export type ExcelType = {
시설코드: string;
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string;
시군구: string;
시설이름: string;
주소: string;
시설용량: number;
여유용량: number;
변동부하율 : number;
분류식화율 : number;
}
const dataFormSetting=(item : PlanSewageProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'spareCapacity', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'variableLoadPercent', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'classifiedSewerPercent', item: item as unknown as Record<string, unknown>});
};
export type PlanSewageActions = {
searchPlanSewageList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type PlanSewageType = {
PlanSewageSearch: {
isInit: boolean;
labelArray:string[];
},
PlanSewage : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
planSewageList: PlanSewageProps[];
planSewageCount: number;
excelList: ExcelType[];
},
}
export const planSewageInitState: PlanSewageType = {
PlanSewageSearch: {
isInit: false,
labelArray: LabelArray,
},
PlanSewage : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
planSewageList : planSewageList,
planSewageCount : 0,
excelList : []
},
}
export type PlanSewageStore = PlanSewageType & PlanSewageActions;
export const planSewageReducer:(set:(fn: (state: PlanSewageType) => PlanSewageType) => void)=>PlanSewageActions=(set: (fn: (state: PlanSewageType) => PlanSewageType) => void) => {
return {
searchPlanSewageList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchPlanSewageList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
const excelDownList: ExcelType[] = [];
console.log('res' , res.data);
/**
* 결과데이터 계산
* (1) 여유용량 = 시설용량 - 건기시 평균유량
* (2) 변동부하율 = 건기시 일최대 유량/일평균유량(기준 최근 3개년)
* (3) 분류식화율 = 분류식지역 면적(㎢)/처리구역 면적합계(㎢) ×100
*/
const resultData = res.data.map((item:PlanSewageProps) => {
item.spareCapacity = Number(item.fctCpc) - Number(item.avgInflAmntSunny);
item.variableLoadPercent = item.maxInflAmntSunny && item.maxInflAmntSunny!==0 ? Number(item.maxInflAmntSunny) / Number(item.avgInflAmntSunny) : 0;
item.classifiedSewerPercent = item.sprtProcZoneAr && item.sprtProcZoneAr!==0 ? Number(item.sprtProcZoneAr) / Number(item.procZoneSm) * 100 : 0;
return item;
});
resultData.forEach((item:PlanSewageProps) => {
dataFormSetting(item)
})
if(pageSize>5000){
resultData.forEach((item:PlanSewageProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.lgbsn,
단위유역: item.rvbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시설이름: item.bscFctNm,
시도: item.sido,
시군구: item.gugun,
주소: item.daddr,
시설용량: item.fctCpc,
여유용량: item.spareCapacity,
변동부하율: item.variableLoadPercent,
분류식화율: item.classifiedSewerPercent,
})
})
}
set((state:PlanSewageType) => ({
...state,
PlanSewage: pageSize<5000?{
...state.PlanSewage,
planSewageList: resultData,
planSewageCount: res.count,
}:{
...state.PlanSewage,
excelList: excelDownList,
}
}))
}
}
}
-507
View File
@@ -1,507 +0,0 @@
import { getFetchAreaList, postFetch } from "@/app/components/api";
import type { TableUpperProps } from "../dashboard";
import DataForm from "@/app/components/common/DataForm";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 처리구역 리스트
export type ProcessAreaProps = {
swtrmtFcltCd : string,
sido : string,
gugun : string,
swtrmtFcltNm : string,
location : string,
fctCpc : number | '',
ruse : boolean,
yrDt4 : string,
yr : string,
yrDc1 : string,
yrDc2 : string,
yrDc3 : string,
yrDc4 : string,
/**
* 증설계획
*/
// 기승인(당초4단계), 현재, 변경1~4
planSwageQntSumDt4 : number | '',
planSwageQntSum : number | '',
planSwageQntSumDc1 : number | '',
planSwageQntSumDc2 : number | '',
planSwageQntSumDc3 : number | '',
planSwageQntSumDc4 : number | '',
/**
* 처리인구
*/
// 기승인(당초4단계), 변경1~4
swtrmtPpltnCntDt4 : number | '',
swtrmtPpltnCnt : number | '',
swtrmtPpltnCntDc1 : number | '',
swtrmtPpltnCntDc2 : number | '',
swtrmtPpltnCntDc3 : number | '',
swtrmtPpltnCntDc4 : number | '',
/**
* 처리구역면적
*/
// 기승인(당초4단계), 변경1~4
swtrmtZoneAreaDt4 : number | '',
swtrmtZoneArea : number | '',
swtrmtZoneAreaDc1 : number | '',
swtrmtZoneAreaDc2 : number | '',
swtrmtZoneAreaDc3 : number | '',
swtrmtZoneAreaDc4 : number | '',
// 처리구역
trtDstr : string | '',
}
export interface ExcelType {
시설코드: string,
시설명: string,
주소: string,
시설용량: number | '',
증설계획기존: number | '',
증설계획현재: number | '',
증설계획1단계: number | '',
증설계획2단계: number | '',
증설계획3단계: number | '',
증설계획4단계: number | '',
처리인구기존: number | '',
처리인구현재: number | '',
처리인구1단계: number | '',
처리인구2단계: number | '',
처리인구3단계: number | '',
처리인구4단계: number | '',
처리구역기존: number | '',
처리구역현재: number | '',
처리구역1단계: number | '',
처리구역2단계: number | '',
처리구역3단계: number | '',
처리구역4단계: number | '',
처리구역: string | ''
}
// (2) 처리구역 팝업
// 시설증설 계획
export type FcltyImproveInfoProps = {
planSwageQntSumDt4 : number | '',
planSwageQntSum : number | '',
planSwageQntSumDc1 : number | '',
planSwageQntSumDc2 : number | '',
planSwageQntSumDc3 : number | '',
planSwageQntSumDc4 : number | '',
}
// 처리인구 계획
export type ProcessPopulationProps = {
swtrmtPpltnCntDt4 : number | '',
swtrmtPpltnCnt : number | '',
swtrmtPpltnCntDc1 : number | '',
swtrmtPpltnCntDc2 : number | '',
swtrmtPpltnCntDc3 : number | '',
swtrmtPpltnCntDc4 : number | '',
}
// 처리구역 계획
export type ProcessAreaPlanProps = {
swtrmtZoneAreaDt4 : number | '',
swtrmtZoneArea : number | '',
swtrmtZoneAreaDc1 : number | '',
swtrmtZoneAreaDc2 : number | '',
swtrmtZoneAreaDc3 : number | '',
swtrmtZoneAreaDc4 : number | '',
trtDstr : string,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '연도별', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더(행정별)
const upHeadList1:TableUpperProps[] = [
{ id: "swtrmtFcltCd", title: "시설코드", rowSpan:3, row:0},
{ id: "A", title: "행정구역", colSpan:2, rowSpan:2, row:0},
{ id: "sido", title: "시도", upName:"upChangeRe", row:2 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe", row:2 },
{ id: "swtrmtFcltNm", title: "시설명", rowSpan:3, row:0},
{ id: "location", title: "주소", rowSpan:3, row:0, },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", rowSpan:3, row:0},
{ id: "B", title: "시설계획", colSpan:19, row:0},
{ id: "C", title: "증설계획(m³/일)", colSpan:6, row:1},
{ id: "planSwageQntSumDt4", title: "기승인\n4단계", row:2},
{ id: "planSwageQntSum", title: "현재", row:2},
{ id: "planSwageQntSumDc1", title: "1단계", row:2},
{ id: "planSwageQntSumDc2", title: "2단계", row:2},
{ id: "planSwageQntSumDc3", title: "3단계", row:2},
{ id: "planSwageQntSumDc4", title: "4단계", row:2},
{ id: "D", title: "처리인구(인)", colSpan:6, row:1},
{ id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row:2},
{ id: "swtrmtPpltnCnt", title: "현재", row:2},
{ id: "swtrmtPpltnCntDc1", title: "1단계", row:2},
{ id: "swtrmtPpltnCntDc2", title: "2단계", row:2},
{ id: "swtrmtPpltnCntDc3", title: "3단계", row:2},
{ id: "swtrmtPpltnCntDc4", title: "4단계", row:2},
{ id: "E", title: "처리구역(km²)", colSpan:7, row:1},
{ id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row:2},
{ id: "swtrmtZoneArea", title: "현재", row:2},
{ id: "swtrmtZoneAreaDc1", title: "1단계", row:2},
{ id: "swtrmtZoneAreaDc2", title: "2단계", row:2},
{ id: "swtrmtZoneAreaDc3", title: "3단계", row:2},
{ id: "swtrmtZoneAreaDc4", title: "4단계", row:2},
{ id: "trtDstr", title: "처리구역", row:2}
];
const processAreaList:ProcessAreaProps[] = [
]
/**
* 처리구역정보 상세팝업
*/
// 시설증설 계획 (헤더)
const upHeadListFcltyImproveInfo:TableUpperProps[] = [
{ id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0},
{ id: "A", title: "증설계획(m³/일)", colSpan:5, row:0},
{ id: "planSwageQntSum", title: "현재", row:1},
{ id: "planSwageQntSumDc1", title: "1단계", row:1},
{ id: "planSwageQntSumDc2", title: "2단계", row:1},
{ id: "planSwageQntSumDc3", title: "3단계", row:1},
{ id: "planSwageQntSumDc4", title: "4단계", row:1},
];
// 시설증설 계획 (리스트)
const gridListFcltyImproveInfo:FcltyImproveInfoProps[] = [
];
// 처리인구 계획 (헤더)
const upHeadListProcessPopulation:TableUpperProps[] = [
{ id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0},
{ id: "A", title: "처리인구(인)", colSpan:5, row:0},
{ id: "swtrmtPpltnCnt", title: "현재", row:1},
{ id: "swtrmtPpltnCntDc1", title: "1단계", row:1},
{ id: "swtrmtPpltnCntDc2", title: "2단계", row:1},
{ id: "swtrmtPpltnCntDc3", title: "3단계", row:1},
{ id: "swtrmtPpltnCntDc4", title: "4단계", row:1},
];
// 처리인구 계획 (리스트)
const gridListProcessPopulation:ProcessPopulationProps[] = [
];
// 처리구역 계획 (헤더)
const upHeadListProcessAreaPlan:TableUpperProps[] = [
{ id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan:2, row:0},
{ id: "A", title: "처리면적(km²)", colSpan:6, row:0},
{ id: "swtrmtZoneArea", title: "현재", row:1},
{ id: "swtrmtZoneAreaDc1", title: "1단계", row:1},
{ id: "swtrmtZoneAreaDc2", title: "2단계", row:1},
{ id: "swtrmtZoneAreaDc3", title: "3단계", row:1},
{ id: "swtrmtZoneAreaDc4", title: "4단계", row:1},
{ id: "trtDstr", title: "처리구역", row:1}
];
// 시설증설 계획 (리스트)
const gridListProcessAreaPlan:ProcessAreaPlanProps[] = [
];
/**====================================
* Store - API
=====================================*/
// 시설 상세 정보 조회 API
const fetchFacilityDetailInfo = async (swtrmtFcltCd: string) => {
try {
const tailUrl = '/1/5/A/0/0/0/SW';
const res = await postFetch('fo', '/prcsFclt/ruseInfo'+tailUrl, [swtrmtFcltCd]);
return { data:res.data, count:res.count };
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
const fetchProcessAreaList = async (pageNo : number, pageSize : number, ctpvNm : string | undefined, sggNm : string | undefined, targetYear : string | undefined ): Promise<{data:ProcessAreaProps[], count:number}> => {
try {
targetYear = targetYear ? targetYear : "2024";
const baseUrl = 'https://apigateway.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy';
const queryParams = new URLSearchParams();
if (ctpvNm && ctpvNm !== '전체') queryParams.append('ctpvNm', ctpvNm);
if (sggNm && sggNm !== '전체') queryParams.append('sggNm', sggNm);
if (targetYear) queryParams.append('targetYear',targetYear);
const queryString = queryParams.toString();
const url = queryString ? `${baseUrl}?${queryString}` : baseUrl;
const res = await getFetchAreaList(url);
if(res && res.length > 0){
const enrichedData = await Promise.all(
res.map(async (item: ProcessAreaProps) => {
upHeadList1.forEach((head:TableUpperProps) => {
if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + item.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + item.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + item.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + item.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + item.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + item.yrDt4 + '년)';
}
});
const res = await fetchFacilityDetailInfo(item.swtrmtFcltCd);
const detailInfo = res.data.filter((item:ProcessAreaProps) => item.ruse === true);
return {
...item,
sido: detailInfo.length > 0 ? detailInfo[0].sido||'' : '',
sigungo: detailInfo.length > 0 ? detailInfo[0].gugun||'' : '',
location: detailInfo.length > 0 ? detailInfo[0].addr||'' : '',
fctCpc: detailInfo.length > 0 ? detailInfo[0].fctCpc||'' : 0,
trtDstr: detailInfo.length > 0 ? detailInfo[0].trtDstr||'' : '',
};
})
);
const startIndex = (pageNo - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedData = enrichedData.slice(startIndex, endIndex);
return {data: paginatedData, count: enrichedData.length};
}else{
return {data: [], count: 0};
}
} catch (error) {
console.error('시설계획정보를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
/**====================================
* Store - expaort
=====================================*/
export type ProcessAreaActions = {
searchProcessAreaList : (pageNo: number, pageSize: number, ctpvNm?:string, sggNm?:string, targetYear?:string) => void;
initModalData : (swtrmtFcltCd:string) => void;
}
export type ProcessAreaType = {
ProcessAreaSearch: {
isInit: boolean;
processFacilityLabelArray:string[];
},
ProcessArea : {
isInit: boolean;
upHeadList1: TableUpperProps[];
processAreaList: ProcessAreaProps[];
processAreaCount: number;
excelList: ExcelType[];
},
ProcessAreaDetail : {
isInit: boolean;
upHeadListFcltyImproveInfo: TableUpperProps[];
gridListFcltyImproveInfo: FcltyImproveInfoProps[]; // Changed from facilityAreaProps to ProcessAreaProps
upHeadListProcessPopulation: TableUpperProps[];
gridListProcessPopulation: ProcessPopulationProps[]; // Changed from facilityAreaProps to ProcessAreaProps
upHeadListProcessAreaPlan: TableUpperProps[];
gridListProcessAreaPlan: ProcessAreaPlanProps[]; // Changed from facilityAreaProps to ProcessAreaProps
},
}
export const processAreaInitState: ProcessAreaType = {
ProcessAreaSearch: {
isInit: false,
processFacilityLabelArray: ProcessFacilityLabelArray,
},
ProcessArea : {
isInit : false,
upHeadList1 : upHeadList1,
processAreaList : processAreaList,
processAreaCount : 0,
excelList : []
},
ProcessAreaDetail : {
isInit : false,
upHeadListFcltyImproveInfo : upHeadListFcltyImproveInfo,
gridListFcltyImproveInfo : gridListFcltyImproveInfo,
upHeadListProcessPopulation : upHeadListProcessPopulation,
gridListProcessPopulation : gridListProcessPopulation,
upHeadListProcessAreaPlan : upHeadListProcessAreaPlan,
gridListProcessAreaPlan : gridListProcessAreaPlan,
},
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: ProcessAreaType | ((state: ProcessAreaType) => ProcessAreaType)) => void;
export type ProcessAreaStore = ProcessAreaType & ProcessAreaActions;
export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: SetStateFunction) => {
return {
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessAreaList: async ( pageNo:number, pageSize:number, ctpvNm?:string, sggNm?:string, targetYear?:string) => {
const res = await fetchProcessAreaList(pageNo, pageSize, ctpvNm, sggNm, targetYear);
res.data.forEach((item:ProcessAreaProps) => {
DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>});
})
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
excelDownList.push({
시설코드: item.swtrmtFcltCd,
시설명: item.swtrmtFcltNm,
주소: item.location,
시설용량: item.fctCpc,
증설계획기존: item.planSwageQntSumDt4,
증설계획현재: item.planSwageQntSum,
증설계획1단계: item.planSwageQntSumDc1,
증설계획2단계: item.planSwageQntSumDc2,
증설계획3단계: item.planSwageQntSumDc3,
증설계획4단계: item.planSwageQntSumDc4,
처리인구기존: item.swtrmtPpltnCntDt4,
처리인구현재: item.swtrmtPpltnCnt,
처리인구1단계: item.swtrmtPpltnCntDc1,
처리인구2단계: item.swtrmtPpltnCntDc2,
처리인구3단계: item.swtrmtPpltnCntDc3,
처리인구4단계: item.swtrmtPpltnCntDc4,
처리구역기존: item.swtrmtZoneAreaDt4,
처리구역현재: item.swtrmtZoneArea,
처리구역1단계: item.swtrmtZoneAreaDc1,
처리구역2단계: item.swtrmtZoneAreaDc2,
처리구역3단계: item.swtrmtZoneAreaDc3,
처리구역4단계: item.swtrmtZoneAreaDc4,
처리구역: item.trtDstr
});
});
}else{
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
})
}
set((state:ProcessAreaType) => ({
...state,
ProcessArea: pageSize<5000?{
...state.ProcessArea,
processAreaList: res.data,
processAreaCount: res.count,
}:{
...state.ProcessArea,
excelList: excelDownList,
},
}))
},
initModalData: async (swtrmtFcltCd:string) => {
set((state: ProcessAreaType) => {
const rtnData = state.ProcessArea.processAreaList.find((item: ProcessAreaProps) => item.swtrmtFcltCd === swtrmtFcltCd);
if (rtnData) {
upHeadListFcltyImproveInfo.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
upHeadListProcessAreaPlan.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
upHeadListProcessPopulation.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
// 시설증설 계획 데이터 생성
const fcltyImproveInfo: FcltyImproveInfoProps = {
planSwageQntSumDt4: rtnData.planSwageQntSumDt4,
planSwageQntSum: rtnData.planSwageQntSum,
planSwageQntSumDc1: rtnData.planSwageQntSumDc1,
planSwageQntSumDc2: rtnData.planSwageQntSumDc2,
planSwageQntSumDc3: rtnData.planSwageQntSumDc3,
planSwageQntSumDc4: rtnData.planSwageQntSumDc4
};
// 처리인구 계획 데이터 생성
const processPopulation: ProcessPopulationProps = {
swtrmtPpltnCntDt4: rtnData.swtrmtPpltnCntDt4,
swtrmtPpltnCnt: rtnData.swtrmtPpltnCnt,
swtrmtPpltnCntDc1: rtnData.swtrmtPpltnCntDc1,
swtrmtPpltnCntDc2: rtnData.swtrmtPpltnCntDc2,
swtrmtPpltnCntDc3: rtnData.swtrmtPpltnCntDc3,
swtrmtPpltnCntDc4: rtnData.swtrmtPpltnCntDc4
};
// 처리구역 계획 데이터 생성
const processAreaPlan: ProcessAreaPlanProps = {
swtrmtZoneAreaDt4: rtnData.swtrmtZoneAreaDt4,
swtrmtZoneArea: rtnData.swtrmtZoneArea,
swtrmtZoneAreaDc1: rtnData.swtrmtZoneAreaDc1,
swtrmtZoneAreaDc2: rtnData.swtrmtZoneAreaDc2,
swtrmtZoneAreaDc3: rtnData.swtrmtZoneAreaDc3,
swtrmtZoneAreaDc4: rtnData.swtrmtZoneAreaDc4,
trtDstr: rtnData.trtDstr
};
return {
...state,
ProcessAreaDetail: {
...state.ProcessAreaDetail,
gridListFcltyImproveInfo: [fcltyImproveInfo],
gridListProcessPopulation: [processPopulation],
gridListProcessAreaPlan: [processAreaPlan]
}
};
}
return state;
});
}
}
}
-832
View File
@@ -1,832 +0,0 @@
import { getFetch } from "@/app/components/api";
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 처리시설 리스트
export type facilityListProps = {
bscFctCd : string,
bscFctNm : string,
bscFctClsNm : string,
sido : string,
gugun : string,
daddr : string,
fctCpc : number | string | '',
plInflWqltBod : number | string | '',
dsInflWqltBod : number | string | '',
trtFrfNm : string,
rnDt : string,
cmplDt : string,
trstOprEtpNm : string,
rvbsn : string,
mdmbsn : string,
lgbsn : string,
rgnNm : string,
mnstr : string,
efflWtrrgn : string,
efflWqltStndrdNm : string
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
시설용량: number | '',
계획유입수질: number | '',
설계유입수질: number | '',
공법: string,
가동개시일: string,
준공일: string,
관리대행업자: string,
}
// (2) 처리시설 팝업
// 시설 기본정보
export type BasicInfoProps = {
bscFctNm : string,
fctCpc : number | '',
sido : string,
gugun : string,
startDay : string,
addr : string,
cmplDt : string,
trtFrfNm : string,
trstOprEtpNm : string,
}
// 시설 계획정보
export type PlanInfoProps = {
plInflWqltBod? : string,
plInflWqltToc? : string,
plInflWqltCod? : string,
plInflWqltSs? : string,
plInflWqltTn? : string,
plInflWqltTp? : string,
dsInflWqltBod? : string,
dsInflWqltToc? : string,
dsInflWqltCod? : string,
dsInflWqltSs? : string,
dsInflWqltTn? : string,
dsInflWqltTp? : string,
sitArea? : string | number,
trtDstrArea? : string | number,
swrTrtPopl? : string | number
}
// 시설 계획정보
export type LegalWaterQltyProps = {
totClfgrpcnt : number,
bod : number,
cod : number,
ss : number,
tn : number,
tp : number
}
// 시설 수역 및 구역정보
export type fcltyAreaInfoProps = {
rvbsn : string,
efflWtrrgn : string,
lgbsn : string,
efflWqltStndrdNm : string,
rgnNm : string,
}
// 시설 이력관리
export type fcltyHistInfoProps = {
extnsSeqno : string,
extnsNm : string,
bzStrtYrMnth : string,
bzEdYrMnth : string,
eqpPrmsDt : string,
cmplDt : string,
trlrnCmplDt : string,
rnDt : string,
sitArea : number,
trtDstrArea : number,
swrTrtPopl : number
}
// 시설 수질정보
export type fcltyWaterQltyInfoProps = {
extnsSeqno : string,
plInflWqltBod : string,
plInflWqltToc : string,
plInflWqltCod : string,
plInflWqltSs : string,
plInflWqltTn : string,
plInflWqltTp : string,
dsInflWqltBod : string,
dsInflWqltToc : string,
dsInflWqltCod : string,
dsInflWqltSs : string,
dsInflWqltTn : string,
dsInflWqltTp : string,
plEfflWqltBod : string,
plEfflWqltToc : string,
plEfflWqltCod : string,
plEfflWqltSs : string,
plEfflWqltTn : string,
plEfflWqltTp : string,
bod : number,
cod : number,
ss : number,
tn : number,
tp : number,
totClfgrpcnt : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '시설구분', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2 },
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:"right"},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:"right"},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
// 처리시설 리스트 (유역)
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3},
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
// 처리시설 리스트 (환경청청)
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3},
{ id: "rgnNm", title: "환경청"},
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2 },
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
const processFacilityList:facilityListProps[] = [
]
/**
* 처리시설 상세팝업
*/
// 시설 기본정보 (헤더)
const upHeadListBasicInfo:TableUpperProps[] = [
{ id: "bscFctNm", title: "시설명" },
{ id: "fctCpc", title: "시설용량n(m³/일)", align: 'right'},
{ id: "sido", title: "시도"},
{ id: "gugun", title: "시군구"},
{ id: "rnDt", title: "가동개시일"},
{ id: "daddr", title: "주소"},
{ id: "cmplDt", title: "준공일"},
{ id: "trtFrfNm", title: "공법"},
{ id: "trstOprEtpNm", title: "관리대행업자"},
];
// 시설 기본정보 (리스트)
const gridListBasicInfo:BasicInfoProps[] = [
{
bscFctNm : "난지",
fctCpc : 860000,
sido : "서울특별시",
gugun : "",
startDay : "1987-06-30",
addr : "경기도 고양시 덕양구 대덕로 426 (현천동)",
cmplDt : "2013-04-30",
trtFrfNm : "MLE, A2O",
trstOprEtpNm : "자체",
}
];
// 시설 법적방류수질 (헤더)
const upHeadListLegalWaterQlty:TableUpperProps[] = [
{ id: "bod", title: "BOD(mg/L)", align: 'right'},
{ id: "cod", title: "TOC(COD)(mg/L)", align: 'right'},
{ id: "ss", title: "SS(mg/L)", align: 'right'},
{ id: "tn", title: "T-N(mg/L)", align: 'right'},
{ id: "tp", title: "T-P(mg/L) ", align: 'right'},
{ id: "totClfgrpcnt", title: "대장균군(개/100ml)"},
]
// 시설 법적방류수질 (리스트)
let gridListLegalWaterQlty:LegalWaterQltyProps[] = [
]
// 시설 계획정보 (헤더)
const upHeadListPlanInfo:TableUpperProps[] = [
{ id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' },
{ id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' },
{ id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' },
{ id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' },
{ id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' },
{ id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' },
{ id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' },
{ id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' },
{ id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' },
{ id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' },
{ id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' },
{ id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' },
{ id: "sitArea", title: "부지면적\n(m²)", align: 'right'},
{ id: "trtDstrArea", title: "처리구역면적\n(ha)", align: 'right'},
{ id: "swrTrtPopl", title: "하수처리인구\n(인)", align: 'right'},
];
// 시설 계획정보 (리스트)
const gridListPlanInfo:PlanInfoProps[] = [
{
plInflWqltBod : "179.0",
plInflWqltToc : "90.0",
plInflWqltCod : "0.0",
plInflWqltSs : "132.0",
plInflWqltTn : "38.000",
plInflWqltTp : "4.300",
dsInflWqltBod : "178.0",
dsInflWqltToc : "91.0",
dsInflWqltCod : "0.0",
dsInflWqltSs : "131.0",
dsInflWqltTn : "40.200",
dsInflWqltTp : "4.500",
sitArea : "928,574",
trtDstrArea : "7,994.0",
swrTrtPopl : "1,573,277",
}
];
// 시설 수역 및 구역정보 (헤더)
const upHeadListFcltyAreaInfo:TableUpperProps[] = [
{ id: "rvbsn", title: "하천명" },
{ id: "efflWtrrgn", title: "방류수역"},
{ id: "lgbsn", title: "단위유역"},
{ id: "efflWqltStndrdNm", title: "지역구분"},
{ id: "rgnNm", title: "관할 유역(지방) 환경청"}
];
// 시설 수역 및 구역정보 (리스트)
const gridListFcltyAreaInfo:fcltyAreaInfoProps[] = [
{
rvbsn : "한강",
efflWtrrgn : "한강수계",
lgbsn : "한강본류",
efflWqltStndrdNm : "500톤 이상\n(Ⅲ지역)",
rgnNm : "한강유역환경청"
}
];
// 시설 이력관리 (헤더)
const upHeadListFcltyHistInfo:TableUpperProps[] = [
{ id: "extnsSeqno", title: "차수", align: 'center' },
{ id: "extnsNm", title: "구분"},
{ id: "bzStrtYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:1},
{ id: "bzEdYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:2},
{ id: "eqpPrmsDt", title: "설치인가일"},
{ id: "cmplDt", title: "준공일"},
{ id: "trlrnCmplDt", title: "시운전완료일"},
{ id: "rnDt", title: "가동개시일"},
];
// 시설 이력관리 (리스트)
const gridListFcltyHistInfo:fcltyHistInfoProps[] = [
{
extnsSeqno : "최초",
extnsNm : "최초",
bzStrtYrMnth : "1984.12.01",
bzEdYrMnth : "1987.06.01",
eqpPrmsDt : "1985.09.13",
cmplDt : "1987.06.30",
trlrnCmplDt : "1987.06.30",
rnDt : "1987.06.30",
sitArea : 0,
trtDstrArea : 0,
swrTrtPopl : 0,
}
];
// 시설 수질정보 (헤더)
const upHeadListFcltyWaterQltyInfo:TableUpperProps[] = [
{ id: "extnsSeqno", title: "차수" },
{ id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' },
{ id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' },
{ id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' },
{ id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' },
{ id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' },
{ id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' },
{ id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' },
{ id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' },
{ id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' },
{ id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' },
{ id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' },
{ id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' },
{ id: "plEfflWqltBod", title: "BOD", upName:"upChangeRe3", upSequnce:1, align: 'right' },
{ id: "plEfflWqltToc", title: "TOC", upName:"upChangeRe3", upSequnce:2, align: 'right' },
{ id: "plEfflWqltCod", title: "COD", upName:"upChangeRe3", upSequnce:3, align: 'right' },
{ id: "plEfflWqltSs", title: "SS", upName:"upChangeRe3", upSequnce:4, align: 'right' },
{ id: "plEfflWqltTn", title: "T-N", upName:"upChangeRe3", upSequnce:5, align: 'right' },
{ id: "plEfflWqltTp", title: "T-P", upName:"upChangeRe3", upSequnce:6, align: 'right' },
];
// 시설 수질정보 (리스트)
const gridListFcltyWaterQltyInfo:fcltyWaterQltyInfoProps[] = [
{
extnsSeqno: "최초",
plInflWqltBod: "111.0",
plInflWqltToc: "(0.0)",
plInflWqltCod: "(0.0)",
plInflWqltSs: "122.0",
plInflWqltTn: "0.000",
plInflWqltTp: "0.000",
dsInflWqltBod: "111.0",
dsInflWqltToc: "(0.0)",
dsInflWqltCod: "(0.0)",
dsInflWqltSs: "122.0",
dsInflWqltTn: "0.000",
dsInflWqltTp: "0.000",
plEfflWqltBod: "20.0",
plEfflWqltCod: "(0.0)",
plEfflWqltToc: "(0.0)",
plEfflWqltSs: "12.0",
plEfflWqltTn: "0.000",
plEfflWqltTp: "0.000",
bod: 0,
cod: 0,
ss: 0,
tn: 0,
tp: 0,
totClfgrpcnt: 0
}
];
/**====================================
* Store - api (데이터 호출)
====================================*/
// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
const fetchFacilityList = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => {
try {
const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : '';
const url = '/prcsFclt/info/history/'+pageNo+'/'+pageSize+tailStr;
//const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl);
const res = await getFetch('fo', url);
return res;
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 시설정보 리스트 데이터 호출 (대시보드용)
const fetchFacilityListSearching = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => {
try {
const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : '';
const url = '/prcsFclt/searching/'+pageNo+'/'+pageSize+tailStr;
//const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl);
const res = await getFetch('fo', url);
return res;
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 모달
// 시설정보 이력정보 데이터 호출
const fetchFacilityHistInfo = async (facilityCd:string ): Promise<fcltyHistInfoProps[]> => {
try {
const res = await getFetch('fo', '/prcsFclt/hist/'+facilityCd);
const data = res.data;
DataForm({ type: 'date', column: 'trlrnCmplDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'eqpPrmsDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'cmplDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'rnDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'sitArea', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'trtDstrArea', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'swrTrtPopl', item: data as unknown as Record<string, unknown> });
return data;
} catch (error) {
console.error('시설정보 이력정보 데이터를 가져오는데 실패했습니다:', error);
return [];
}
};
// 시설정보 수질정보 데이터 호출
const fetchFacilityWaterQltyInfo = async (facilityCd:string ): Promise<fcltyWaterQltyInfoProps[]> => {
try {
const res = await getFetch('fo', '/prcsFclt/info/history/'+facilityCd);
const data = res.data;
return data;
} catch (error) {
console.error('시설정보 수질정보 데이터를 가져오는데 실패했습니다:', error);
return [];
}
};
/**====================================
* Store - expaort
====================================*/
export type ProcessFacilityActions = {
/**
* 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param partValue : 구분값(행정명, 대유역, 환경청)
* @param nameCd : 행정코드(시도명, 시군구명, 단위유역)
* @param fcltCpct : 시설용량
* @param oprtngSttsCd : 운영 상태 코드
* @param facilityName : 시설명
*/
searchProcessFacility : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void;
/**
* 시설정보 이력정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityHist : (facilityCd:string) => void;
/**
* 시설정보 수질정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityWaterQlty : (facilityCd:string) => void;
/**
* 시설정보 리스트 데이터 호출 (대시보드용)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacilitySearching : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void;
}
export type ProcessFacilityType = {
ProcessFacility: {
isInit: boolean;
processFacilityLabelArray:string[];
},
ProcessFacilityList : {
isInit: boolean;
upHeadList1:TableUpperProps[];
upHeadList2:TableUpperProps[];
upHeadList3:TableUpperProps[];
processFacilityList:facilityListProps[];
processFacilityCount:number;
excelList : ExcelType[];
},
ProcessFacilityDetail : {
isInit : boolean;
upHeadListBasicInfo : TableUpperProps[];
gridListBasicInfo : BasicInfoProps[];
upHeadListPlanInfo : TableUpperProps[];
gridListPlanInfo : PlanInfoProps[];
upHeadListFcltyAreaInfo : TableUpperProps[];
gridListFcltyAreaInfo : fcltyAreaInfoProps[];
upHeadListFcltyHistInfo : TableUpperProps[];
gridListFcltyHistInfo : fcltyHistInfoProps[];
upHeadListFcltyWaterQltyInfo : TableUpperProps[];
gridListFcltyWaterQltyInfo : fcltyWaterQltyInfoProps[];
upHeadListLegalWaterQlty : TableUpperProps[];
gridListLegalWaterQlty : LegalWaterQltyProps[];
},
}
export const processFacilityInitState:ProcessFacilityType = {
ProcessFacility: {
isInit: false,
processFacilityLabelArray: ProcessFacilityLabelArray,
},
ProcessFacilityList : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
processFacilityList : processFacilityList,
processFacilityCount:1,
excelList : []
},
ProcessFacilityDetail : {
isInit : false,
upHeadListBasicInfo : upHeadListBasicInfo,
gridListBasicInfo : gridListBasicInfo,
upHeadListPlanInfo : upHeadListPlanInfo,
gridListPlanInfo : gridListPlanInfo,
upHeadListFcltyAreaInfo : upHeadListFcltyAreaInfo,
gridListFcltyAreaInfo : gridListFcltyAreaInfo,
upHeadListFcltyHistInfo : upHeadListFcltyHistInfo,
gridListFcltyHistInfo : gridListFcltyHistInfo,
upHeadListFcltyWaterQltyInfo : upHeadListFcltyWaterQltyInfo,
gridListFcltyWaterQltyInfo : gridListFcltyWaterQltyInfo,
upHeadListLegalWaterQlty : upHeadListLegalWaterQlty,
gridListLegalWaterQlty : gridListLegalWaterQlty,
}
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: ProcessFacilityType | ((state: ProcessFacilityType) => ProcessFacilityType)) => void;
export type ProcessFacilityStore = ProcessFacilityType & ProcessFacilityActions;
export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActions=(set: SetStateFunction) => {
return{
// 메인페이지
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacility: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => {
const res = await fetchFacilityList(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName);
res.data.forEach((item:facilityListProps) => {
DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>});
})
res.data.forEach(item => {
item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : '';
item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : '';
})
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sido,
시군구: item.gugun,
시설이름: item.bscFctNm,
주소: item.daddr,
시설용량: item.fctCpc ? Number(item.fctCpc) : '',
계획유입수질: item.plInflWqltBod ? Number(item.plInflWqltBod) : '',
설계유입수질: item.dsInflWqltBod ? Number(item.dsInflWqltBod) : '',
공법: item.trtFrfNm,
가동개시일: item.rnDt,
준공일: item.cmplDt,
관리대행업자: item.mnstr,
})
})
}else{
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
})
}
set((state:ProcessFacilityType) => ({
...state,
ProcessFacilityList: pageSize<5000?{
...state.ProcessFacilityList,
processFacilityList: res.data,
processFacilityCount: res.count,
}:{
...state.ProcessFacilityList,
excelList: excelDownList,
},
}))
},
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacilitySearching: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => {
const res = await fetchFacilityListSearching(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName);
/* res.data.forEach((item:facilityListProps) => {
DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>});
})
res.data.forEach(item => {
item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : '';
item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : '';
}) */
console.log(res.data);
set((state:ProcessFacilityType) => ({
...state,
ProcessFacilityList: {
...state.ProcessFacilityList,
processFacilityList: res.data,
processFacilityCount: res.count,
},
}))
},
// 모달 이력정보
/**
* 시설정보 이력정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityHist: async (facilityCd:string) => {
const promiseSet = await Promise.all([fetchFacilityHistInfo(facilityCd)]);
const [facilityHistInfo] = promiseSet;
// extnsSeqno를 기준으로 오름차순 정렬
const sortedHistInfo = [...facilityHistInfo].sort((a, b) => {
// 숫자로 변환하여 비교
const numA = Number(a.extnsSeqno);
const numB = Number(b.extnsSeqno);
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB;
}
// 숫자 변환이 실패하면 문자열로 비교
return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno));
});
sortedHistInfo.forEach(item => {
if(item.extnsSeqno == '0'){
item.extnsSeqno = '최초';
item.extnsNm = '신설';
}
});
// yyyy-MM-dd로 변환
sortedHistInfo.forEach(item => {
item.eqpPrmsDt = item.eqpPrmsDt ? item.eqpPrmsDt.substring(0,4) + '-' + item.eqpPrmsDt.substring(4,6) + '-' + item.eqpPrmsDt.substring(6,8) : '';
item.trlrnCmplDt = item.trlrnCmplDt ? item.trlrnCmplDt.substring(0,4) + '-' + item.trlrnCmplDt.substring(4,6) + '-' + item.trlrnCmplDt.substring(6,8) : '';
item.cmplDt = item.cmplDt ? item.cmplDt.substring(0,4) + '-' + item.cmplDt.substring(4,6) + '-' + item.cmplDt.substring(6,8) : '';
item.rnDt = item.rnDt ? item.rnDt.substring(0,4) + '-' + item.rnDt.substring(4,6) + '-' + item.rnDt.substring(6,8) : '';
});
// 마지막 이력 데이터의 값을 gridListPlanInfo에 업데이트
if (sortedHistInfo.length > 0) {
const lastHistInfo = sortedHistInfo[sortedHistInfo.length - 1];
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyHistInfo: sortedHistInfo,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
swrTrtPopl: lastHistInfo?.swrTrtPopl ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.swrTrtPopl,
trtDstrArea: lastHistInfo?.trtDstrArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.trtDstrArea,
sitArea: lastHistInfo?.sitArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.sitArea
}]
}
}));
} else {
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyHistInfo: sortedHistInfo,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
swrTrtPopl: '',
trtDstrArea: '',
sitArea: ''
}]
}
}));
}
},
// 모달 수질정보
/**
* 시설정보 수질정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityWaterQlty: async (facilityCd:string) => {
const promiseSet = await Promise.all([fetchFacilityWaterQltyInfo(facilityCd)]);
const [facilityWaterQltyInfo] = promiseSet;
// extnsSeqno를 기준으로 오름차순 정렬
const sortedWaterQltyInfo = [...facilityWaterQltyInfo].sort((a, b) => {
// 숫자로 변환하여 비교
const numA = Number(a.extnsSeqno);
const numB = Number(b.extnsSeqno);
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB;
}
// 숫자 변환이 실패하면 문자열로 비교
return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno));
});
sortedWaterQltyInfo.forEach(item => {
if(item.extnsSeqno == '0'){
item.extnsSeqno = '최초';
}
});
// 법적수질정보 초기화 및 push
gridListLegalWaterQlty = [];
gridListLegalWaterQlty.push({
bod: facilityWaterQltyInfo[0]?.bod ?? 0,
cod: Number(facilityWaterQltyInfo[0]?.cod ?? 0),
ss: Number(facilityWaterQltyInfo[0]?.ss ?? 0),
tn: Number(facilityWaterQltyInfo[0]?.tn ?? 0),
tp: Number(facilityWaterQltyInfo[0]?.tp ?? 0),
totClfgrpcnt : Number(facilityWaterQltyInfo[0]?.totClfgrpcnt ?? 0),
});
// 마지막 수질 데이터의 값을 gridListPlanInfo에 업데이트
if (sortedWaterQltyInfo.length > 0) {
const lastWaterQltyInfo = sortedWaterQltyInfo[sortedWaterQltyInfo.length - 1];
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyWaterQltyInfo: sortedWaterQltyInfo,
gridListLegalWaterQlty: gridListLegalWaterQlty,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
dsInflWqltBod: lastWaterQltyInfo?.dsInflWqltBod === 'NULL' || lastWaterQltyInfo?.dsInflWqltBod === undefined ? '' : lastWaterQltyInfo.dsInflWqltBod,
dsInflWqltToc: lastWaterQltyInfo?.dsInflWqltToc === 'NULL' || lastWaterQltyInfo?.dsInflWqltToc === undefined ? '' : lastWaterQltyInfo.dsInflWqltToc,
dsInflWqltCod: lastWaterQltyInfo?.dsInflWqltCod === 'NULL' || lastWaterQltyInfo?.dsInflWqltCod === undefined ? '' : lastWaterQltyInfo.dsInflWqltCod,
dsInflWqltSs: lastWaterQltyInfo?.dsInflWqltSs === 'NULL' || lastWaterQltyInfo?.dsInflWqltSs === undefined ? '' : lastWaterQltyInfo.dsInflWqltSs,
dsInflWqltTn: lastWaterQltyInfo?.dsInflWqltTn === 'NULL' || lastWaterQltyInfo?.dsInflWqltTn === undefined ? '' : lastWaterQltyInfo.dsInflWqltTn,
dsInflWqltTp: lastWaterQltyInfo?.dsInflWqltTp === 'NULL' || lastWaterQltyInfo?.dsInflWqltTp === undefined ? '' : lastWaterQltyInfo.dsInflWqltTp,
plInflWqltBod: lastWaterQltyInfo?.plInflWqltBod === 'NULL' || lastWaterQltyInfo?.plInflWqltBod === undefined ? '' : lastWaterQltyInfo.plInflWqltBod,
plInflWqltToc: lastWaterQltyInfo?.plInflWqltToc === 'NULL' || lastWaterQltyInfo?.plInflWqltToc === undefined ? '' : lastWaterQltyInfo.plInflWqltToc,
plInflWqltCod: lastWaterQltyInfo?.plInflWqltCod === 'NULL' || lastWaterQltyInfo?.plInflWqltCod === undefined ? '' : lastWaterQltyInfo.plInflWqltCod,
plInflWqltSs: lastWaterQltyInfo?.plInflWqltSs === 'NULL' || lastWaterQltyInfo?.plInflWqltSs === undefined ? '' : lastWaterQltyInfo.plInflWqltSs,
plInflWqltTn: lastWaterQltyInfo?.plInflWqltTn === 'NULL' || lastWaterQltyInfo?.plInflWqltTn === undefined ? '' : lastWaterQltyInfo.plInflWqltTn,
plInflWqltTp: lastWaterQltyInfo?.plInflWqltTp === 'NULL' || lastWaterQltyInfo?.plInflWqltTp === undefined ? '' : lastWaterQltyInfo.plInflWqltTp
}]
}
}));
} else {
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyWaterQltyInfo: sortedWaterQltyInfo,
gridListLegalWaterQlty: gridListLegalWaterQlty,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
dsInflWqltBod: '',
dsInflWqltToc: '',
dsInflWqltCod: '',
dsInflWqltSs: '',
dsInflWqltTn: '',
dsInflWqltTp: '',
plEfflWqltBod: '',
plEfflWqltToc: '',
plEfflWqltCod: '',
plEfflWqltSs: '',
plEfflWqltTn: '',
plEfflWqltTp: ''
}]
}
}));
}
}
}
}
@@ -1,97 +0,0 @@
export type TableUpperProps = {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
type PublicReuseFacility = {
sido: string,
sigungo: string,
facilityName: string,
location: string,
locationgubun: string,
facilityCapacity: string,
yn?: string,
authoDay?: string,
area?: string,
reuse?: string,
register?: string
}
const upHeadList:TableUpperProps[] = [
{ id: "sido", title: "시도"},
{ id: "sigungo", title: "시군구"},
{ id: "facilityName", title: "시설명"},
{ id: "location", title: "위치"},
{ id: "locationgubun", title: "지역구분"},
{ id: "facilityCapacity", title: "시설용량"},
{ id: "yn", title: '여부', upName:'upChangeRe' , upSequnce:1 },
{ id: "authoDay", title: '허가일',upName:'upChangeRe', upSequnce:2 },
{ id: "area", title: '면적' ,upName:'upChangeRe',upSequnce:3 }];
const publicReuseFacilityUper:PublicReuseFacility[] = [
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
reuse: "Y",
register: "Y"
},
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
reuse: "Y",
register: "Y"
},
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
yn: "Y",
authoDay: "2012",
area:"123"
},
]
export type tableState = {
isInit: boolean;
upHeadList: TableUpperProps[];
publicReuseFacilityUper: PublicReuseFacility[];
}
type tableActions = {
decrementList: () => void
}
export type tableType = tableState & tableActions;
export const tableStateExport:tableState= {
isInit:false,
upHeadList: upHeadList,
publicReuseFacilityUper: publicReuseFacilityUper,
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: tableState | ((state: tableState) => tableState)) => void;
export const tableActionsExport:(set:SetStateFunction)=>tableActions=(set: SetStateFunction) => {
return {
decrementList:()=>set(
(state:tableState) => {
return {
...state,
publicReuseFacilityUper:
state.publicReuseFacilityUper.slice(0, state.publicReuseFacilityUper.length - 1)
}}
)
}
}
-274
View File
@@ -1,274 +0,0 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
export type RdiProps = {
bscFctCd : string,
sido : string,
sigungo : string,
bscFctNm : string,
lclgvrnmntNm : string,
daddr : string,
fctCpc : number,
summerMinBodValue : number,
inflAmnt : number,
winterAvgBodLowRainfall : number,
rdiiValue : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
export interface RdiInterface {
bscFctCd : string,
sido : string,
sigungo : string,
bscFctNm : string,
lclgvrnmntNm : string,
daddr : string,
fctCpc : number,
summerMinBodValue : number,
inflAmnt : number,
winterAvgBodLowRainfall : number,
rdiiValue : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
// 모달
export interface RdiInputModalProps {
msrDt : string,
realSewageInput : number | '',
sewageOccur : number | '',
underWaterInput : number | '',
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설명: string,
주소: string,
시설용량: number,
하절기강우일최저유입BOD: number | '',
하절기최저유입BOD유입유량: number | '',
동절기청천시유입BOD: number | '',
RDII추정 : number | '',
RDII추정시일강수량 : number | '',
}
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe",upSequnce:2, 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 },
{ id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "3mm 이상 강우일 중 최저 유입BOD\n(mg/L)", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량\n(m³/일)", row:1 },
{ id: "C", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "청천시 유입BOD\n(mg/L)", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량\n(mm)", row:0, rowSpan:2 },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 },
{ id: "D", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "C", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 },
{ id: "D", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 },
];
const rdiList:RdiProps[] = [
]
/**====================================
* Store - API
====================================*/
const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
try{
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/rdii/list/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
console.log(url);
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
}catch(error){
console.log(error);
return { data: rdiList, count: 0 };
}
}
/**====================================
* Store - expaort
====================================*/
export type RdiActions = {
searchRdiList: (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type RdiType = {
RdiSearch: {
isInit: boolean;
labelArray:string[];
},
Rdi : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
rdiList: RdiProps[]; // Changed from facilityAreaProps to ProcessAreaProps
rdiCount: number;
excelList: ExcelType[];
},
}
export const rdiInitState: RdiType = {
RdiSearch: {
isInit: false,
labelArray: LabelArray,
},
Rdi : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
rdiList : rdiList,
rdiCount : 2,
excelList : []
}
}
const dataFormSetting=(item : RdiProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'summerMinBodValue', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'winterAvgBodLowRainfall', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'rdiiValue', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'infoRnDay', item: item as unknown as Record<string, unknown>});
}
// Add type definitions for state setter function
type SetStateFunction = (state: RdiType | ((state: RdiType) => RdiType)) => void;
export type RdiStore = RdiType & RdiActions;
export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunction) => {
console.log("tableActionsExport");
return {
searchRdiList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchRdiList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const excelDownList: ExcelType[] = [];
const rtnData = data.map((item: RdiProps) => {
return {
...item,
sido: item.lclgvrnmntNm.split(' ')[0]?.toString() || '',
sigungo: item.lclgvrnmntNm.split(' ')[1]?.toString() || '',
inflAmnt: item.inflAmnt?item.inflAmnt.toFixed(1) : ''
}
})
rtnData.forEach((item: RdiProps) => {
dataFormSetting(item)
})
if(pageSize>5000){
rtnData.forEach((item: RdiProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시설명: item.bscFctNm,
시도: item.sido,
시군구: item.sigungo,
주소: item.daddr,
시설용량: item.fctCpc,
하절기강우일최저유입BOD: item.summerMinBodValue,
하절기최저유입BOD유입유량: item.inflAmnt,
동절기청천시유입BOD: item.winterAvgBodLowRainfall,
RDII추정 : item.rdiiValue,
RDII추정시일강수량 : item.infoRnDay,
});
});
}
set((state: RdiType) => ({
...state,
Rdi: pageSize<5000?{
...state.Rdi,
rdiList: rtnData,
rdiCount: res.count,
}:{
...state.Rdi,
excelList: excelDownList,
}
}))
},
}
}

Some files were not shown because too many files have changed in this diff Show More