Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42795e517b | ||
|
|
e9bfaa865a | ||
|
|
8a2ed7df58 | ||
|
|
00d6ff90c6 | ||
|
|
9bfd32b5c2 | ||
|
|
159fad40ef | ||
|
|
4aa4089ddd | ||
|
|
f6cb75ff1c | ||
|
|
61e4643503 | ||
|
|
8ed0a86cf5 | ||
|
|
a11f760162 | ||
|
|
e35b73d067 | ||
|
|
2620e40872 | ||
|
|
bb4d7b9d56 | ||
|
|
f6cefd6230 | ||
|
|
c6b45499c4 | ||
|
|
30bbf51e77 | ||
|
|
bb6dbfa617 | ||
|
|
1778c07ef9 | ||
|
|
075e3711a9 | ||
|
|
f58f9c3fe3 | ||
|
|
af98524806 | ||
|
|
ecf430dd33 | ||
|
|
892e329207 | ||
|
|
543fbb70cc | ||
|
|
3a191e8d06 | ||
|
|
402b9a5539 | ||
|
|
ef80915519 | ||
|
|
2e83898777 | ||
|
|
3d988b4644 | ||
|
|
c59c17f9f0 | ||
|
|
55793d8463 | ||
|
|
ba657e8311 | ||
|
|
53fef3bdbf | ||
|
|
21afb058cf | ||
|
|
5232703dd9 | ||
|
|
406ce4056a | ||
|
|
ba6d7f4177 | ||
|
|
56e9218646 | ||
|
|
2d5534b3a6 | ||
|
|
6d967497ba |
@@ -6,8 +6,8 @@ import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -21,7 +21,7 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/apilog/model/table-columns";
|
||||
|
||||
type DateRange = {
|
||||
from: Date | undefined;
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ function ApiLogPage() {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
|
||||
const [userId, setUserId] = useState("");
|
||||
const [apiHttpPmhd, setApiHttpPmhd] = useState("");
|
||||
@@ -84,8 +85,10 @@ function ApiLogPage() {
|
||||
...searchParams,
|
||||
userId,
|
||||
apiHttpPmhd,
|
||||
dmndDtStart: dayjs(dateRange.from).format("YYYY-MM-DD"),
|
||||
dmndDtEnd: dayjs(dateRange.to).format("YYYY-MM-DD"),
|
||||
dmndDtStart: dateRange.from
|
||||
? dayjs(dateRange.from).format("YYYY-MM-DD")
|
||||
: "",
|
||||
dmndDtEnd: dateRange.to ? dayjs(dateRange.to).format("YYYY-MM-DD") : "",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -126,6 +129,7 @@ function ApiLogPage() {
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
@@ -134,23 +138,31 @@ function ApiLogPage() {
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label className="mr-2">조회기간</Label>
|
||||
<DateRangePicker
|
||||
value={dateRange}
|
||||
onValueChange={(value) => setDateRange(value || defaultDateRange)}
|
||||
>
|
||||
<div className="card card-border flex items-center gap-2">
|
||||
<Select onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</DateRangePicker>
|
||||
<Select value={presetDateRange} onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">사용자ID</Label>
|
||||
<Input
|
||||
value={userId}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { Dashboard, DashboardService } from "../model/dashboard-interface";
|
||||
|
||||
export class DashboardApiService implements DashboardService {
|
||||
public async getDashboard(): Promise<Dashboard> {
|
||||
const response = await client.get<Dashboard>(
|
||||
ENDPOINT.CMS_SERVICE.DASHBOARD,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const DashboardApi = new DashboardApiService();
|
||||
@@ -0,0 +1,49 @@
|
||||
import type { ChartData, ChartDataset } from "chart.js";
|
||||
import { getCssVariableValue } from "@common/components";
|
||||
import type {
|
||||
MenuStatisticsContent,
|
||||
SiteStatisticsContent,
|
||||
} from "../model/dashboard-interface";
|
||||
|
||||
export function processSiteAccessData(
|
||||
siteAccessList: SiteStatisticsContent[],
|
||||
): ChartData<"line">["datasets"] {
|
||||
const siteGroups: Record<string, ChartDataset> = siteAccessList.reduce(
|
||||
(acc, item) => {
|
||||
if (!acc[item.siteNm]) {
|
||||
acc[item.siteNm] = {
|
||||
label: item.siteNm,
|
||||
data: Array(24).fill(0), // 24시간 데이터 초기화
|
||||
borderColor: getCssVariableValue(
|
||||
`--chart-${Math.floor(Math.random() * 5) + 1}`,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// 시간대별 접속 카운트 누적
|
||||
const hourIndex = parseInt(item.accessHour, 10);
|
||||
if (!isNaN(hourIndex) && hourIndex >= 0 && hourIndex < 24) {
|
||||
acc[item.siteNm]!.data[hourIndex] = item.accessCnt;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, ChartDataset>,
|
||||
);
|
||||
|
||||
return Object.values(siteGroups) as ChartData<"line">["datasets"];
|
||||
}
|
||||
|
||||
export function processMenuAccessData(
|
||||
menuAccessList: MenuStatisticsContent[],
|
||||
): ChartData<"bar">["datasets"] {
|
||||
const data = menuAccessList.map((item) => item.accessCnt);
|
||||
|
||||
return [
|
||||
{
|
||||
label: "메뉴별 접속 횟수",
|
||||
data,
|
||||
backgroundColor: "rgb(32, 148, 250)",
|
||||
},
|
||||
] as ChartData<"bar">["datasets"];
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
import { type ChartData, type ChartOptions } from "chart.js";
|
||||
|
||||
const commonData = {
|
||||
labels: ["1", "2", "3", "4", "5", "6", "7"],
|
||||
datasets: [
|
||||
{
|
||||
fill: true,
|
||||
pointRadius: 0,
|
||||
tension: 0.5,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const commonOptions = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
display: false,
|
||||
},
|
||||
y: {
|
||||
display: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const chartData: Record<
|
||||
string,
|
||||
{ data: ChartData<"line" | "bar">; options: ChartOptions<"line" | "bar"> }
|
||||
> = {
|
||||
memberChartData: {
|
||||
data: {
|
||||
...commonData,
|
||||
datasets: [
|
||||
{
|
||||
...commonData.datasets[0],
|
||||
data: [8, 10, 7, 12, 9, 13, 12],
|
||||
borderColor: "rgba(255, 255, 255, 0.6)",
|
||||
backgroundColor: "rgba(255, 255, 255, 0.1)",
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
scales: {
|
||||
...commonOptions.scales,
|
||||
y: {
|
||||
...commonOptions.scales.y,
|
||||
min: 5,
|
||||
max: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
postChartData: {
|
||||
data: {
|
||||
...commonData,
|
||||
datasets: [
|
||||
{
|
||||
...commonData.datasets[0],
|
||||
data: [5200, 5350, 5450, 5520, 5600, 5650, 5678],
|
||||
borderColor: "rgba(86, 138, 255, 1)",
|
||||
backgroundColor: "rgba(86, 138, 255, 0.1)",
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
},
|
||||
},
|
||||
visitorChartData: {
|
||||
data: {
|
||||
...commonData,
|
||||
datasets: [
|
||||
{
|
||||
...commonData.datasets[0],
|
||||
data: [1800, 2100, 1950, 2300, 2150, 2250, 2345],
|
||||
borderColor: "rgba(149, 131, 252)",
|
||||
backgroundColor: "rgba(149, 131, 252, 0.1)",
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
},
|
||||
},
|
||||
apiChartData: {
|
||||
data: {
|
||||
...commonData,
|
||||
datasets: [
|
||||
{
|
||||
...commonData.datasets[0],
|
||||
data: [41000, 42500, 43200, 44100, 44800, 45200, 45678],
|
||||
borderColor: "rgba(220, 103, 206)",
|
||||
backgroundColor: "rgba(220, 103, 206, 0.1)",
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
},
|
||||
},
|
||||
siteAccessChartData: {
|
||||
data: {
|
||||
labels: Array.from({ length: 24 }, (_, i) =>
|
||||
i.toString().padStart(2, "0"),
|
||||
),
|
||||
datasets: [],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "top",
|
||||
},
|
||||
tooltip: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
text: "시간대",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
text: "접속자 수",
|
||||
},
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
menuAccessChartData: {
|
||||
data: {
|
||||
labels: [],
|
||||
datasets: [],
|
||||
},
|
||||
options: {
|
||||
...commonOptions,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "top",
|
||||
},
|
||||
tooltip: {
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
title: {
|
||||
text: "메뉴명",
|
||||
},
|
||||
},
|
||||
y: {
|
||||
title: {
|
||||
text: "접속 횟수",
|
||||
},
|
||||
beginAtZero: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
export interface SiteStatisticsContent {
|
||||
siteId: string;
|
||||
siteNm: string;
|
||||
accessHour: string;
|
||||
accessCnt: number;
|
||||
}
|
||||
|
||||
export interface MenuStatisticsContent {
|
||||
menuCd: number;
|
||||
menuNm: string;
|
||||
accessCnt: number;
|
||||
}
|
||||
|
||||
export interface Dashboard {
|
||||
memberTotCnt: number;
|
||||
memberTodayCnt: number;
|
||||
pstTotCnt: number;
|
||||
pstTodayCnt: number;
|
||||
accessTodayCnt: number;
|
||||
accessBefDayPer: number;
|
||||
apiTodayCnt: number;
|
||||
waitMemberCnt: number;
|
||||
waitCntnCnt: number;
|
||||
notiList: [];
|
||||
schdlList: [];
|
||||
siteAccessList: SiteStatisticsContent[];
|
||||
menuAccessList: MenuStatisticsContent[];
|
||||
}
|
||||
|
||||
export interface DashboardService {
|
||||
getDashboard(): Promise<Dashboard>;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import dayjs from "dayjs";
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { Badge } from "@common/components";
|
||||
import type { RecentPost } from "@/entities/post";
|
||||
|
||||
const noticePostTableColumns: ColumnDef<RecentPost>[] = [
|
||||
{
|
||||
header: "번호",
|
||||
cell: ({ row, table }) => {
|
||||
if (row.original.upndFixYn === "Y") {
|
||||
return (
|
||||
<Badge
|
||||
color="orange"
|
||||
className="flex size-8 items-center justify-center"
|
||||
>
|
||||
<i className="diveicon di-volume-up-fill" />
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
const totalRows = table.getRowCount();
|
||||
const currentPage = table.getState().pagination.pageIndex;
|
||||
const pageSize = table.getState().pagination.pageSize;
|
||||
return totalRows - (currentPage * pageSize + row.index);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "pstTtl",
|
||||
header: "제목",
|
||||
},
|
||||
{
|
||||
accessorKey: "wrtrNm",
|
||||
header: "작성자",
|
||||
},
|
||||
{
|
||||
accessorKey: "regDt",
|
||||
header: "작성일",
|
||||
cell: ({ row }) => dayjs(row.original.regDt).format("YYYY-MM-DD"),
|
||||
},
|
||||
{
|
||||
accessorKey: "inqNocs",
|
||||
header: "조회수",
|
||||
},
|
||||
];
|
||||
|
||||
const recentPostTableColumns: ColumnDef<RecentPost>[] = [
|
||||
{
|
||||
header: "번호",
|
||||
cell: ({ row, table }) => {
|
||||
const totalRows = table.getRowCount();
|
||||
const currentPage = table.getState().pagination.pageIndex;
|
||||
const pageSize = table.getState().pagination.pageSize;
|
||||
return totalRows - (currentPage * pageSize + row.index);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "pstTtl",
|
||||
header: "제목",
|
||||
},
|
||||
{
|
||||
accessorKey: "wrtrNm",
|
||||
header: "작성자",
|
||||
},
|
||||
{
|
||||
accessorKey: "regDt",
|
||||
header: "작성일",
|
||||
cell: ({ row }) => dayjs(row.original.regDt).format("YYYY-MM-DD"),
|
||||
},
|
||||
{
|
||||
accessorKey: "inqNocs",
|
||||
header: "조회수",
|
||||
},
|
||||
];
|
||||
|
||||
export { noticePostTableColumns, recentPostTableColumns };
|
||||
@@ -1,5 +1,409 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel } from "@tanstack/react-table";
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
Filler,
|
||||
Legend,
|
||||
Tooltip,
|
||||
type ChartData,
|
||||
} from "chart.js";
|
||||
import { Bar, Line } from "react-chartjs-2";
|
||||
import { PostApi, type RecentPost } from "@/entities/post";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { DashboardApi } from "../api/dashboard-service";
|
||||
import { chartData } from "../model/chart-data";
|
||||
import {
|
||||
noticePostTableColumns,
|
||||
recentPostTableColumns,
|
||||
} from "../model/table-columns";
|
||||
import { processMenuAccessData, processSiteAccessData } from "../lib/utils";
|
||||
|
||||
ChartJS.register(
|
||||
CategoryScale,
|
||||
LinearScale,
|
||||
PointElement,
|
||||
LineElement,
|
||||
BarElement,
|
||||
Filler,
|
||||
Legend,
|
||||
Tooltip,
|
||||
);
|
||||
|
||||
function DashboardPage() {
|
||||
return <></>;
|
||||
const router = useRouter();
|
||||
const { data } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.DASHBOARD],
|
||||
queryFn: () => DashboardApi.getDashboard(),
|
||||
});
|
||||
|
||||
const noticePostQuery = useQuery({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
|
||||
queryFn: () =>
|
||||
PostApi.getRecentPostList({
|
||||
page: 0,
|
||||
size: 5,
|
||||
keywordType: "bbsTypeCd",
|
||||
keyword: "BBSNOTICE",
|
||||
}),
|
||||
});
|
||||
|
||||
const recentPostQuery = useQuery({
|
||||
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
|
||||
queryFn: () =>
|
||||
PostApi.getRecentPostList({
|
||||
page: 0,
|
||||
size: 5,
|
||||
}),
|
||||
});
|
||||
|
||||
const handleNoticePostRowClick = (row: RecentPost) => {
|
||||
router.push(`/bbs/bbsnotice/${row.bbsId}/${row.pstNo}`);
|
||||
};
|
||||
|
||||
const handleRecentPostRowClick = (row: RecentPost) => {
|
||||
router.push(
|
||||
`/bbs/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`,
|
||||
);
|
||||
};
|
||||
|
||||
const { tableComponent: noticePostTableComponent } = useDataTable({
|
||||
query: noticePostQuery,
|
||||
tableOptions: {
|
||||
columns: noticePostTableColumns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
},
|
||||
dataTableOptions: {
|
||||
variant: "line",
|
||||
onRowClick: handleNoticePostRowClick,
|
||||
},
|
||||
});
|
||||
|
||||
const { tableComponent: recentPostTableComponent } = useDataTable({
|
||||
query: recentPostQuery,
|
||||
tableOptions: {
|
||||
columns: recentPostTableColumns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
},
|
||||
dataTableOptions: {
|
||||
variant: "line",
|
||||
onRowClick: handleRecentPostRowClick,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="flex flex-col gap-1">
|
||||
{/* 첫번째 라인 */}
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
{/* 총 회원수 카드 */}
|
||||
<div className="card card-border h-80 !bg-primary !p-0">
|
||||
<div className="relative h-full">
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter text-white">
|
||||
<h3 className="text-2xl font-medium">총 회원수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.memberTotCnt ?? "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
금일 신규 가입자 {data?.memberTodayCnt ?? "-"}명
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
<div className="h-full">
|
||||
<Line
|
||||
data={chartData.memberChartData!.data as ChartData<"line">}
|
||||
options={chartData.memberChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 전체 게시물 카드 */}
|
||||
<div className="card card-border h-80 !p-0">
|
||||
<div className="relative h-full">
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">전체 게시물</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.pstTotCnt ?? "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
금일 +{data?.pstTodayCnt ?? "-"} 등록
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
<div className="h-full">
|
||||
<Line
|
||||
data={chartData.postChartData!.data as ChartData<"line">}
|
||||
options={chartData.postChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 오늘 접속자 수 카드 */}
|
||||
<div className="card card-border h-80 !p-0">
|
||||
<div className="relative h-full">
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">오늘 접속자 수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.accessTodayCnt ?? "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">
|
||||
전일 대비 +{data?.accessBefDayPer ?? "-"}%
|
||||
</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
<div className="h-full">
|
||||
<Line
|
||||
data={chartData.visitorChartData!.data as ChartData<"line">}
|
||||
options={chartData.visitorChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* API 호출 건수 카드 */}
|
||||
<div className="card card-border h-80 !p-0">
|
||||
<div className="relative h-full">
|
||||
<div className="absolute left-0 top-0 p-6 tracking-tighter">
|
||||
<h3 className="text-2xl font-medium">API 호출 건수</h3>
|
||||
<div className="mt-2 text-6xl font-bold">
|
||||
{data?.apiTodayCnt ?? "-"}
|
||||
</div>
|
||||
<p className="mt-1 text-2xl opacity-80">금일 누적</p>
|
||||
</div>
|
||||
{/* 차트 영역 */}
|
||||
<div className="h-full">
|
||||
<Line
|
||||
data={chartData.apiChartData!.data as ChartData<"line">}
|
||||
options={chartData.apiChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 두번째 라인 */}
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{/* 승인 대기 알림 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">승인 대기 알림</h3>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="flex gap-4">
|
||||
{/* 사용자 승인 */}
|
||||
<div className="flex flex-1 flex-col items-start rounded-md bg-accent p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-lg bg-red-100">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-red-500"
|
||||
>
|
||||
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="12" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-2xl font-medium">사용자 승인</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">
|
||||
{data?.waitMemberCnt}건
|
||||
</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
신규 가입 승인 대기
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 매뉴 승인 */}
|
||||
<div className="flex flex-1 flex-col items-start rounded-md bg-accent p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-blue-500"
|
||||
>
|
||||
<rect x="3" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="3" width="7" height="7"></rect>
|
||||
<rect x="14" y="14" width="7" height="7"></rect>
|
||||
<rect x="3" y="14" width="7" height="7"></rect>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-2xl font-medium">메뉴 승인</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">3건</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
매뉴 수정 승인 대기
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 컨텐츠 승인 */}
|
||||
<div className="flex flex-1 flex-col items-start rounded-md bg-accent p-6">
|
||||
<div className="flex items-center">
|
||||
<div className="mr-4 flex h-10 w-10 items-center justify-center rounded-lg bg-yellow-100">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="text-yellow-500"
|
||||
>
|
||||
<path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
|
||||
<polyline points="14 2 14 8 20 8"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
<span className="text-2xl font-medium">컨텐츠 승인</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<div className="text-5xl font-bold">
|
||||
{data?.waitCntnCnt}건
|
||||
</div>
|
||||
<div className="mt-2 text-2xl text-gray-500">
|
||||
컨텐츠 수정 승인 대기
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{/* 실시간 알림 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">실시간 알림</h3>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">신규 사용자 가입</span>
|
||||
<span>1분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">메뉴 수정 요청</span>
|
||||
<span>5분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">게시물 신고</span>
|
||||
<span>10분 전</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">컨텐츠 승인 요청</span>
|
||||
<span>15분 전</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 일정 알림 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">일정 알림</h3>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">3월 정기 시스템 점검</span>
|
||||
<span>내일</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">서버 업데이트 예정</span>
|
||||
<span>3일 후</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between border-b border-border pb-4">
|
||||
<span className="font-medium">백업 스케줄</span>
|
||||
<span>5일 후</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-medium">정기 보안 점검</span>
|
||||
<span>7일 후</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 세번째 라인 */}
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{/* 접속자 통계 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">접속자 통계</h3>
|
||||
</div>
|
||||
<div className="h-80 p-4">
|
||||
<Line
|
||||
data={{
|
||||
...chartData.siteAccessChartData!.data,
|
||||
datasets: processSiteAccessData(data?.siteAccessList ?? []),
|
||||
}}
|
||||
options={chartData.siteAccessChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 메뉴별 접속 통계 */}
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">메뉴별 접속 통계</h3>
|
||||
</div>
|
||||
<div className="h-80 p-4">
|
||||
<Bar
|
||||
data={{
|
||||
labels:
|
||||
data?.menuAccessList?.map((item) => item.menuNm) || [],
|
||||
datasets: processMenuAccessData(data?.menuAccessList || []),
|
||||
}}
|
||||
options={chartData.menuAccessChartData!.options}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 네번째 라인 */}
|
||||
<div className="grid grid-cols-2 gap-1">
|
||||
{/* 공지사항 */}
|
||||
<div className="card card-border !p-0">
|
||||
<div className="card-header !m-0 !border-none">
|
||||
<h3 className="text-2xl font-medium">공지사항</h3>
|
||||
</div>
|
||||
{noticePostTableComponent}
|
||||
</div>
|
||||
{/* 신규 게시글 */}
|
||||
<div className="card card-border !p-0">
|
||||
<div className="card-header !m-0 !border-none">
|
||||
<h3 className="text-2xl font-medium">신규 게시글</h3>
|
||||
</div>
|
||||
{recentPostTableComponent}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { DashboardPage };
|
||||
|
||||
@@ -6,8 +6,8 @@ import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -22,7 +22,7 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/loginlog/model/table-columns";
|
||||
|
||||
type DateRange = {
|
||||
from: Date | undefined;
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
@@ -32,13 +32,12 @@ function LoginlogPage() {
|
||||
to: dayjs().toDate(),
|
||||
};
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
|
||||
const [isManualLoading, setIsManualLoading] = useState(false);
|
||||
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
|
||||
const [userId, setUserId] = useState("");
|
||||
const [errOcrnYn, setErrOcrnYn] = useState("N");
|
||||
const [searchParams, setSearchParams] = useState<LoginlogParams>({
|
||||
@@ -48,6 +47,7 @@ function LoginlogPage() {
|
||||
searchDateTo: dayjs().format("YYYY-MM-DD"),
|
||||
sort: "lgnDt,desc",
|
||||
});
|
||||
const [isManualLoading, setIsManualLoading] = useState(false);
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.LOGINLOG, searchParams, pagination],
|
||||
@@ -88,8 +88,12 @@ function LoginlogPage() {
|
||||
...searchParams,
|
||||
userId,
|
||||
errOcrnYn,
|
||||
searchDateFrom: dayjs(dateRange.from).format("YYYY-MM-DD"),
|
||||
searchDateTo: dayjs(dateRange.to).format("YYYY-MM-DD"),
|
||||
searchDateFrom: dateRange.from
|
||||
? dayjs(dateRange.from).format("YYYY-MM-DD")
|
||||
: "",
|
||||
searchDateTo: dateRange.to
|
||||
? dayjs(dateRange.to).format("YYYY-MM-DD")
|
||||
: "",
|
||||
});
|
||||
query.refetch().finally(() => {
|
||||
setIsManualLoading(false);
|
||||
@@ -133,6 +137,7 @@ function LoginlogPage() {
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
@@ -141,23 +146,31 @@ function LoginlogPage() {
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label className="mr-2">조회기간</Label>
|
||||
<DateRangePicker
|
||||
value={dateRange}
|
||||
onValueChange={(value) => setDateRange(value || defaultDateRange)}
|
||||
>
|
||||
<div className="card card-border flex items-center gap-2">
|
||||
<Select onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</DateRangePicker>
|
||||
<Select value={presetDateRange} onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">사용자ID</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
|
||||
|
||||
@@ -110,6 +110,7 @@ function MenuPage() {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
});
|
||||
form.reset();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -24,7 +24,7 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import { columns } from "@/pages/privacylog/model/table-columns";
|
||||
|
||||
type DateRange = {
|
||||
from: Date | undefined;
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
@@ -38,6 +38,7 @@ function PrivacyLogPage() {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange>(defaultDateRange);
|
||||
const [inqInfoKeyword, setInqInfoKeyword] = useState("");
|
||||
const [searchParams, setSearchParams] = useState<PrivacyLogParams>({
|
||||
@@ -85,8 +86,10 @@ function PrivacyLogPage() {
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
inqInfoKeyword,
|
||||
inqDtStart: dayjs(dateRange.from).format("YYYY-MM-DD"),
|
||||
inqDtEnd: dayjs(dateRange.to).format("YYYY-MM-DD"),
|
||||
inqDtStart: dateRange.from
|
||||
? dayjs(dateRange.from).format("YYYY-MM-DD")
|
||||
: "",
|
||||
inqDtEnd: dateRange.to ? dayjs(dateRange.to).format("YYYY-MM-DD") : "",
|
||||
});
|
||||
};
|
||||
|
||||
@@ -125,6 +128,7 @@ function PrivacyLogPage() {
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange || defaultDateRange);
|
||||
};
|
||||
|
||||
@@ -133,30 +137,37 @@ function PrivacyLogPage() {
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label className="mr-2">조회기간</Label>
|
||||
<DateRangePicker
|
||||
value={dateRange}
|
||||
onValueChange={(value) => setDateRange(value || defaultDateRange)}
|
||||
>
|
||||
<div className="card card-border flex items-center gap-2">
|
||||
<Select onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</DateRangePicker>
|
||||
<Select value={presetDateRange} onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">조회정보</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
|
||||
value={inqInfoKeyword}
|
||||
onChange={(e) => setInqInfoKeyword(e.target.value)}
|
||||
/>
|
||||
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -37,7 +37,7 @@ import { columns } from "../model/table-columns";
|
||||
import { MovePostDialog } from "./move-post-dialog";
|
||||
|
||||
type DateRange = {
|
||||
from: Date | undefined;
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
@@ -51,22 +51,21 @@ function RecentPage() {
|
||||
const { confirm } = useConfirm();
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const [dateRange, setDateRange] = useState<DateRange | undefined>({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
});
|
||||
const [tableSort, setTableSort] = useState("regDt,desc");
|
||||
const [pagination, setPagination] = useState<PaginationState>({
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange | undefined>({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
});
|
||||
const [keywordType, setKeywordType] = useState("");
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const [bbsId, setBbsId] = useState<string | null>(null);
|
||||
const [delYn, setDelYn] = useState("N");
|
||||
|
||||
const [searchParams, setSearchParams] = useState<RecentPostParams>({
|
||||
keywordType: "",
|
||||
keyword: "",
|
||||
@@ -193,6 +192,7 @@ function RecentPage() {
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange);
|
||||
};
|
||||
|
||||
@@ -250,21 +250,31 @@ function RecentPage() {
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label className="mr-2">날짜</Label>
|
||||
<DateRangePicker value={dateRange} onValueChange={setDateRange}>
|
||||
<div className="card card-border flex items-center gap-2">
|
||||
<Select onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</DateRangePicker>
|
||||
|
||||
<Select value={presetDateRange} onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1W">일주일</SelectItem>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange?.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange?.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
<Label className="ml-4 mr-2 text-label">게시판</Label>
|
||||
<Select
|
||||
value={bbsId?.toString() || ""}
|
||||
@@ -281,7 +291,6 @@ function RecentPage() {
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Label className="ml-4 mr-2 text-label">상태</Label>
|
||||
<Select value={delYn} onValueChange={(value) => setDelYn(value)}>
|
||||
<SelectTrigger className="w-[10rem]">
|
||||
@@ -292,7 +301,6 @@ function RecentPage() {
|
||||
<SelectItem value="Y">삭제</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Label className="ml-4 mr-2 text-label">검색</Label>
|
||||
<Select
|
||||
value={keywordType}
|
||||
|
||||
@@ -8,8 +8,8 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
DateRangePicker,
|
||||
Input,
|
||||
InputDatePicker,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -28,7 +28,7 @@ import { useDataTable } from "@/shared/lib";
|
||||
import { columns } from "../model/table-columns";
|
||||
|
||||
type DateRange = {
|
||||
from: Date | undefined;
|
||||
from?: Date | undefined;
|
||||
to?: Date | undefined;
|
||||
};
|
||||
|
||||
@@ -39,6 +39,7 @@ function UsersPage() {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
const [presetDateRange, setPresetDateRange] = useState("");
|
||||
const [dateRange, setDateRange] = useState<DateRange | undefined>({
|
||||
from: undefined,
|
||||
to: undefined,
|
||||
@@ -167,6 +168,7 @@ function UsersPage() {
|
||||
default:
|
||||
presetDateRange = undefined;
|
||||
}
|
||||
setPresetDateRange(value);
|
||||
setDateRange(presetDateRange);
|
||||
};
|
||||
|
||||
@@ -277,23 +279,34 @@ function UsersPage() {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label className="mr-2 text-label">기간</Label>
|
||||
<DateRangePicker value={dateRange} onValueChange={setDateRange}>
|
||||
<div className="card card-border flex items-center gap-2">
|
||||
<Select onValueChange={handlePresetDateRange}>
|
||||
<SelectTrigger>
|
||||
<SelectValue
|
||||
placeholder="기간 선택"
|
||||
className="w-[24rem]"
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
<SelectItem value="6M">6개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</DateRangePicker>
|
||||
<Select
|
||||
value={presetDateRange}
|
||||
onValueChange={handlePresetDateRange}
|
||||
>
|
||||
<SelectTrigger className="w-[12rem]">
|
||||
<SelectValue placeholder="기간 선택" className="w-[24rem]" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1M">1개월</SelectItem>
|
||||
<SelectItem value="3M">3개월</SelectItem>
|
||||
<SelectItem value="6M">6개월</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<InputDatePicker
|
||||
value={dateRange?.from}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, from: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
~
|
||||
<InputDatePicker
|
||||
value={dateRange?.to}
|
||||
onChange={(date) => {
|
||||
setDateRange({ ...dateRange, to: date });
|
||||
setPresetDateRange("");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
|
||||
@@ -23,6 +23,7 @@ export const ENDPOINT = {
|
||||
PRIVACYLOG: "/user-service//api/v1/prvc-inq-logs",
|
||||
},
|
||||
CMS_SERVICE: {
|
||||
DASHBOARD: "/cms-service/api/v1/dashboard",
|
||||
SITES: "/cms-service/api/v1/sites",
|
||||
IPS: "/cms-service/api/v1/site-ips",
|
||||
DOMAINS: "/cms-service/api/v1/site-dmns",
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { useFoStore } from "@/app/store";
|
||||
import type { TableUpperProps } from "@/app/store/processFacility";
|
||||
import { ContentsDiv, UiModal, UiTable } from "@common/business_components";
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
|
||||
import { cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
import DashBoardChartModal from "./modal/page";
|
||||
|
||||
@@ -30,20 +30,20 @@ const headMakeColSpanPlanFclty = (upHeadList: TableUpperProps[], planYearList: s
|
||||
const headMakeColSpanOperationStatus = (upHeadList: TableUpperProps[], searchYear: string) => {
|
||||
return ( <>
|
||||
<TableRow>
|
||||
<TableHead rowSpan={1} colSpan={17} style={{ whiteSpace: 'pre-line' }}>{searchYear+'년도 운영실태'}</TableHead>
|
||||
<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 style={{ whiteSpace: 'pre-line' }} rowSpan={2}key={head.id}>{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))}
|
||||
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 style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
|
||||
(head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'||head.upName === 'upChangeRe4'||head.upName === 'upChangeRe5' ?<TableHead key={head.id}>{head.title}</TableHead>:null))}
|
||||
</TableRow>
|
||||
</> )
|
||||
}
|
||||
@@ -238,10 +238,10 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}:
|
||||
(head.id === 'flowRate' && index === 11)?<TableCell rowSpan={2} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
(head.id === 'rdi'&&index === 0)?<TableCell rowSpan={7} 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 === 'rdiRain'&&index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el['rdiRain']}</TableCell>:
|
||||
(head.id === 'rdiRain'&&index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}></TableCell>:
|
||||
(head.id === 'waterQlty')?<TableCell className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
|
||||
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=0 && index<=4))?<TableCell className="bg-yellow-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
|
||||
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=5 && index<=10))?<TableCell className="bg-green-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
|
||||
|
||||
@@ -14,7 +14,7 @@ const tabList = [
|
||||
{ value: 'facilityInfo', label: '시설정보 및 운영현황' },
|
||||
{ value: 'mainGraph', label: '주요그래프' },
|
||||
{ value: 'rainManage', label: '강우일 운영현황' },
|
||||
//{ value: 'drainageEquipment', label: '배수설비 정비현황' }
|
||||
{ value: 'drainageEquipment', label: '배수설비 정비현황' }
|
||||
];
|
||||
|
||||
export default function DashBoard() {
|
||||
@@ -57,8 +57,7 @@ export default function DashBoard() {
|
||||
|
||||
/******************선택된 데이터******************/
|
||||
// 연도
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
|
||||
const [searchYear, setSearchYear] = useState<string>('2021');
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
|
||||
// 시설명
|
||||
const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
|
||||
bscFctCd: '',
|
||||
@@ -72,11 +71,11 @@ export default function DashBoard() {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
setFcltyList({
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
bscFctCd: '11000SW001R',
|
||||
bscFctNm: '난지',
|
||||
fcltyCpc: 860000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, A2O',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
|
||||
});
|
||||
}
|
||||
}, [isInit, initializeCommonData]);
|
||||
@@ -258,11 +257,11 @@ export default function DashBoard() {
|
||||
|
||||
const initVal = () => {
|
||||
setFcltyList({
|
||||
bscFctCd: '26260PB001R',
|
||||
bscFctNm: '수영',
|
||||
fcltyCpc: 452000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, MBR',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅳ지역)'
|
||||
bscFctCd: '11000SW001R',
|
||||
bscFctNm: '난지',
|
||||
fcltyCpc: 860000,
|
||||
trtFrfNm: '표준활성슬러지법, MLE, A2O',
|
||||
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,7 @@ export default function FacilityPrecipitation() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -126,9 +125,9 @@ export default function FacilityPrecipitation() {
|
||||
useEffect(() => {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
searchFacilityPrecipitationList(1, 10, searchYear || "0");
|
||||
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, searchYear]);
|
||||
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
|
||||
@@ -63,7 +63,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let maxEfflWqltBod = 0;
|
||||
let maxEfflWqltTn = 0;
|
||||
let maxEfflWqltTp = 0;
|
||||
let maxefflWqltToc = 0;
|
||||
let maxefflWqltCod = 0;
|
||||
let maxEfflWqltSs = 0;
|
||||
|
||||
let maxInflAssctAftWqltBod = 0;
|
||||
@@ -80,7 +80,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let minEfflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltBod) : 0;
|
||||
let minEfflWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTn) : 0;
|
||||
let minEfflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTp) : 0;
|
||||
let minefflWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltToc) : 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;
|
||||
@@ -97,7 +97,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
let avgEfflWqltBod = 0;
|
||||
let avgEfflWqltTn = 0;
|
||||
let avgEfflWqltTp = 0;
|
||||
let avgefflWqltToc = 0;
|
||||
let avgefflWqltCod = 0;
|
||||
let avgEfflWqltSs = 0;
|
||||
|
||||
let avgInflAssctAftWqltBod = 0;
|
||||
@@ -115,7 +115,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
const efflWqltBod = parseNumber(item.efflWqltBod);
|
||||
const efflWqltTn = parseNumber(item.efflWqltTn);
|
||||
const efflWqltTp = parseNumber(item.efflWqltTp);
|
||||
const efflWqltToc = parseNumber(item.efflWqltToc);
|
||||
const efflWqltCod = parseNumber(item.efflWqltCod);
|
||||
const efflWqltSs = parseNumber(item.efflWqltSs);
|
||||
|
||||
const inflAssctAftWqltBod = parseNumber(item.inflAssctAftWqltBod);
|
||||
@@ -133,7 +133,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
maxEfflWqltBod = Math.max(maxEfflWqltBod, efflWqltBod);
|
||||
maxEfflWqltTn = Math.max(maxEfflWqltTn, efflWqltTn);
|
||||
maxEfflWqltTp = Math.max(maxEfflWqltTp, efflWqltTp);
|
||||
maxefflWqltToc = Math.max(maxefflWqltToc, efflWqltToc);
|
||||
maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod);
|
||||
maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs);
|
||||
|
||||
maxInflAssctAftWqltBod = Math.max(maxInflAssctAftWqltBod, inflAssctAftWqltBod);
|
||||
@@ -150,7 +150,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
minEfflWqltBod = Math.min(minEfflWqltBod, efflWqltBod);
|
||||
minEfflWqltTn = Math.min(minEfflWqltTn, efflWqltTn);
|
||||
minEfflWqltTp = Math.min(minEfflWqltTp, efflWqltTp);
|
||||
minefflWqltToc = Math.min(minefflWqltToc, efflWqltToc);
|
||||
minefflWqltCod = Math.min(minefflWqltCod, efflWqltCod);
|
||||
minEfflWqltSs = Math.min(minEfflWqltSs, efflWqltSs);
|
||||
|
||||
minInflAssctAftWqltBod = Math.min(minInflAssctAftWqltBod, inflAssctAftWqltBod);
|
||||
@@ -167,7 +167,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
avgEfflWqltBod += efflWqltBod;
|
||||
avgEfflWqltTn += efflWqltTn;
|
||||
avgEfflWqltTp += efflWqltTp;
|
||||
avgefflWqltToc += efflWqltToc;
|
||||
avgefflWqltCod += efflWqltCod;
|
||||
avgEfflWqltSs += efflWqltSs;
|
||||
|
||||
avgInflAssctAftWqltBod += inflAssctAftWqltBod;
|
||||
@@ -186,7 +186,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt: '평균',
|
||||
efflWqltBod: filteredData.length==0?'0.0':(Math.round(avgEfflWqltBod / filteredData.length * 10) / 10).toFixed(1),
|
||||
efflWqltToc: filteredData.length==0?'0.0':(Math.round(avgefflWqltToc / 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),
|
||||
@@ -209,7 +209,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt : '최대',
|
||||
efflWqltBod : Math.round(maxEfflWqltBod * 10) / 10?(Math.round(maxEfflWqltBod * 10) / 10).toFixed(1):'0.0',
|
||||
efflWqltToc : Math.round(maxefflWqltToc * 10) / 10?(Math.round(maxefflWqltToc * 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',
|
||||
@@ -232,7 +232,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
{
|
||||
msrDt : '최소',
|
||||
efflWqltBod : Math.round(minEfflWqltBod * 10) / 10?(Math.round(minEfflWqltBod?minEfflWqltBod:0 * 10) / 10).toFixed(1) : '0.0',
|
||||
efflWqltToc : Math.round(minefflWqltToc * 10) / 10?(Math.round(minefflWqltToc?minefflWqltToc: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',
|
||||
@@ -299,16 +299,16 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
"유입TN(mg/L)": item.inflAssctAftWqltTn,
|
||||
"유입TP(mg/L)": item.inflAssctBfrWqltTp,
|
||||
"유출BOD(mg/L)": item.efflWqltBod,
|
||||
"유출TOC(COD)(mg/L)": item.efflWqltToc,
|
||||
"유출TOC(COD)(mg/L)": item.efflWqltCod,
|
||||
"유출SS(mg/L)": item.efflWqltSs,
|
||||
"유출TN(mg/L)": item.efflWqltTn,
|
||||
"유출TP(mg/L)": item.efflWqltTp,
|
||||
/* "강우량(mm/일)": item.infoRnDay?item.infoRnDay.toString():"0.0", */
|
||||
"강우량(일)": 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:K1' });
|
||||
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: searchYear+'년 '+fcltyName + ' 수질현황', sheetNameArray: [fcltyName + ' 수질현황'], headerRange: 'A1:L1' });
|
||||
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
|
||||
alert('다운받을 데이터가 없습니다.');
|
||||
}
|
||||
@@ -351,7 +351,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
</div>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">수질 TMS</h3>
|
||||
{/*<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3> */}
|
||||
<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3>
|
||||
</div>
|
||||
<div className="overflow-y-auto h-[250px]">
|
||||
<UiTable
|
||||
@@ -449,9 +449,6 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
|
||||
</UiTable>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 청천시 : 강우량이 3mm미만의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tab_term_rain">
|
||||
@@ -479,9 +476,6 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe
|
||||
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
|
||||
</UiTable>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날</span>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</UITab>
|
||||
</TabsContent>
|
||||
|
||||
@@ -48,8 +48,7 @@ export default function ProcessFacilitySearch() {
|
||||
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
|
||||
const [fcltyName, setFcltyName] = useState<string>("");
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
//const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>("2023");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [fcltCpct, setFcltCpct] = useState<string>("0");
|
||||
|
||||
// 모달 상태 추가
|
||||
@@ -197,7 +196,7 @@ export default function ProcessFacilitySearch() {
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>}
|
||||
bodyClass="sm:max-w-[90vw] w-[1300px] h-[920px]"
|
||||
bodyClass="sm:max-w-[90vw] w-[1200px] h-[920px]"
|
||||
open={selectedRow !== null}
|
||||
onOpenChange={(open) => !open && closeModal()}
|
||||
noHeaderYn="Y"
|
||||
|
||||
@@ -52,7 +52,6 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
return Number(value.replace(/,/g, ''));
|
||||
};
|
||||
|
||||
console.log('filteredData : ', filteredData);
|
||||
let maxtotInflAmnt = 0;
|
||||
let maxinflAmnt = 0;
|
||||
let maxsumFlow = 0;
|
||||
@@ -66,7 +65,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
let minsumFlow = parseNumber(filteredData[0]?.sumFlow);
|
||||
let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt);
|
||||
let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt);
|
||||
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay && filteredData[0]?.infoRnDay.toString().trim() !== 'x' ? filteredData[0]?.infoRnDay.toString().trim() : 0);
|
||||
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay);
|
||||
|
||||
let avgtotInflAmnt = 0;
|
||||
let avginflAmnt = 0;
|
||||
@@ -81,7 +80,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
const sumFlow = parseNumber(item.sumFlow);
|
||||
const returnAmnt = parseNumber(item.returnAmnt);
|
||||
const efflAmnt = parseNumber(item.efflAmnt);
|
||||
const infoRnDay = parseNumber(item.infoRnDay && item.infoRnDay.toString().trim() !== 'x' ? item.infoRnDay.toString().trim() : 0);
|
||||
const infoRnDay = parseNumber(item.infoRnDay);
|
||||
maxtotInflAmnt = Math.max(maxtotInflAmnt, totInflAmnt);
|
||||
maxinflAmnt = Math.max(maxinflAmnt, inflAmnt);
|
||||
maxsumFlow = Math.max(maxsumFlow, sumFlow);
|
||||
@@ -167,7 +166,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
"연계처리량(m³/일)": item.sumFlow,
|
||||
"총인반류수량(m³/일)": item.returnAmnt,
|
||||
"방류량(m³/일)": item.efflAmnt,
|
||||
/* "강우량(mm/일)": item.infoRnDay, */
|
||||
"강우량": item.infoRnDay,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -195,18 +194,20 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
<div 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">
|
||||
@@ -219,18 +220,20 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
<div 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">
|
||||
@@ -243,18 +246,20 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
<div 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">
|
||||
@@ -267,21 +272,20 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<UiTable
|
||||
headName="유량현황"
|
||||
tableData={copyYearList}
|
||||
headlist={upHeadListFlowRateModalYear}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
className="h-[370px]"
|
||||
boldArray={[0,1,2]}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 청천시 : 강우량이 3mm미만의 날</span>
|
||||
<div 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">
|
||||
@@ -294,11 +298,10 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
|
||||
ref={ref}
|
||||
queryClick={filterData}
|
||||
/>
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
|
||||
<div className="h-[370px] overflow-y-auto overflow-x-auto">
|
||||
<div className="w-[120%]">
|
||||
<UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}/>
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날</span>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</UITab>
|
||||
|
||||
@@ -49,8 +49,7 @@ export default function ProcessFacilitySearch() {
|
||||
const [fcltCpct, setFcltCpct] = useState<string>("0");
|
||||
const [fcltyName, setFcltyName] = useState<string>("");
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
//const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYearValue, setSearchYearValue] = useState<string>("2023");
|
||||
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);
|
||||
@@ -126,11 +125,10 @@ export default function ProcessFacilitySearch() {
|
||||
// 시도 데이터 초기화
|
||||
useEffect(() => {
|
||||
if(!isInit) {
|
||||
console.log('searchYearValue', searchYearValue, isInit);
|
||||
initializeCommonData();
|
||||
searchFlowRateList(1, 10, searchYearValue || "0");
|
||||
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchFlowRateList, searchYearValue]);
|
||||
}, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
@@ -199,7 +197,7 @@ export default function ProcessFacilitySearch() {
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>}
|
||||
bodyClass="sm:max-w-[90vw] w-[1250px] h-[780px]"
|
||||
bodyClass="sm:max-w-[90vw] w-[1050px] h-[700px]"
|
||||
open={selectedRow !== null}
|
||||
onOpenChange={(open: boolean) => !open && closeModal()}
|
||||
/>
|
||||
|
||||
@@ -70,8 +70,7 @@ export default function PlanSewage() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -191,11 +190,6 @@ export default function PlanSewage() {
|
||||
<div className={widthSize}>
|
||||
<UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
|
||||
</div>
|
||||
<div className="mt-14">
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 여유용량 = 하수처리시설 시설용량 – 건기시 유입유량(평균)</span><br/>
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 변동부하율 = 건기시 일최대 유량/일평균유량(기준 최근 3개년)</span><br/>
|
||||
<span className="text-2xl font-bold text-gray-500">☞ 분류식화율 = 분류식지역 면적(㎢) / 처리구역 면적합계(㎢) ×100</span><br/>
|
||||
</div>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</MainDiv>
|
||||
|
||||
@@ -1,35 +1,104 @@
|
||||
"use client"
|
||||
import { useFoStore } from "@/app/store";
|
||||
import type { modalTableType } from "@/app/store/sewageOccur";
|
||||
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, searchSewageOccurDetailList} = useFoStore((state) => state);
|
||||
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<modalTableType[]>([]);
|
||||
const [detailList, setDetailList] = useState<SewageOccurDetailProps[]>([]);
|
||||
const {
|
||||
upHeadListSewageOccurDetail,
|
||||
gridListSewageOccurDetail
|
||||
} = SewageOccurDetail;
|
||||
|
||||
useEffect(() => {
|
||||
searchSewageOccurDetailList(props.facilityCd, validSearchYear);
|
||||
}, [searchSewageOccurDetailList, props.facilityCd, validSearchYear]);
|
||||
|
||||
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">
|
||||
|
||||
@@ -18,8 +18,7 @@ export default function PlanSewage() {
|
||||
SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
|
||||
SewageOccurSearch: { labelArray: [] },
|
||||
initializeCommonData: () => {},
|
||||
searchSewageOccurList: () => {},
|
||||
calculateModalDataForAllFacilities: async () => []
|
||||
searchSewageOccurList: () => {}
|
||||
};
|
||||
|
||||
const {
|
||||
@@ -84,8 +83,7 @@ export default function PlanSewage() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
// const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
@@ -128,9 +126,9 @@ export default function PlanSewage() {
|
||||
useEffect(() => {
|
||||
if (!isInit) {
|
||||
initializeCommonData();
|
||||
searchSewageOccurList(1, 10, searchYear || "0");
|
||||
searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0");
|
||||
}
|
||||
}, [isInit, initializeCommonData, searchSewageOccurList, searchYear]);
|
||||
}, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]);
|
||||
|
||||
useEffect(() => {
|
||||
if (excelList && excelList.length > 0) {
|
||||
@@ -226,11 +224,7 @@ export default function PlanSewage() {
|
||||
</ContentsDiv>
|
||||
{selectedRow !== null && (
|
||||
<UiModal
|
||||
body={<SewageOccurModal
|
||||
fcltyName={fcltyNm}
|
||||
facilityCd={fcltyCd}
|
||||
searchYear={searchYear}
|
||||
/>}
|
||||
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()}
|
||||
|
||||
@@ -82,8 +82,7 @@ export default function UnderWaterInput() {
|
||||
// 시설상태
|
||||
const [operationValue, setOperationValue] = useState<string>("0");
|
||||
// 조회기간
|
||||
//const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
const [searchYear, setSearchYear] = useState<string>("2023");
|
||||
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
|
||||
|
||||
/**
|
||||
* 페이지 함수
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
====================================**/
|
||||
|
||||
import { getFetch } from "@/app/components/api";
|
||||
import { LoginApi } from "@/src/entities/auth";
|
||||
import type { ILogin } from "@/src/entities/auth/model/login-interface";
|
||||
|
||||
/**
|
||||
* STORE - TYPE
|
||||
@@ -411,6 +413,15 @@ export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)
|
||||
|
||||
// 시도 변경 이벤트
|
||||
initializeCommonData: async () => {
|
||||
const payLoad : ILogin = {
|
||||
userId: "tuser10003",
|
||||
password: "test1234!"
|
||||
};
|
||||
|
||||
console.log(payLoad.userId)
|
||||
console.log(payLoad.password)
|
||||
|
||||
LoginApi.login(payLoad);
|
||||
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
|
||||
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
|
||||
set((state: CommonType) => ({
|
||||
|
||||
@@ -347,7 +347,6 @@ const upHeadListOperationStatus : TableUpperProps[] = [
|
||||
{ id: "groundWaterSummer", title: "하절기\n평균", upName:"upChangeRe5", upSequnce:2 },
|
||||
{ id: "groundWaterWinter", title: "동절기\n평균", upName:"upChangeRe5", upSequnce:3 },
|
||||
{ id: "rdi", title: "RDII 추정" },
|
||||
{ id: "rdiRain", title: "RDII 추정시\n일강수량(mm)" },
|
||||
]
|
||||
|
||||
const upHeadListRainManage : TableUpperProps[] = [
|
||||
@@ -1208,42 +1207,42 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
}
|
||||
const fcltyCpcValue = res.data.wqltObjs[0].fctCpc;
|
||||
|
||||
const itemList = ['계', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50mm~60mm 미만', '60mm 이상'];
|
||||
const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0'];
|
||||
const rainDate = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTn: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTp: number[] = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTocCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltToc = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSs = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTn = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTp = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCodCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCod = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSs = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTn = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTp = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const itemList = ['계', '3mm 미만', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50mm~60mm 미만', '60mm 이상'];
|
||||
const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0', '0'];
|
||||
const rainDate = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTn: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const dsInflWqltTp: number[] = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTocCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltToc = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const inflAssctAftWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltBod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCodCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltCod = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSsCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltSs = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTnCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTn = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTpCnt = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
const efflWqltTp = [0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
// Process the data and create rain management list
|
||||
res.data.wqltObjs.forEach((item: RainManageListProps) => {
|
||||
|
||||
// 강우일
|
||||
/*if(Number(item.infoRnDay) < 3){
|
||||
if(Number(item.infoRnDay) < 3){
|
||||
rainDate[1] = Number(rainDate[1] || 0) + 1;
|
||||
inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1;
|
||||
inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1268,33 +1267,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1;
|
||||
efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0);
|
||||
}*/
|
||||
if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){
|
||||
rainDate[1] = Number(rainDate[1] || 0) + 1;
|
||||
inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1;
|
||||
inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[1] = 0;
|
||||
inflAssctAftWqltBodCnt[1] = Number(inflAssctAftWqltBodCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltBod[1] = Number(inflAssctAftWqltBod[1] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[1] = Number(inflAssctAftWqltTocCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltToc[1] = Number(inflAssctAftWqltToc[1] || 0) + Number(item.inflAssctAftWqltToc || 0);
|
||||
inflAssctAftWqltSsCnt[1] = Number(inflAssctAftWqltSsCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltSs[1] = Number(inflAssctAftWqltSs[1] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[1] = Number(inflAssctAftWqltTnCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltTn[1] = Number(inflAssctAftWqltTn[1] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[1] = Number(inflAssctAftWqltTpCnt[1] || 0) + 1;
|
||||
inflAssctAftWqltTp[1] = Number(inflAssctAftWqltTp[1] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[1] = Number(efflWqltBodCnt[1] || 0) + 1;
|
||||
efflWqltBod[1] = Number(efflWqltBod[1] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[1] = Number(efflWqltCodCnt[1] || 0) + 1;
|
||||
efflWqltCod[1] = Number(efflWqltCod[1] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[1] = Number(efflWqltSsCnt[1] || 0) + 1;
|
||||
efflWqltSs[1] = Number(efflWqltSs[1] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[1] = Number(efflWqltTnCnt[1] || 0) + 1;
|
||||
efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1;
|
||||
efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 10 && Number(item.infoRnDay) < 20){
|
||||
}else if(Number(item.infoRnDay) >= 3 && Number(item.infoRnDay) < 10){
|
||||
rainDate[2] = Number(rainDate[2] || 0) + 1;
|
||||
inflAmtCnt[2] = Number(inflAmtCnt[2] || 0) + 1;
|
||||
inflAmnt[2] = Number(inflAmnt[2] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1310,7 +1283,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
inflAssctAftWqltTpCnt[2] = Number(inflAssctAftWqltTpCnt[2] || 0) + 1;
|
||||
inflAssctAftWqltTp[2] = Number(inflAssctAftWqltTp[2] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[2] = Number(efflWqltBodCnt[2] || 0) + 1;
|
||||
efflWqltBod[2] = Number(efflWqltBod[2] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltBod[2] = Number(efflWqltBod[2] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[2] = Number(efflWqltCodCnt[2] || 0) + 1;
|
||||
efflWqltCod[2] = Number(efflWqltCod[2] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[2] = Number(efflWqltSsCnt[2] || 0) + 1;
|
||||
@@ -1319,7 +1292,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[2] = Number(efflWqltTn[2] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[2] = Number(efflWqltTpCnt[2] || 0) + 1;
|
||||
efflWqltTp[2] = Number(efflWqltTp[2] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 20 && Number(item.infoRnDay) < 30){
|
||||
}else if(Number(item.infoRnDay) >= 10 && Number(item.infoRnDay) < 20){
|
||||
rainDate[3] = Number(rainDate[3] || 0) + 1;
|
||||
inflAmtCnt[3] = Number(inflAmtCnt[3] || 0) + 1;
|
||||
inflAmnt[3] = Number(inflAmnt[3] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1344,11 +1317,11 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[3] = Number(efflWqltTn[3] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[3] = Number(efflWqltTpCnt[3] || 0) + 1;
|
||||
efflWqltTp[3] = Number(efflWqltTp[3] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 30 && Number(item.infoRnDay) < 40){
|
||||
}else if(Number(item.infoRnDay) >= 20 && Number(item.infoRnDay) < 30){
|
||||
rainDate[4] = Number(rainDate[4] || 0) + 1;
|
||||
inflAmtCnt[4] = Number(inflAmtCnt[4] || 0) + 1;
|
||||
inflAmnt[4] = Number(inflAmnt[4] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[4] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0;
|
||||
fcltyCpct[4] = 0;
|
||||
inflAssctAftWqltBodCnt[4] = Number(inflAssctAftWqltBodCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltBod[4] = Number(inflAssctAftWqltBod[4] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[4] = Number(inflAssctAftWqltTocCnt[4] || 0) + 1;
|
||||
@@ -1356,7 +1329,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
inflAssctAftWqltSsCnt[4] = Number(inflAssctAftWqltSsCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltSs[4] = Number(inflAssctAftWqltSs[4] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[4] = Number(inflAssctAftWqltTnCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltTn[4] = Number(inflAssctAftWqltTn[5] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTn[4] = Number(inflAssctAftWqltTn[4] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[4] = Number(inflAssctAftWqltTpCnt[4] || 0) + 1;
|
||||
inflAssctAftWqltTp[4] = Number(inflAssctAftWqltTp[4] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[4] = Number(efflWqltBodCnt[4] || 0) + 1;
|
||||
@@ -1365,15 +1338,15 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltCod[4] = Number(efflWqltCod[4] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[4] = Number(efflWqltSsCnt[4] || 0) + 1;
|
||||
efflWqltSs[4] = Number(efflWqltSs[4] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[4] = Number(efflWqltTnCnt[5] || 0) + 1;
|
||||
efflWqltTnCnt[4] = Number(efflWqltTnCnt[4] || 0) + 1;
|
||||
efflWqltTn[4] = Number(efflWqltTn[4] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[4] = Number(efflWqltTpCnt[5] || 0) + 1;
|
||||
efflWqltTpCnt[4] = Number(efflWqltTpCnt[4] || 0) + 1;
|
||||
efflWqltTp[4] = Number(efflWqltTp[4] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){
|
||||
}else if(Number(item.infoRnDay) >= 30 && Number(item.infoRnDay) < 40){
|
||||
rainDate[5] = Number(rainDate[5] || 0) + 1;
|
||||
inflAmtCnt[5] = Number(inflAmtCnt[5] || 0) + 1;
|
||||
inflAmnt[5] = Number(inflAmnt[5] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[5] = 0;
|
||||
fcltyCpct[5] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0;
|
||||
inflAssctAftWqltBodCnt[5] = Number(inflAssctAftWqltBodCnt[5] || 0) + 1;
|
||||
inflAssctAftWqltBod[5] = Number(inflAssctAftWqltBod[5] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[5] = Number(inflAssctAftWqltTocCnt[5] || 0) + 1;
|
||||
@@ -1394,7 +1367,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[5] = Number(efflWqltTn[5] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[5] = Number(efflWqltTpCnt[5] || 0) + 1;
|
||||
efflWqltTp[5] = Number(efflWqltTp[5] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 50 && Number(item.infoRnDay) < 60){
|
||||
}else if(Number(item.infoRnDay) >= 40 && Number(item.infoRnDay) < 50){
|
||||
rainDate[6] = Number(rainDate[6] || 0) + 1;
|
||||
inflAmtCnt[6] = Number(inflAmtCnt[6] || 0) + 1;
|
||||
inflAmnt[6] = Number(inflAmnt[6] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1419,7 +1392,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[6] = Number(efflWqltTn[6] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[6] = Number(efflWqltTpCnt[6] || 0) + 1;
|
||||
efflWqltTp[6] = Number(efflWqltTp[6] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 60){
|
||||
}else if(Number(item.infoRnDay) >= 50 && Number(item.infoRnDay) < 60){
|
||||
rainDate[7] = Number(rainDate[7] || 0) + 1;
|
||||
inflAmtCnt[7] = Number(inflAmtCnt[7] || 0) + 1;
|
||||
inflAmnt[7] = Number(inflAmnt[7] || 0) + Number(item.inflAmnt || 0);
|
||||
@@ -1444,6 +1417,31 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
efflWqltTn[7] = Number(efflWqltTn[7] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[7] = Number(efflWqltTpCnt[7] || 0) + 1;
|
||||
efflWqltTp[7] = Number(efflWqltTp[7] || 0) + Number(item.efflWqltTp || 0);
|
||||
}else if(Number(item.infoRnDay) >= 60){
|
||||
rainDate[8] = Number(rainDate[8] || 0) + 1;
|
||||
inflAmtCnt[8] = Number(inflAmtCnt[8] || 0) + 1;
|
||||
inflAmnt[8] = Number(inflAmnt[8] || 0) + Number(item.inflAmnt || 0);
|
||||
fcltyCpct[8] = 0;
|
||||
inflAssctAftWqltBodCnt[8] = Number(inflAssctAftWqltBodCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltBod[8] = Number(inflAssctAftWqltBod[8] || 0) + Number(item.inflAssctAftWqltBod || 0);
|
||||
inflAssctAftWqltTocCnt[8] = Number(inflAssctAftWqltTocCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltToc[8] = Number(inflAssctAftWqltToc[8] || 0) + Number(item.inflAssctAftWqltToc || 0);
|
||||
inflAssctAftWqltSsCnt[8] = Number(inflAssctAftWqltSsCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltSs[8] = Number(inflAssctAftWqltSs[8] || 0) + Number(item.inflAssctAftWqltSs || 0);
|
||||
inflAssctAftWqltTnCnt[8] = Number(inflAssctAftWqltTnCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltTn[8] = Number(inflAssctAftWqltTn[8] || 0) + Number(item.inflAssctAftWqltTn || 0);
|
||||
inflAssctAftWqltTpCnt[8] = Number(inflAssctAftWqltTpCnt[8] || 0) + 1;
|
||||
inflAssctAftWqltTp[8] = Number(inflAssctAftWqltTp[8] || 0) + Number(item.inflAssctAftWqltTp || 0);
|
||||
efflWqltBodCnt[8] = Number(efflWqltBodCnt[8] || 0) + 1;
|
||||
efflWqltBod[8] = Number(efflWqltBod[8] || 0) + Number(item.efflWqltBod || 0);
|
||||
efflWqltCodCnt[8] = Number(efflWqltCodCnt[8] || 0) + 1;
|
||||
efflWqltCod[8] = Number(efflWqltCod[8] || 0) + Number(item.efflWqltCod || 0);
|
||||
efflWqltSsCnt[8] = Number(efflWqltSsCnt[8] || 0) + 1;
|
||||
efflWqltSs[8] = Number(efflWqltSs[8] || 0) + Number(item.efflWqltSs || 0);
|
||||
efflWqltTnCnt[8] = Number(efflWqltTnCnt[8] || 0) + 1;
|
||||
efflWqltTn[8] = Number(efflWqltTn[8] || 0) + Number(item.efflWqltTn || 0);
|
||||
efflWqltTpCnt[8] = Number(efflWqltTpCnt[8] || 0) + 1;
|
||||
efflWqltTp[8] = Number(efflWqltTp[8] || 0) + Number(item.efflWqltTp || 0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1490,7 +1488,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
|
||||
rainDate[0] = (Number(rainDate[1]) || 0) + (Number(rainDate[2]) || 0) + (Number(rainDate[3]) || 0) +
|
||||
(Number(rainDate[4]) || 0) + (Number(rainDate[5]) || 0) + (Number(rainDate[6]) || 0) +
|
||||
(Number(rainDate[7]) || 0);
|
||||
(Number(rainDate[7]) || 0) + (Number(rainDate[8]) || 0);
|
||||
|
||||
// 강수비율
|
||||
for(let idx=0; idx<rainDate.length; idx++){
|
||||
@@ -1534,16 +1532,16 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
|
||||
|
||||
inflAmnt[0] = calculateAverage(calculateTotal(inflAmnt), dataIsCount);
|
||||
fcltyCpct[0] = calculateTotal(fcltyCpct);
|
||||
inflAssctAftWqltBod[0] = Number((calculateTotal(inflAssctAftWqltBod)/7).toFixed(1));
|
||||
inflAssctAftWqltToc[0] = Number((calculateTotal(inflAssctAftWqltToc)/7).toFixed(1));
|
||||
inflAssctAftWqltSs[0] = Number((calculateTotal(inflAssctAftWqltSs)/7).toFixed(1));
|
||||
inflAssctAftWqltTn[0] = Number((calculateTotal(inflAssctAftWqltTn)/7).toFixed(3));
|
||||
inflAssctAftWqltTp[0] = Number((calculateTotal(inflAssctAftWqltTp)/7).toFixed(3));
|
||||
efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/7).toFixed(1));
|
||||
efflWqltCod[0] = Number((calculateTotal(efflWqltCod)/7).toFixed(1));
|
||||
efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/7).toFixed(1));
|
||||
efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/7).toFixed(3));
|
||||
efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/7).toFixed(3));
|
||||
inflAssctAftWqltBod[0] = Number((calculateTotal(inflAssctAftWqltBod)/8).toFixed(1));
|
||||
inflAssctAftWqltToc[0] = Number((calculateTotal(inflAssctAftWqltToc)/8).toFixed(1));
|
||||
inflAssctAftWqltSs[0] = Number((calculateTotal(inflAssctAftWqltSs)/8).toFixed(1));
|
||||
inflAssctAftWqltTn[0] = Number((calculateTotal(inflAssctAftWqltTn)/8).toFixed(3));
|
||||
inflAssctAftWqltTp[0] = Number((calculateTotal(inflAssctAftWqltTp)/8).toFixed(3));
|
||||
efflWqltBod[0] = Number((calculateTotal(efflWqltBod)/8).toFixed(1));
|
||||
efflWqltCod[0] = Number((calculateTotal(efflWqltCod)/8).toFixed(1));
|
||||
efflWqltSs[0] = Number((calculateTotal(efflWqltSs as number[])/8).toFixed(1));
|
||||
efflWqltTn[0] = Number((calculateTotal(efflWqltTn as number[])/8).toFixed(3));
|
||||
efflWqltTp[0] = Number((calculateTotal(efflWqltTp as number[])/8).toFixed(3));
|
||||
|
||||
// 최종 리스트
|
||||
const filteredData = itemList.map((item, index) => ({
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface FacilityWaterQltyProps {
|
||||
inflAssctAftWqltTp: string | '',
|
||||
inflAssctBfrWqltTp: string | '',
|
||||
efflWqltBod: string | '',
|
||||
efflWqltToc: string | '',
|
||||
efflWqltCod: string | '',
|
||||
efflWqltSs: string | '',
|
||||
efflWqltTn: string | '',
|
||||
efflWqltTp: string | '',
|
||||
@@ -104,7 +104,7 @@ export type modalExcelType = {
|
||||
"유출SS(mg/L)": string;
|
||||
"유출TN(mg/L)": string;
|
||||
"유출TP(mg/L)": string;
|
||||
/* "강우량(mm/일)": string; */
|
||||
"강우량(일)": string;
|
||||
}
|
||||
|
||||
/**====================================
|
||||
@@ -133,7 +133,7 @@ const upHeadList1: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, 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 },
|
||||
@@ -157,7 +157,7 @@ const upHeadList2: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, 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 },
|
||||
@@ -185,7 +185,7 @@ const upHeadList3: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
|
||||
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
|
||||
{ id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, 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 },
|
||||
@@ -203,11 +203,11 @@ const upHeadListModalYear: TableUpperProps[] = [
|
||||
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
|
||||
{ id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" },
|
||||
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" },
|
||||
{ id: "efflWqltToc", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, 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: "강우량(mm/일)", align: "right" }, */
|
||||
{ id: "infoRnDay", title: "강우량", align: "right" },
|
||||
];
|
||||
|
||||
// 처리시설 수질현황 헤더(모달-수질TMS)
|
||||
@@ -409,8 +409,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 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);
|
||||
@@ -427,8 +427,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
@@ -456,7 +456,7 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
유입TN: item.inflAssctAftWqltTn,
|
||||
유입TP: item.inflAssctAftWqltTp,
|
||||
유출BOD: item.efflWqltBod,
|
||||
유출TOC: item.efflWqltToc,
|
||||
유출TOC: item.efflWqltCod,
|
||||
유출SS: item.efflWqltSs,
|
||||
유출TN: item.efflWqltTn,
|
||||
유출TP: item.efflWqltTp,
|
||||
@@ -610,8 +610,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 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);
|
||||
@@ -628,8 +628,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType
|
||||
if(item.inflAssctAftWqltToc){
|
||||
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
|
||||
}
|
||||
if(item.efflWqltToc){
|
||||
item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1);
|
||||
if(item.efflWqltCod){
|
||||
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
|
||||
}
|
||||
|
||||
if(item.tpInflWqltBod){
|
||||
|
||||
@@ -13,8 +13,8 @@ const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용
|
||||
|
||||
export type flowRateListProps = {
|
||||
bscFctCd : string,
|
||||
sido : string,
|
||||
gugun : string,
|
||||
sidoNm : string,
|
||||
sggNm : string,
|
||||
bscFctNm : string,
|
||||
daddr : string,
|
||||
fctCpc : number,
|
||||
@@ -50,13 +50,13 @@ export interface ExcelType {
|
||||
}
|
||||
|
||||
export type modalExcelType = {
|
||||
"날짜": string,
|
||||
날짜: string,
|
||||
"총유입량(m³/일)": number | '',
|
||||
"하수유입량(m³/일)": number | '',
|
||||
"연계처리량(m³/일)": number | '',
|
||||
"총인반류수량(m³/일)": number | '',
|
||||
"방류량(m³/일)": number | '',
|
||||
/* "강우량(mm/일)": number | string | '', */
|
||||
"강우량": number | string | '',
|
||||
}
|
||||
/**
|
||||
* (2) 모달
|
||||
@@ -93,8 +93,8 @@ export type FlowRateModalProps = {
|
||||
const upHeadList1:TableUpperProps[] = [
|
||||
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2},
|
||||
{ id: "A", title: "행정명", row: 0 ,colSpan: 2 },
|
||||
{ id: "sido", title: "시도", upName:"upChangeRe", row:1 },
|
||||
{ id: "gugun", title: "시군구", upName:"upChangeRe", row:1 },
|
||||
{ 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},
|
||||
@@ -131,8 +131,8 @@ const upHeadList3:TableUpperProps[] = [
|
||||
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", row:1 },
|
||||
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
|
||||
{ id: "B", title: "행정명", row: 0 ,colSpan: 2 },
|
||||
{ id: "sido", title: "시도", upName:"upChangeRe2", row:1 },
|
||||
{ id: "gugun", title: "시군구", upName:"upChangeRe2", row:1 },
|
||||
{ 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 },
|
||||
@@ -157,7 +157,7 @@ const upHeadListFlowRateModal:TableUpperProps[] = [
|
||||
{ id: "sumFlow", title: "연계처리량(m³/일)", align:"right"},
|
||||
{ id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"},
|
||||
{ id: "efflAmnt", title: "방류량(m³/일)", align:"right"},
|
||||
/* { id: "infoRnDay", title: "강우량(mm/일)", align:"right"} */
|
||||
{ id: "infoRnDay", title: "강우량", align:"right"}
|
||||
]
|
||||
|
||||
const modalFlowRateListYear:FlowRateModalProps[] = [
|
||||
@@ -268,8 +268,11 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
|
||||
const data = res.data;
|
||||
const excelDownList: ExcelType[] = [];
|
||||
|
||||
const rtnData = data.map((item: flowRateListProps) => {
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
|
||||
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0)
|
||||
dataFormSetting(item);
|
||||
return item;
|
||||
@@ -283,8 +286,8 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
단위유역: item.lgbsn,
|
||||
중권역: item.mdmbsn,
|
||||
환경청: item.rgnNm,
|
||||
시도: item.sido,
|
||||
시군구: item.gugun,
|
||||
시도: item.sidoNm,
|
||||
시군구: item.sggNm,
|
||||
시설이름: item.bscFctNm,
|
||||
주소: item.daddr,
|
||||
용량: item.fctCpc,
|
||||
@@ -297,6 +300,7 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
});
|
||||
}
|
||||
|
||||
console.log('excelDownList' , excelDownList);
|
||||
set((state: FlowRateSearchType) => ({
|
||||
...state,
|
||||
FlowRateList: pageSize<5000?{
|
||||
@@ -312,14 +316,8 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow
|
||||
searchFlowRateModalList: async (bscFctCd: string, searchYear: string) => {
|
||||
const res = await fetchFlowRateModalList(bscFctCd, searchYear);
|
||||
const data = res.data;
|
||||
|
||||
const tmpData:FlowRateModalProps[] = [];
|
||||
|
||||
data.forEach((item: FlowRateModalProps) => {
|
||||
if(item.infoRnDay && item.infoRnDay.toString().trim() === 'x') {
|
||||
item.infoRnDay = '';
|
||||
}
|
||||
});
|
||||
|
||||
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
|
||||
for(let idx = 0; idx < data.length; idx++) {
|
||||
const item = data[idx];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import DataForm from "@/app/components/common/DataForm";
|
||||
import type { TableUpperProps } from "../dashboard";
|
||||
import { getFetch } from "@/app/components/api";
|
||||
export type { TableUpperProps };
|
||||
@@ -28,14 +29,21 @@ export type SewageOccurProps = {
|
||||
mdmbsn : string,
|
||||
msrDt : string,
|
||||
}
|
||||
export type modalTableType = { msrDt: string,waterUsage: number,swageChangePercent: number,estimationSewage: number};
|
||||
|
||||
export type SewageOccurDetailProps = {
|
||||
cnt : number,
|
||||
sum : number,
|
||||
yr:string,
|
||||
usege:number
|
||||
msrDt : string,
|
||||
waterUsage : number,
|
||||
swageChangePercent : number,
|
||||
estimationSewage : number,
|
||||
}
|
||||
|
||||
/**====================================
|
||||
* Store - data
|
||||
====================================*/
|
||||
|
||||
/**
|
||||
* (1) 메인페이지
|
||||
*/
|
||||
// 검색단
|
||||
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
|
||||
|
||||
@@ -112,13 +120,159 @@ const upHeadList3:TableUpperProps[] = [
|
||||
const upHeadListSewageOccurDetail:TableUpperProps[] = [
|
||||
{ id: "msrDt", title: "년월" },
|
||||
{ id: "waterUsage", title: "물사용량(m³/일)" },
|
||||
{ id: "swageChangePercent", title: "오수전환율(%)" },
|
||||
{ id: "swageChangePercent", title: "오수전환율" },
|
||||
{ id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2 },
|
||||
];
|
||||
|
||||
const sewageOccurList:SewageOccurProps[] = []
|
||||
const sewageOccurList:SewageOccurProps[] = [
|
||||
]
|
||||
|
||||
const tempSewageOccurList:modalTableType[] = []
|
||||
const tempSewageOccurList:SewageOccurDetailProps[] = [
|
||||
{
|
||||
msrDt: '2024-01',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 60,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-02',
|
||||
waterUsage: 106,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-03',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-04',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-05',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 10,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-06',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-07',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 580,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-08',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-09',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-10',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-11',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2024-12',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-01',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-02',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-03',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-04',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-05',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-06',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-07',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-08',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 0.8,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-09',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-10',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-11',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
{
|
||||
msrDt: '2025-12',
|
||||
waterUsage: 100,
|
||||
swageChangePercent: 80,
|
||||
estimationSewage: 80,
|
||||
},
|
||||
]
|
||||
|
||||
/**====================================
|
||||
* Store - API
|
||||
@@ -126,6 +280,11 @@ const tempSewageOccurList:modalTableType[] = []
|
||||
|
||||
const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
|
||||
// 20250701임시시
|
||||
console.log("partCd", partCd);
|
||||
console.log("nameCd", nameCd);
|
||||
console.log("fcltCpct", fcltCpct);
|
||||
console.log("operationValue", operationValue);
|
||||
console.log("fcltyNm", fcltyNm);
|
||||
console.log("searchYear", searchYear);
|
||||
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
|
||||
const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
|
||||
@@ -140,18 +299,11 @@ const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear
|
||||
return { data: res.data, count: res.count } */
|
||||
}
|
||||
|
||||
const fetchSewageOccurDetailList = async (bscFctCd: string, searchYear: string) => {
|
||||
const url = '/fclt/flow/' + bscFctCd + '/' + searchYear;
|
||||
const res = await getFetch('fo', url);
|
||||
return { data: res.data, count: res.count }
|
||||
}
|
||||
|
||||
/**====================================
|
||||
* Store - expaort
|
||||
====================================*/
|
||||
export type SewageOccurActions = {
|
||||
searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
|
||||
searchSewageOccurDetailList: (bscFctCd: string, searchYear: string) => void;
|
||||
}
|
||||
|
||||
export type SewageOccurType = {
|
||||
@@ -173,7 +325,7 @@ export type SewageOccurType = {
|
||||
SewageOccurDetail : {
|
||||
isInit: boolean;
|
||||
upHeadListSewageOccurDetail: TableUpperProps[];
|
||||
gridListSewageOccurDetail: modalTableType[];
|
||||
gridListSewageOccurDetail: SewageOccurDetailProps[];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,6 +352,14 @@ export const sewageOccurInitState: SewageOccurType = {
|
||||
}
|
||||
}
|
||||
|
||||
const dataFormSetting=(item : SewageOccurProps)=> {
|
||||
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
|
||||
DataForm({ type: 'float1', column: 'waterUsage', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'estimationSewage', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'sewageInputYear', item: item as unknown as Record<string, unknown>});
|
||||
DataForm({ type: 'float2', column: 'sewageInputChungchun', item: item as unknown as Record<string, unknown>});
|
||||
}
|
||||
|
||||
export type SewageOccurStore = SewageOccurType & SewageOccurActions;
|
||||
|
||||
// Add type definitions for API responses
|
||||
@@ -215,7 +375,9 @@ type FlowResponse = {
|
||||
count: number;
|
||||
}
|
||||
|
||||
export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
|
||||
type SetStateFunction = (state: SewageOccurType | ((state: SewageOccurType) => SewageOccurType)) => void;
|
||||
|
||||
export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set: SetStateFunction) => {
|
||||
return {
|
||||
searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
|
||||
const res = await fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
|
||||
@@ -226,32 +388,67 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
|
||||
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
item.waterUsage = item.fctCpc * 0.6;
|
||||
item.swageChangePercent = 80;
|
||||
|
||||
item.estimationSewage = item.waterUsage * (item.swageChangePercent/100);
|
||||
|
||||
const bscFctCd = item.bscFctCd;
|
||||
const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear;
|
||||
const res2 = await getFetch('fo', url2) as FlowResponse;
|
||||
const sumFlow = res2.data.reduce((acc: number, curr: FlowDataItem) => Number(acc) + Number(curr.inflAmnt), 0);
|
||||
|
||||
let sumChungcunDay = 0;
|
||||
let chungcunSumFlow = 0;
|
||||
let totWaterUsage = 0;
|
||||
|
||||
res2.data.forEach((item: FlowDataItem) => {
|
||||
if(Number(item.infoRnDay) ===0){
|
||||
chungcunSumFlow += Number(item.inflAmnt);
|
||||
sumChungcunDay += 1;
|
||||
}else{
|
||||
console.log(item.bscFctCd +' day : ' +' '+item.msrDt);
|
||||
}
|
||||
totWaterUsage += Number(item.inflAmnt);
|
||||
});
|
||||
const avg = totWaterUsage /res2.count;
|
||||
item.waterUsage = parseFloat((avg * 0.65).toFixed(2));
|
||||
item.sewageInputYear = parseFloat(avg.toFixed(2));
|
||||
item.sewageInputChungchun = parseFloat((chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay)).toFixed(2));
|
||||
item.estimationSewage = parseFloat((item.waterUsage * (item.swageChangePercent/100)).toFixed(2));
|
||||
|
||||
item.sewageInputYear = sumFlow/res2.count;
|
||||
item.sewageInputChungchun = chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay);
|
||||
return item;
|
||||
}));
|
||||
// 조회 데이터 설정
|
||||
set((state: any) => ({
|
||||
|
||||
console.log('calData : ', calData);
|
||||
|
||||
const excelDownList: ExcelType[] = [];
|
||||
if(pageSize>5000){
|
||||
calData.forEach(item => {
|
||||
dataFormSetting(item)
|
||||
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
|
||||
/* item.ctpvNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
|
||||
excelDownList.push({
|
||||
시설코드: item.bscFctCd,
|
||||
권역: item.rvbsn,
|
||||
단위유역: item.lgbsn,
|
||||
중권역: item.mdmbsn,
|
||||
환경청: item.rgnNm,
|
||||
시도: item.sidoNm,
|
||||
시군구: item.sggNm,
|
||||
시설명: item.bscFctNm,
|
||||
주소: item.daddr,
|
||||
시설용량: item.fctCpc,
|
||||
물사용량: item.waterUsage,
|
||||
오수전환율: item.swageChangePercent,
|
||||
오수발생량추정: item.estimationSewage,
|
||||
연간하수유입량: item.sewageInputYear,
|
||||
청천시하수유입량: item.sewageInputChungchun,
|
||||
});
|
||||
});
|
||||
}else{
|
||||
calData.forEach((item: SewageOccurProps) => {
|
||||
dataFormSetting(item)
|
||||
/* item.sidoNmNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
|
||||
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
|
||||
});
|
||||
}
|
||||
set((state: SewageOccurType) => ({
|
||||
...state,
|
||||
SewageOccur: pageSize<5000?{
|
||||
...state.SewageOccur,
|
||||
@@ -259,81 +456,7 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => {
|
||||
sewageOccurCount: res.count,
|
||||
}:{
|
||||
...state.SewageOccur,
|
||||
excelList: [],
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
searchSewageOccurDetailList: async (bscFctCd: string, searchYear: string) => {
|
||||
const res = await fetchSewageOccurDetailList(bscFctCd, searchYear);
|
||||
const monthData: SewageOccurDetailProps[] = [];
|
||||
let tot = 0, cnt= 0
|
||||
res.data.forEach((item: FlowDataItem) => {
|
||||
const month = item.msrDt?.substring(4, 6);
|
||||
const monthIdx = month ? Number(month) - 1 : -1;
|
||||
if (monthIdx >= 0) {
|
||||
if (typeof monthData[monthIdx] === 'object') {
|
||||
monthData[monthIdx].sum += Number(item.inflAmnt);
|
||||
monthData[monthIdx].cnt++;
|
||||
} else {
|
||||
monthData[monthIdx] = {cnt:1, sum:Number(item.inflAmnt), yr:item.msrDt.substring(0,4), usege:0};
|
||||
}
|
||||
}
|
||||
tot += Number(item.inflAmnt)
|
||||
cnt++
|
||||
})
|
||||
console.log('tot', tot, cnt, tot/cnt, tot/cnt*0.65);
|
||||
const tableData:modalTableType[] = []
|
||||
|
||||
let maxWaterUsage = 0;
|
||||
let minWaterUsage = 9999999999;
|
||||
let avgWaterUsage = 0;
|
||||
monthData.forEach((item, index) => {
|
||||
item.usege = item.sum/item.cnt *0.65
|
||||
tableData.push({
|
||||
msrDt: item.yr+'-'+(index+1>10?String(index+1):'0'+(index+1)),
|
||||
waterUsage: Math.round(item.usege*100) /100,
|
||||
swageChangePercent: Math.round((item.usege/item.usege*0.8) * 100) / 100,
|
||||
estimationSewage: Math.round(item.usege*0.8 * 100) / 100
|
||||
})
|
||||
if (item.usege > maxWaterUsage) {
|
||||
maxWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
if (item.usege < minWaterUsage) {
|
||||
minWaterUsage = item.usege;
|
||||
}
|
||||
|
||||
});
|
||||
avgWaterUsage = tot/cnt*0.65
|
||||
|
||||
const newData= [
|
||||
{
|
||||
msrDt: '평균',
|
||||
waterUsage: Math.round(avgWaterUsage*100) /100,
|
||||
swageChangePercent: 0.8,
|
||||
estimationSewage: Math.round(avgWaterUsage*0.8*100) /100,
|
||||
},
|
||||
{
|
||||
msrDt: '최대',
|
||||
waterUsage: Math.round(maxWaterUsage*100) /100,
|
||||
swageChangePercent: 0.8,
|
||||
estimationSewage: Math.round(maxWaterUsage*0.8*100) /100,
|
||||
},
|
||||
{
|
||||
msrDt: '최소',
|
||||
waterUsage: Math.round(minWaterUsage*100) /100,
|
||||
swageChangePercent: 0.8,
|
||||
estimationSewage: Math.round(minWaterUsage*0.8*100) /100,
|
||||
},
|
||||
...tableData
|
||||
]
|
||||
|
||||
set((state: SewageOccurType) => ({
|
||||
...state,
|
||||
SewageOccurDetail: {
|
||||
...state.SewageOccurDetail,
|
||||
gridListSewageOccurDetail: newData,
|
||||
excelList: excelDownList,
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
+12
-14
@@ -1,19 +1,13 @@
|
||||
import { LoginApi } from '@/src/entities/auth/api/login-service';
|
||||
import { createStore } from 'zustand/vanilla';
|
||||
import { CommonReducer, commonState, type CommonStore, type CommonType } from './common';
|
||||
import { dashBoardInitState, dashBoardReducer, type DashBoardStore, type DashBoardType } from './dashboard';
|
||||
import { dashBoardReducer, dashBoardInitState, type DashBoardStore, type DashBoardType } from './dashboard';
|
||||
import {
|
||||
facilityPrecipitationInitState,
|
||||
facilityPrecipitationReducer,
|
||||
type FacilityPrecipitationStore, type FacilityPrecipitationType
|
||||
} from './facilityPrecipitation';
|
||||
import {
|
||||
facilityWaterQltyInitState,
|
||||
facilityWaterQltyReducer,
|
||||
type FacilityWaterQltyStore, type FacilityWaterQltyType
|
||||
} from './facilityWaterQlty';
|
||||
import { flowRateSearchInitState, flowRateSearchReducer, type FlowRateSearchStore, type FlowRateSearchType } from './flowRate';
|
||||
import { planSewageInitState, planSewageReducer, type PlanSewageStore, type PlanSewageType } from './planSewage';
|
||||
import { flowRateSearchInitState, flowRateSearchReducer, type FlowRateSearchStore, type FlowRateSearchType } from './flowRate';
|
||||
import {
|
||||
processAreaInitState,
|
||||
processAreaReducer,
|
||||
@@ -24,12 +18,11 @@ import {
|
||||
processFacilityReducer,
|
||||
type ProcessFacilityStore, type ProcessFacilityType
|
||||
} from './processFacility';
|
||||
import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';
|
||||
import {
|
||||
rdiInitState,
|
||||
rdiReducer,
|
||||
type RdiStore, type RdiType
|
||||
} from './rdi';
|
||||
facilityWaterQltyInitState,
|
||||
facilityWaterQltyReducer,
|
||||
type FacilityWaterQltyStore, type FacilityWaterQltyType
|
||||
} from './facilityWaterQlty';
|
||||
import {
|
||||
sewageOccurInitState,
|
||||
sewageOccurReducer,
|
||||
@@ -40,6 +33,12 @@ import {
|
||||
underWaterReducer,
|
||||
type UnderWaterStore, type UnderWaterType
|
||||
} from './underWaterInput';
|
||||
import {
|
||||
rdiInitState,
|
||||
rdiReducer,
|
||||
type RdiStore, type RdiType
|
||||
} from './rdi';
|
||||
import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';
|
||||
export type FoStore = CommonStore & tableType & DashBoardStore & ProcessFacilityStore & ProcessAreaStore & FlowRateSearchStore & FacilityPrecipitationStore & FacilityWaterQltyStore & PlanSewageStore & SewageOccurStore & UnderWaterStore & RdiStore;
|
||||
export type FoState = CommonType & tableState & ProcessFacilityType & ProcessAreaType & FacilityPrecipitationType & FlowRateSearchType & DashBoardType & FacilityWaterQltyType & PlanSewageType & SewageOccurType & UnderWaterType & RdiType;
|
||||
|
||||
@@ -59,7 +58,6 @@ export const StoreZus =
|
||||
}
|
||||
) => {
|
||||
return createStore<FoStore>()((set) => {
|
||||
LoginApi.login({userId: "admin1gmail", password: "test1234!"})
|
||||
return {
|
||||
...initState,
|
||||
...CommonReducer(set),
|
||||
|
||||
@@ -11,10 +11,6 @@ const compat = new FlatCompat({
|
||||
|
||||
const eslintConfig = [
|
||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
||||
{rules:{
|
||||
// other rules..
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
}},
|
||||
];
|
||||
|
||||
export default eslintConfig;
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -20,7 +20,7 @@ export class TokenManager {
|
||||
|
||||
public setToken(key: string, value: string): void {
|
||||
setCookie(key, value, {
|
||||
secure: location.protocol === "https:",
|
||||
secure: window.location.protocol === "https:",
|
||||
sameSite: "lax",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
NEXT_PUBLIC_SITE_ID = SN
|
||||
NEXT_PUBLIC_PORT = 11302
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11302
|
||||
NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver_swl
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
|
||||
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_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = https://sn.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_ADDR_URL = http://con.mind-one.co.kr:11082
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api
|
||||
|
||||
@@ -376,7 +376,7 @@ export const MsSwageMhlCol = [
|
||||
{ id: 'vrtc_len', title: '세로길이(m)' },
|
||||
// { id: 'ang_dir', title: '표고' },
|
||||
{ id: 'altd_vl', title: '표고(EL.m)' },
|
||||
{ id: 'dpthlVl', title: '심도(m)' },
|
||||
{ id: 'dpthl_vl', title: '심도(m)' },
|
||||
// { id: 'inst_mng_no', title: '기관관리번호' }
|
||||
];
|
||||
export const MsSwageMhlCol2 = [
|
||||
@@ -507,7 +507,7 @@ export const ExclnSewagePumpCol2 = [
|
||||
// { id: 'instMngNo', title: '기관관리번호' },
|
||||
]
|
||||
// 오수펌프장
|
||||
export const DrtwSewagePumpCol1 = [
|
||||
export const DrtwSewagePumpCol = [
|
||||
// { id: 'fid', title: 'FID' },
|
||||
// { id: 'geom', title: 'geometry' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
@@ -574,16 +574,16 @@ export const stofcbrCol = [
|
||||
{ id: 'drtn_angl', title: '방향각도(°)' },
|
||||
]
|
||||
export const stofcbrCol2 = [
|
||||
// { id: 'ftre_smbol_cd', title: '지형지물부호코드' },
|
||||
{ id: 'pbadmsEmdCd', title: '시설관리번호' },
|
||||
// { id: 'pbadms_emd_cd', title: '행정읍면동코드' },
|
||||
// { id: 'ftreSmbolCd', title: '지형지물부호코드' },
|
||||
{ id: 'fcltMngNo', title: '시설관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mng_inst_cd', title: '관리기관코드' },
|
||||
// { id: 'mngInstCd', title: '관리기관코드' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
// { id: 'fclty_shp_cd', title: '시설물형태코드' },
|
||||
// { id: 'fcltyShpCd', title: '시설물형태코드' },
|
||||
{ id: 'worflwWallHgt', title: '월류벽높이(m)' },
|
||||
// { id: 'mhl_exst_yn_cd', title: '맨홀존재여부코드' },
|
||||
// { id: 'swage_mhl_ftre_smbol_cd', title: '하수맨홀지형지물부호코드' },
|
||||
// { id: 'mhlExstYnCd', title: '맨홀존재여부코드' },
|
||||
// { id: 'swageMhlFtreSmbolCd', title: '하수맨홀지형지물부호코드' },
|
||||
{ id: 'swageMhlMngNo', title: '하수맨홀관리번호' },
|
||||
{ id: 'drtnAngl', title: '방향각도(°)' },
|
||||
]
|
||||
|
||||
@@ -39,14 +39,13 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
|
||||
{ lwpoCd: "203", lwpoCdNm: "청소, 준설" },
|
||||
{ lwpoCd: "204", lwpoCdNm: "단순보수" },
|
||||
];
|
||||
// const BIZ_TY_CD = useBizByFcltStore(state => state.BIZ_TY_CD);
|
||||
const BIZ_TY_CD = [
|
||||
{ lwpoCd: "SV", lwpoCdNm: "용역" },
|
||||
{ lwpoCd: "SF", lwpoCdNm: "자체" },
|
||||
{ lwpoCd: "SC", lwpoCdNm: "공사" },
|
||||
];
|
||||
|
||||
console.log('BIZ_SE_CD', BIZ_SE_CD);
|
||||
|
||||
// 마운트 및 업데이트 시 API 호출
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useGisContext } from "@common/business_components/ui/map/Gis";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useEffect, useRef, useState, useCallback, useMemo } from "react";
|
||||
import { getLayerContentComponent } from "./components/common";
|
||||
import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol1, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
|
||||
import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
|
||||
import InfilChart from './components/popupComponent/chart/infilChart';
|
||||
import RdiiChart from './components/popupComponent/chart/rdiiChart';
|
||||
import { useInfilChartModalStore, useRdiiChartModalStore } from "./components/popupComponent/chartModalStore";
|
||||
@@ -14,7 +14,6 @@ import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
|
||||
import { codeMapper } from "./components/common/codeMapper";
|
||||
|
||||
|
||||
// TODO:
|
||||
const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => {
|
||||
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
|
||||
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
|
||||
@@ -59,59 +58,124 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
let result = null;
|
||||
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
||||
let featureLayer: { get: (key: string) => any }; // 피처가 속한 레이어를 찾기 위한 변수
|
||||
layers.forEach((layer: any) => {
|
||||
layers.some((layer: any) => {
|
||||
const source = layer.getSource();
|
||||
// console.log('실행됨3 source', source);
|
||||
// console.log('실행됨3 featureData', featureData);
|
||||
// console.log('실행됨3 featureData.fid', featureData.fid);
|
||||
// if (source && featureData && featureData.fid) {
|
||||
// featureLayer = layer; // 피처가 속한 레이어 찾기
|
||||
// }
|
||||
if (source && source.hasFeature && featureData
|
||||
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
|
||||
featureLayer = layer; // 피처가 속한 레이어 찾기
|
||||
return true; // 반복 종료
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// console.log("featureLayer.get('lyrTyNm');", featureLayer.get('lyrTyNm'));
|
||||
// SearchModal 클릭 이벤트
|
||||
if (featureLayer.get('lyrNm') === null) {
|
||||
setIsClicked(false);
|
||||
if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_stofcbr') result = '우수토실';
|
||||
else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_ppl') result = '하수관로';
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_pmstn') {
|
||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||
console.log('값 비교', featureData.values_.usg_cd.trim(), drtwPumpCode?.val);
|
||||
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
|
||||
setIsDrtwSewagePump(false);
|
||||
} else {
|
||||
setIsDrtwSewagePump(true);
|
||||
}
|
||||
result = '하수펌프장';
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_mhl') result = '하수맨홀';
|
||||
}
|
||||
else {
|
||||
|
||||
if (featureData['ol_uid']) {
|
||||
// Gis 레이어 클릭 이벤트
|
||||
setIsClicked(true);
|
||||
const lyrTyNm = featureLayer.get('lyrTyNm');
|
||||
if (lyrTyNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설';
|
||||
else if (lyrTyNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역';
|
||||
else if (lyrTyNm === 'tb_ms_stofcbr') result = '우수토실';
|
||||
else if (lyrTyNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
||||
else if (lyrTyNm === 'tb_ms_swage_ppl') result = '하수관로';
|
||||
else if (lyrTyNm === 'tb_ms_swage_pmstn') {
|
||||
console.log('gis data', featureData.values_);
|
||||
const lyrTyNm = featureData.id_.split('.')[0];
|
||||
if (lyrTyNm === 'tb_ms_swage_ppl') {
|
||||
result = '하수관로';
|
||||
setHeadlist(MsSwagePplCol);
|
||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
}
|
||||
else if (lyrTyNm === 'tb_ms_swage_mhl') {
|
||||
result = '하수맨홀';
|
||||
setHeadlist(MsSwageMhlCol);
|
||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
}
|
||||
else if (lyrTyNm === 'tb_ms_swage_pmstn') { // 하수펌프장
|
||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||
console.log('값 비교', featureData.values_.usg_cd.trim(), drtwPumpCode?.val);
|
||||
if (drtwPumpCode && featureData.values_.usg_cd.trim() === drtwPumpCode.val) {
|
||||
setIsDrtwSewagePump(false);
|
||||
} else {
|
||||
setIsDrtwSewagePump(true);
|
||||
setHeadlist(ExclnSewagePumpCol);
|
||||
}
|
||||
else {
|
||||
setIsDrtwSewagePump(true);
|
||||
setHeadlist(DrtwSewagePumpCol);
|
||||
}
|
||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
result = '하수펌프장';
|
||||
}
|
||||
else if (lyrTyNm === 'tb_ms_swage_mhl') result = '하수맨홀';
|
||||
else if (lyrTyNm === 'tb_il_swage_prcs_fclt') {
|
||||
result = '하수처리시설';
|
||||
setHeadlist(SwtrmtFcltCol);
|
||||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||||
}
|
||||
else if (lyrTyNm === 'tb_ms_swage_prcs_zone') {
|
||||
result = '하수처리구역';
|
||||
setHeadlist(SwtrmtPrcsZoneCol);
|
||||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||||
}
|
||||
else if (lyrTyNm === 'tb_ms_stofcbr') {
|
||||
result = '우수토실';
|
||||
setHeadlist(stofcbrCol);
|
||||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
}
|
||||
else if (lyrTyNm === 'om_dt_untrt_swg_rain') {
|
||||
result = '중점우수토실';
|
||||
setHeadlist(untrtSwgRainCol1);
|
||||
setHeadlist2(untrtSwgRainCol2);
|
||||
setLayerSpecificNm(featureData.values_.manage_no);
|
||||
}
|
||||
else result = featureLayer.get('lyrNm');
|
||||
}
|
||||
else {
|
||||
// SearchModal 클릭 이벤트
|
||||
setIsClicked(false);
|
||||
console.log('SearchModal data', featureData);
|
||||
const lyrTyNm = featureData.lyrTypeNm;
|
||||
console.log('실행됨4 SearchModal 클릭 이벤트', lyrTyNm);
|
||||
console.log('실행됨4 SearchModal 클릭 이벤트 featureData', featureData);
|
||||
if (featureData.lyrTypeNm === 'tb_ms_swage_ppl') {
|
||||
result = '하수관로';
|
||||
setHeadlist(MsSwagePplCol2)
|
||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_mhl') {
|
||||
result = '하수맨홀';
|
||||
setHeadlist(MsSwageMhlCol2)
|
||||
setLayerSpecificNm(featureData.fcltMngNo);
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_pmstn') { // 하수펌프장
|
||||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(code => code.text === '오수펌프장');
|
||||
if (drtwPumpCode && featureData.usgCd.trim() === drtwPumpCode.val) {
|
||||
console.log('오수펌프장');
|
||||
setIsDrtwSewagePump(false);
|
||||
setHeadlist(ExclnSewagePumpCol2)
|
||||
}
|
||||
else {
|
||||
console.log('빗물펌프장');
|
||||
setIsDrtwSewagePump(true);
|
||||
setHeadlist(DrtwSewagePumpCol2)
|
||||
}
|
||||
setLayerSpecificNm(featureData.fcltMngNo);
|
||||
result = '하수펌프장';
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') {
|
||||
result = '하수처리시설';
|
||||
setHeadlist(SwtrmtFcltCol2)
|
||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') {
|
||||
result = '하수처리구역';
|
||||
setHeadlist(SwtrmtPrcsZoneCol2);
|
||||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||||
}
|
||||
else if (featureData.lyrTypeNm === 'tb_ms_stofcbr') {
|
||||
result = '우수토실';
|
||||
setHeadlist(stofcbrCol2)
|
||||
setLayerSpecificNm(featureData.fcltMngNo);
|
||||
}
|
||||
// else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
||||
}
|
||||
|
||||
return result;
|
||||
// return featureLayer.get('lyrNm'); // 레이어 이름 출력
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@@ -122,8 +186,10 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
|
||||
setLayerNm(selectedLayerName); // 상태 업데이트
|
||||
|
||||
console.log('gis data', featureData.values_);
|
||||
const data = featureData.values_;
|
||||
// console.log('gis data', featureData.values_);
|
||||
let data;
|
||||
if (featureData['ol_uid']) data = featureData.values_; // Gis 레이어 클릭 이벤트일 때
|
||||
else data = featureData; // SearchModal 클릭 이벤트일 때
|
||||
|
||||
// 하수맨홀인 경우 용도코드를 텍스트로 변환
|
||||
if (selectedLayerName === '하수맨홀' && data) {
|
||||
@@ -139,16 +205,11 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
// 🎯 기본 데이터 포맷팅
|
||||
const formattedData = formatDisplayData(data);
|
||||
|
||||
if (featureData?.values_?.fclt_mng_no) setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||||
else if (featureData?.values_?.swtrmt_fclt_cd) setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||||
else setLayerSpecificNm(featureData?.values_?.manage_no);
|
||||
setFetchData(formattedData);
|
||||
setTableData(formattedData);
|
||||
|
||||
switch (selectedLayerName) {
|
||||
case '하수관로':
|
||||
if (isClicked) setHeadlist(MsSwagePplCol)
|
||||
else setHeadlist(MsSwagePplCol2)
|
||||
setButtonChk(true)
|
||||
|
||||
// ✅ codeMapper 사용하여 API 결과도 변환
|
||||
@@ -156,12 +217,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
layerName: selectedLayerName,
|
||||
data: { ...formattedData }
|
||||
}));
|
||||
// setFetchData(mappedData);
|
||||
// setTableData(mappedData);
|
||||
break;
|
||||
case '하수맨홀':
|
||||
if (isClicked) setHeadlist(MsSwageMhlCol)
|
||||
else setHeadlist(MsSwageMhlCol2)
|
||||
setButtonChk(true)
|
||||
|
||||
// ✅ codeMapper 사용하여 API 결과도 변환
|
||||
@@ -169,46 +226,26 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
|
||||
layerName: selectedLayerName,
|
||||
data: { ...formattedData }
|
||||
}));
|
||||
// setFetchData(mappedData);
|
||||
// setTableData(mappedData);
|
||||
break;
|
||||
case '하수펌프장':
|
||||
if (isDrtwSewagePump) {
|
||||
if (isClicked) setHeadlist(DrtwSewagePumpCol1)
|
||||
else setHeadlist(DrtwSewagePumpCol2)
|
||||
} else {
|
||||
if (isClicked) setHeadlist(ExclnSewagePumpCol)
|
||||
else setHeadlist(ExclnSewagePumpCol2)
|
||||
}
|
||||
|
||||
setButtonChk(true)
|
||||
|
||||
setTableData(codeMapper({
|
||||
layerName: selectedLayerName,
|
||||
data: { ...formattedData }
|
||||
}));
|
||||
// setFetchData(mappedData);
|
||||
// setTableData(mappedData);
|
||||
break;
|
||||
case '하수처리시설':
|
||||
if (isClicked) setHeadlist(SwtrmtFcltCol)
|
||||
else setHeadlist(SwtrmtFcltCol2)
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '하수처리구역':
|
||||
if (isClicked) setHeadlist(SwtrmtPrcsZoneCol)
|
||||
else setHeadlist(SwtrmtPrcsZoneCol2)
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '우수토실':
|
||||
if (isClicked) setHeadlist(stofcbrCol)
|
||||
else setHeadlist(stofcbrCol2)
|
||||
setButtonChk(true)
|
||||
setIsMainSsc(false)
|
||||
break;
|
||||
case '중점우수토실':
|
||||
setHeadlist(untrtSwgRainCol1)
|
||||
setHeadlist2(untrtSwgRainCol2)
|
||||
setButtonChk(false)
|
||||
setIsMainSsc(true)
|
||||
// 🎯 processedData도 포맷팅하여 설정
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
import React from 'react';
|
||||
import { MS_SWAGE_MHL_USE_CODES, MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from '../components/common/table';
|
||||
import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import SwagePpl from './swagePpl';
|
||||
import SwagePrcsFclt from './swagePrcsFclt';
|
||||
import SwagePrcsZone from './swagePrcsZone';
|
||||
import SwageMhl from './swageMhl';
|
||||
import SwagePmstn from './swagePmstn';
|
||||
|
||||
// 기본정보 '구분' 선택 목록
|
||||
export const BASIC_TYPES = [
|
||||
{ val: "tb_ms_swage_ppl", text: "하수관로" },
|
||||
{ val: "tb_il_swage_prcs_fclt", text: "하수처리시설" },
|
||||
{ val: "tb_ms_swage_prcs_zone", text: "하수처리구역" },
|
||||
{ val: "tb_ms_swage_mhl", text: "하수맨홀" },
|
||||
{ val: "tb_ms_swage_pmstn", text: "하수펌프장" },
|
||||
{ val: "tb_ms_stofcbr", text: "우수토실" },
|
||||
];
|
||||
|
||||
interface BasicTabProps {
|
||||
open?: boolean;
|
||||
basicType: string;
|
||||
city: number;
|
||||
sgg: number;
|
||||
start1: string;
|
||||
start2: string;
|
||||
end1: string;
|
||||
end2: string;
|
||||
var1: string;
|
||||
var2: string;
|
||||
yearFilter: { start: string; end: string; };
|
||||
handleSearch: () => void;
|
||||
// onOpenChange?: (open: boolean) => void;
|
||||
// onFeatureSelect?: (row: any) => void;
|
||||
// setTableData?: (data: any[]) => void;
|
||||
// setTableCount?: (count: number) => void;
|
||||
setBasicType: (type: string) => void;
|
||||
setCity: (city: number) => void;
|
||||
setSgg: (sgg: number) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setStart2: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setEnd2: (value: string) => void;
|
||||
setVar1: (value: string) => void;
|
||||
setVar2: (value: string) => void;
|
||||
}
|
||||
|
||||
const BasicTab: React.FC<BasicTabProps> = ({
|
||||
// open,
|
||||
basicType,
|
||||
city,
|
||||
sgg,
|
||||
start1,
|
||||
start2,
|
||||
end1,
|
||||
end2,
|
||||
var1,
|
||||
var2,
|
||||
yearFilter,
|
||||
handleSearch,
|
||||
// onOpenChange,
|
||||
// onFeatureSelect,
|
||||
// setTableData,
|
||||
// setTableCount,
|
||||
setBasicType,
|
||||
setCity,
|
||||
setSgg,
|
||||
setYearFilter,
|
||||
setStart1,
|
||||
setStart2,
|
||||
setEnd1,
|
||||
setEnd2,
|
||||
setVar1,
|
||||
setVar2,
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
{/* 구분 */}
|
||||
<div className="mb-3 flex items-center justify-between gap-4">
|
||||
<div className="flex items-center">
|
||||
<SelectBox
|
||||
className="w-[150px] mr-3"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="구분"
|
||||
selectArray={BASIC_TYPES}
|
||||
onSelectValue={(value: string | number) =>
|
||||
setBasicType(String(value))
|
||||
}
|
||||
value={basicType}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 분기처리: 하수관로 */}
|
||||
{basicType === "tb_ms_swage_ppl" && (
|
||||
<SwagePpl
|
||||
var1={var1}
|
||||
var2={var2}
|
||||
start1={start1}
|
||||
end1={end1}
|
||||
yearFilter={yearFilter}
|
||||
setVar1={setVar1}
|
||||
setVar2={setVar2}
|
||||
setStart1={setStart1}
|
||||
setEnd1={setEnd1}
|
||||
setYearFilter={setYearFilter}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
{/* 분기처리: 하수처리시설 */}
|
||||
{basicType === "tb_il_swage_prcs_fclt" && (
|
||||
<SwagePrcsFclt
|
||||
var1={var1}
|
||||
start1={start1}
|
||||
start2={start2}
|
||||
end1={end1}
|
||||
end2={end2}
|
||||
yearFilter={yearFilter}
|
||||
setVar1={setVar1}
|
||||
setStart1={setStart1}
|
||||
setStart2={setStart2}
|
||||
setEnd1={setEnd1}
|
||||
setEnd2={setEnd2}
|
||||
setYearFilter={setYearFilter}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
{/* 분기처리: 하수처리구역 */}
|
||||
{basicType === "tb_ms_swage_prcs_zone" && (
|
||||
<SwagePrcsZone
|
||||
var1={var1}
|
||||
var2={var2}
|
||||
city={city}
|
||||
sgg={sgg}
|
||||
setVar1={setVar1}
|
||||
setVar2={setVar2}
|
||||
setCity={setCity}
|
||||
setSgg={setSgg}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
{/* 분기처리: 하수맨홀 */}
|
||||
{basicType === "tb_ms_swage_mhl" && (
|
||||
<SwageMhl
|
||||
var1={var1}
|
||||
start1={start1}
|
||||
end1={end1}
|
||||
yearFilter={yearFilter}
|
||||
setVar1={setVar1}
|
||||
setStart1={setStart1}
|
||||
setEnd1={setEnd1}
|
||||
setYearFilter={setYearFilter}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
{/* 분기처리: 하수펌프장 */}
|
||||
{basicType === "tb_ms_swage_pmstn" && (
|
||||
<SwagePmstn
|
||||
start1={start1}
|
||||
start2={start2}
|
||||
end1={end1}
|
||||
end2={end2}
|
||||
yearFilter={yearFilter}
|
||||
setStart1={setStart1}
|
||||
setStart2={setStart2}
|
||||
setEnd1={setEnd1}
|
||||
setEnd2={setEnd2}
|
||||
setYearFilter={setYearFilter}
|
||||
handleSearch={handleSearch}
|
||||
/>
|
||||
)}
|
||||
{/* 분기처리: 우수토실 */}
|
||||
{basicType === "tb_ms_stofcbr" && (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(BasicTab);
|
||||
@@ -0,0 +1,162 @@
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
// import { useBizByFcltStore } from "../../../store/StoreProvider";
|
||||
|
||||
// 운영정보 '구분' 선택 목록
|
||||
// const WORK_TYPES = [
|
||||
// { val: "maintenance", text: "보수 및 갱신" },
|
||||
// { val: "cleaning", text: "청소 및 준설" },
|
||||
// { val: "inspection", text: "점검 및 진단" },
|
||||
// ];
|
||||
export const WORK_TYPES = [
|
||||
{ val: "200", text: "보수 및 갱신(개대체)" },
|
||||
{ val: "201", text: "보수 및 갱신(유지보수)" },
|
||||
{ val: "204", text: "보수 및 갱신(단순보수)" },
|
||||
{ val: "100", text: "점검 및 진단" },
|
||||
{ val: "203", text: "청소 및 준설" },
|
||||
];
|
||||
// const BIZ_SE_CD = useBizByFcltStore(state => state.BIZ_SE_CD);
|
||||
export const BIZ_SE_CD = [
|
||||
{ lwpoCd: "100", lwpoCdNm: "관로기술진단" },
|
||||
{ lwpoCd: "200", lwpoCdNm: "개대체" },
|
||||
{ lwpoCd: "201", lwpoCdNm: "유지보수" },
|
||||
{ lwpoCd: "203", lwpoCdNm: "청소, 준설" },
|
||||
{ lwpoCd: "204", lwpoCdNm: "단순보수" },
|
||||
];
|
||||
// const BIZ_TY_CD = useBizByFcltStore(state => state.BIZ_TY_CD);
|
||||
export const BIZ_TY_CD = [
|
||||
{ lwpoCd: "0", lwpoCdNm: "전체" },
|
||||
{ lwpoCd: "SV", lwpoCdNm: "용역" },
|
||||
{ lwpoCd: "SF", lwpoCdNm: "자체" },
|
||||
{ lwpoCd: "SC", lwpoCdNm: "공사" },
|
||||
];
|
||||
export const BIZ_TY_CD_SELECT_ARRAY = BIZ_TY_CD.map(item => ({
|
||||
val: item.lwpoCd,
|
||||
text: item.lwpoCdNm,
|
||||
}));
|
||||
// 운영정보 - 사업 현황
|
||||
export const operationTableHeadlist = [
|
||||
{ id: "bizNm", title: "사업명" },
|
||||
{ id: "flfmtBgngYmd", title: "수행시작일자" },
|
||||
{ id: "flfmtEndYmd", title: "수행종료일자" },
|
||||
{ id: "flfmtYr", title: "수행년도" },
|
||||
{ id: "bizTyCd", title: "사업유형" },
|
||||
{ id: "bizAmt", title: "사업금액" },
|
||||
];
|
||||
// 운영정보 - 보수 및 갱신 결과표
|
||||
export const operationMaintenanceTableHeadlist = [
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
{ id: "pdmt", title: "관경(mm)" },
|
||||
{ id: "prlg", title: "연장(m)" },
|
||||
{ id: "instlYmd", title: "설치일자" },
|
||||
// { id: "instlYmd", title: "사업구분" },
|
||||
];
|
||||
// 운영정보 - 청소 및 준설 결과표
|
||||
export const operationCleaningTableHeadlist = [
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
{ id: "pdmt", title: "관경(mm)" },
|
||||
{ id: "prlg", title: "연장(m)" },
|
||||
{ id: "instlYmd", title: "설치일자" },
|
||||
];
|
||||
// 운영정보 - 점검 및 진단 결과표
|
||||
export const operationInspectionTableHeadlist = [
|
||||
{ id: "fcltMngNo", title: "관리번호" },
|
||||
{ id: "usgCd", title: "용도" },
|
||||
{ id: "mqltCd", title: "재질" },
|
||||
{ id: "pdmt", title: "관경(mm)" },
|
||||
{ id: "prlg", title: "연장(m)" },
|
||||
{ id: "instlYmd", title: "설치일자" },
|
||||
{ id: "strct_stat_grd", title: "구조적 상태등급" },
|
||||
{ id: "oper_stat_grd", title: "운영적 상태등급" },
|
||||
];
|
||||
|
||||
interface OperationTabProps {
|
||||
open: boolean;
|
||||
workType: string;
|
||||
var1: string;
|
||||
var2: string;
|
||||
yearFilter: { start: string; end: string; };
|
||||
handleSearch: () => void;
|
||||
// onOpenChange: (open: boolean) => void;
|
||||
// setTableData: (data: any[]) => void;
|
||||
// setTableCount: (count: number) => void;
|
||||
setWorkType: (type: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
setVar1: (value: string) => void;
|
||||
setVar2: (value: string) => void;
|
||||
}
|
||||
|
||||
const OperationTab: React.FC<OperationTabProps> = ({
|
||||
open,
|
||||
workType,
|
||||
var1,
|
||||
var2,
|
||||
yearFilter,
|
||||
handleSearch,
|
||||
// onOpenChange,
|
||||
// setTableData,
|
||||
// setTableCount,
|
||||
setWorkType,
|
||||
setVar1,
|
||||
setVar2,
|
||||
setYearFilter,
|
||||
}) => {
|
||||
return (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<SelectBox
|
||||
className="w-80 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label="업무구분"
|
||||
selectArray={WORK_TYPES.map((w) => ({
|
||||
val: w.val,
|
||||
text: w.text,
|
||||
}))}
|
||||
onSelectValue={(val) => setWorkType(String(val))}
|
||||
value={workType}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-6">
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">사업명</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center mr-4"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => { setVar1(e.target.value); }}
|
||||
/>
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
||||
startLabel="수행기간"
|
||||
endLabel=""
|
||||
/>
|
||||
<SelectBox
|
||||
className="ml-8 w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label="사업 유형"
|
||||
selectArray={BIZ_TY_CD_SELECT_ARRAY}
|
||||
value={var2}
|
||||
onSelectValue={(val) => {
|
||||
setVar2(String(val));
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
export default React.memo(OperationTab);
|
||||
@@ -0,0 +1,109 @@
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input } from '@common/components';
|
||||
import React from 'react';
|
||||
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
import { type SelectDataType } from '../commonStore';
|
||||
import { useCityCd, useSggCd } from '../components/searchModalReportApi';
|
||||
// import { useBizByFcltStore } from "../../../store/StoreProvider";
|
||||
|
||||
|
||||
// // 하수도정비 기본계획 보고서
|
||||
export const reportTableHeadlist = [
|
||||
{ id: "no", title: "NO" },
|
||||
{ id: "sido", title: "시도" },
|
||||
{ id: "sigungu", title: "시군구" },
|
||||
{ id: "approvalYear", title: "승인년도" },
|
||||
{ id: "plantZoneGugun", title: "구분" },
|
||||
{ id: "reportGubun", title: "보고서구분" },
|
||||
{ id: "fileName", title: "파일명" },
|
||||
{ id: "regDate", title: "등록일자" },
|
||||
];
|
||||
|
||||
|
||||
interface BasicTabProps {
|
||||
open?: boolean;
|
||||
gubun: string;
|
||||
sidos: SelectDataType[];
|
||||
ctpvCd: string | number;
|
||||
sigungu: string;
|
||||
filteredSiguns: SelectDataType[];
|
||||
yearFilter: { start: string; end: string; };
|
||||
handleSearch: () => void;
|
||||
handleCtpvCdChange: (val: string | number) => void;
|
||||
// onOpenChange?: (open: boolean) => void;
|
||||
// onFeatureSelect?: (row: any) => void;
|
||||
// setTableData?: (data: any[]) => void;
|
||||
// setTableCount?: (count: number) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
setGubun: (gubun: string) => void;
|
||||
setSigungu: (sigungu: string) => void;
|
||||
}
|
||||
|
||||
const ReportTab: React.FC<BasicTabProps> = ({
|
||||
open,
|
||||
gubun,
|
||||
sidos,
|
||||
ctpvCd,
|
||||
sigungu,
|
||||
filteredSiguns = [{ val: '0', text: '전체', sido: '0' }],
|
||||
yearFilter,
|
||||
handleSearch,
|
||||
handleCtpvCdChange,
|
||||
// onOpenChange,
|
||||
// onFeatureSelect,
|
||||
// setTableData,
|
||||
// setTableCount,
|
||||
setYearFilter,
|
||||
setGubun,
|
||||
setSigungu,
|
||||
}) => {
|
||||
|
||||
|
||||
return (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[10vh]">
|
||||
<div className="flex items-center mb-3">
|
||||
<SelectBox
|
||||
className="w-[150px] mr-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시도"
|
||||
selectArray={sidos}
|
||||
value={ctpvCd}
|
||||
onSelectValue={handleCtpvCdChange}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
<SelectBox
|
||||
className="w-48"
|
||||
selectClass="w-48 rounded-lg bg-white truncate"
|
||||
label="시군구"
|
||||
selectArray={filteredSiguns}
|
||||
value={sigungu}
|
||||
onSelectValue={val => setSigungu(String(val))}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-6">
|
||||
<label className="inline-block w-auto whitespace-nowrap text-xl text-gray-600 mr-2">구분</label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={gubun}
|
||||
onChange={(e) => { setGubun(e.target.value); }}
|
||||
/>
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
||||
startLabel="승인년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ReportTab);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
// 우수토실
|
||||
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { Button } from '@common/components';
|
||||
import React from 'react';
|
||||
|
||||
interface StofcbrProps {
|
||||
yearFilter: { start: string; end: string };
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const Stofcbr: React.FC<StofcbrProps> = ({
|
||||
yearFilter,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Stofcbr);
|
||||
@@ -0,0 +1,94 @@
|
||||
// 하수맨홀
|
||||
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
import { MS_SWAGE_MHL_USE_CODES } from '../components/common/table';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
|
||||
|
||||
interface SwageMhlProps {
|
||||
var1: string;
|
||||
start1: string;
|
||||
end1: string;
|
||||
yearFilter: { start: string; end: string };
|
||||
setVar1: (value: string) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const SwageMhl: React.FC<SwageMhlProps> = ({
|
||||
var1,
|
||||
start1,
|
||||
end1,
|
||||
yearFilter,
|
||||
setVar1,
|
||||
setStart1,
|
||||
setEnd1,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="mb-2 flex items-center">
|
||||
{/* 설치년도 */}
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={(range) =>
|
||||
setYearFilter({
|
||||
...yearFilter,
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
})
|
||||
}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 용도 */}
|
||||
<SelectBox
|
||||
className="ml-8 w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label="용도"
|
||||
selectArray={MS_SWAGE_MHL_USE_CODES}
|
||||
value={var1}
|
||||
onSelectValue={(val) => {
|
||||
setVar1(String(val));
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{/* 하수맨홀 구경 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">구경(mm)</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SwageMhl);
|
||||
@@ -0,0 +1,102 @@
|
||||
// 하수펌프장
|
||||
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
|
||||
interface SwagePmstnProps {
|
||||
start1: string;
|
||||
start2: string;
|
||||
end1: string;
|
||||
end2: string;
|
||||
yearFilter: { start: string; end: string };
|
||||
setStart1: (value: string) => void;
|
||||
setStart2: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setEnd2: (value: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const SwagePmstn: React.FC<SwagePmstnProps> = ({
|
||||
start1,
|
||||
start2,
|
||||
end1,
|
||||
end2,
|
||||
yearFilter,
|
||||
setStart1,
|
||||
setStart2,
|
||||
setEnd1,
|
||||
setEnd2,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 설치년도 */}
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={range => setYearFilter({ ...yearFilter, start: range.start, end: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="ml-8 mr-2 w-auto text-xl text-gray-600">일일처리용량(㎥)</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
양수능력(m²/d)
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd2(val);
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SwagePmstn);
|
||||
@@ -0,0 +1,132 @@
|
||||
// 하수관로
|
||||
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { SelectBox } from '@common/business_components/ui';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES } from '../components/common/table';
|
||||
|
||||
interface SwagePplProps {
|
||||
var1: string;
|
||||
var2: string;
|
||||
start1: string;
|
||||
end1: string;
|
||||
yearFilter: { start: string; end: string };
|
||||
setVar1: (value: string) => void;
|
||||
setVar2: (value: string) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const SwagePpl: React.FC<SwagePplProps> = ({
|
||||
var1,
|
||||
var2,
|
||||
start1,
|
||||
end1,
|
||||
yearFilter,
|
||||
setVar1,
|
||||
setVar2,
|
||||
setStart1,
|
||||
setEnd1,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-6">
|
||||
<div className="mb-2 flex items-center">
|
||||
{/* 설치년도 */}
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={(range) =>
|
||||
setYearFilter({
|
||||
...yearFilter,
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
})
|
||||
}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 용도 */}
|
||||
<SelectBox
|
||||
className="ml-8 w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label="용도"
|
||||
selectArray={MS_SWAGE_PPL_USE_CODES}
|
||||
value={var1}
|
||||
onSelectValue={(val) => {
|
||||
setVar1(String(val));
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{/* 재질 */}
|
||||
<SelectBox
|
||||
className="w-60 mr-8"
|
||||
selectClass="rounded-lg bg-white text-xl"
|
||||
label="재질"
|
||||
selectArray={MS_SWAGE_PPL_CODES}
|
||||
value={var2}
|
||||
onSelectValue={(val) => {
|
||||
setVar2(String(val));
|
||||
}}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
{/* 관경 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">관경(mm)</Label>
|
||||
<Input
|
||||
className="w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setVar1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 연장 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">연장(m)</Label>
|
||||
<Input
|
||||
className="w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SwagePpl);
|
||||
@@ -0,0 +1,121 @@
|
||||
// 하수처리시설
|
||||
|
||||
import { YearRangeSelectBox } from '@/components/ui/YearRangeSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
|
||||
interface SwagePrcsFcltProps {
|
||||
var1: string;
|
||||
start1: string;
|
||||
start2: string;
|
||||
end1: string;
|
||||
end2: string;
|
||||
yearFilter: { start: string; end: string };
|
||||
setVar1: (value: string) => void;
|
||||
setStart1: (value: string) => void;
|
||||
setStart2: (value: string) => void;
|
||||
setEnd1: (value: string) => void;
|
||||
setEnd2: (value: string) => void;
|
||||
setYearFilter: (filter: { start: string; end: string }) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const SwagePrcsFclt: React.FC<SwagePrcsFcltProps> = ({
|
||||
var1,
|
||||
start1,
|
||||
start2,
|
||||
end1,
|
||||
end2,
|
||||
yearFilter,
|
||||
setVar1,
|
||||
setStart1,
|
||||
setStart2,
|
||||
setEnd1,
|
||||
setEnd2,
|
||||
setYearFilter,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-3">
|
||||
{/* 가동개시일자 */}
|
||||
<div className="mb-2 flex items-center">
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">가동개시일자</Label>
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.start}
|
||||
end={yearFilter.end}
|
||||
onChange={(range) =>
|
||||
setYearFilter({
|
||||
...yearFilter,
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
})
|
||||
}
|
||||
startLabel=""
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 시설용량 */}
|
||||
<Label className="ml-8 mr-2 w-auto text-xl text-gray-600">시설용량(㎥/d)</Label>
|
||||
<Input
|
||||
className="w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 처리구역 면적 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">처리구역 면적(m²)</Label>
|
||||
<Input
|
||||
className="w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={start2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setStart2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[60px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={end2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
setEnd2(val);
|
||||
}}
|
||||
/>
|
||||
{/* 하수처리시설명 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">하수처리시설명</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(SwagePrcsFclt);
|
||||
@@ -0,0 +1,69 @@
|
||||
// 하수처리구역
|
||||
|
||||
import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
|
||||
import { Button, Input, Label } from '@common/components';
|
||||
import React from 'react';
|
||||
|
||||
interface swagePrcsZoneProps {
|
||||
var1: string;
|
||||
var2: string;
|
||||
city: number;
|
||||
sgg: number;
|
||||
setVar1: (value: string) => void;
|
||||
setVar2: (value: string) => void;
|
||||
setCity: (value: number) => void;
|
||||
setSgg: (value: number) => void;
|
||||
handleSearch: () => void;
|
||||
}
|
||||
|
||||
const swagePrcsZone: React.FC<swagePrcsZoneProps> = ({
|
||||
var1,
|
||||
var2,
|
||||
city,
|
||||
sgg,
|
||||
setVar1,
|
||||
setVar2,
|
||||
setCity,
|
||||
setSgg,
|
||||
handleSearch,
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-3">
|
||||
<div className="mb-2 flex items-center">
|
||||
<SidoSigunguSelectBox
|
||||
sido={city}
|
||||
sigungu={sgg}
|
||||
onSidoChange={(sidoId) => {
|
||||
setCity(sidoId);
|
||||
setSgg(0);
|
||||
}}
|
||||
onSigunguChange={setSgg}
|
||||
/>
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">하수처리구역명</Label>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={var1}
|
||||
onChange={(e) => {
|
||||
setVar1(e.target.value);
|
||||
}}
|
||||
/>
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">하수처리시설명</Label>
|
||||
<Input
|
||||
className='!text-xl w-[120px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={var2}
|
||||
onChange={(e) => {
|
||||
setVar2(e.target.value);
|
||||
}}
|
||||
/>
|
||||
{/* 조회 버튼 */}
|
||||
<div className="flex-1 flex justify-end">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(swagePrcsZone);
|
||||
@@ -4,7 +4,6 @@ import ContentLayout from '@/components/ui/contentLayout'
|
||||
import { GovSidoSigunguSearchForm } from '@/components/ui/govSidoSigunguSearchForm'
|
||||
import { DatePicker, DateMonthPicker, Form, SearchFormRight, SelectBox } from '@common/business_components/ui'
|
||||
import ListDiv from '@common/business_components/ui/list_div'
|
||||
import { SelectBox2 } from '@common/business_components/ui/select/index-2'
|
||||
import { Button, Label } from '@common/components/ui'
|
||||
import { useEffect, useState, type ReactNode } from 'react'
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||
@@ -259,8 +258,8 @@ const BaseLayout = ({
|
||||
setRtnEnvPartSidoValue={(val) => setCtpvCd?.(val)}
|
||||
setRtnEnvPartSigunValue={setSigungu} // 선택된 시군구 값을 받을 콜백 함수 전달
|
||||
>
|
||||
<SelectBox2 label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} />
|
||||
{/* <SelectBox2 label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} /> */}
|
||||
<SelectBox label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} selectContentClass="z-[101]" />
|
||||
{/* <SelectBox label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} selectContentClass="z-[101]" /> */}
|
||||
</GovSidoSigunguSearchForm>
|
||||
{selectBox?.searchChk ? <SearchFormRight><Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button></SearchFormRight> : null}
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,29 @@ const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
|
||||
export const getData = async (url: string, data?: object): Promise<any> => {
|
||||
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
|
||||
method: "GET",
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return data;
|
||||
try {
|
||||
// 응답이 비어있는지 확인
|
||||
const contentLength = res.headers.get('content-length');
|
||||
if (contentLength === '0') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
||||
const text = await res.text();
|
||||
if (!text || text.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// JSON 파싱 시도
|
||||
const data = JSON.parse(text);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('JSON parsing error:', error);
|
||||
// JSON 파싱에 실패한 경우, 원본 텍스트 또는 null 반환
|
||||
throw new Error('Invalid JSON response from server');
|
||||
}
|
||||
}
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
@@ -93,7 +111,7 @@ export const getCmsData = async (url: string, data?: object): Promise<any> => {
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
|
||||
export function getCookie(name:string) {
|
||||
export function getCookie(name: string) {
|
||||
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
||||
if (match && match[2]) {
|
||||
return decodeURIComponent(match[2]);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useMemo } from "react";
|
||||
import { SelectBox2 } from "@common/business_components/ui/select/index-2";
|
||||
import { SelectBox } from '@common/business_components/ui'
|
||||
|
||||
interface Option {
|
||||
id: number;
|
||||
@@ -138,21 +138,23 @@ export function SidoSigunguSelectBox({
|
||||
|
||||
return (
|
||||
<div className={`flex items-center justify-center ${className} mr-8`}>
|
||||
<SelectBox2
|
||||
<SelectBox
|
||||
className="w-[150px] mr-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="시도"
|
||||
selectArray={sidoList.map(opt => ({ val: opt.id, text: opt.name }))}
|
||||
value={sido}
|
||||
onSelectValue={val => onSidoChange(Number(val))}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
<SelectBox2
|
||||
<SelectBox
|
||||
className="w-48"
|
||||
selectClass="w-48 rounded-lg bg-white truncate"
|
||||
label="시군구"
|
||||
selectArray={sigunguList.map(opt => ({ val: opt.id, text: opt.name }))}
|
||||
value={sigungu}
|
||||
onSelectValue={val => onSigunguChange(Number(val))}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { SelectBox2 } from "@common/business_components/ui/select/index-2";
|
||||
import { SelectBox } from '@common/business_components/ui'
|
||||
|
||||
interface YearRangeSelectBoxProps {
|
||||
start: string;
|
||||
@@ -89,22 +89,24 @@ export function YearRangeSelectBox({
|
||||
|
||||
return (
|
||||
<div className={`flex items-center ${className}`}>
|
||||
<SelectBox2
|
||||
<SelectBox
|
||||
className="flex-1 mr-3"
|
||||
selectClass={selectClass}
|
||||
label={startLabel}
|
||||
selectArray={getStartYearArray()}
|
||||
onSelectValue={val => onChange({ start: String(val), end })}
|
||||
value={start}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
<span className="font-semibold text-xl mx-2">~</span>
|
||||
<SelectBox2
|
||||
<SelectBox
|
||||
className="flex-1 ml-1"
|
||||
selectClass={selectClass}
|
||||
label={endLabel}
|
||||
selectArray={getEndYearArray()}
|
||||
onSelectValue={val => onChange({ start, end: String(val) })}
|
||||
value={end}
|
||||
selectContentClass="z-[101]"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -456,7 +456,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
if (isPrcsFclt) {
|
||||
if (viewMode == 'list') {
|
||||
return (
|
||||
<>
|
||||
<div style={{overflowY:'auto'}}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 현황" />
|
||||
</div>
|
||||
@@ -470,11 +470,11 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
cellClick={hasuCellClick}
|
||||
onPageChange={setHasuPage}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
<div style={{overflowX:'hidden'}}>
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Title title="하수처리시설 상세정보" />
|
||||
</div>
|
||||
@@ -497,7 +497,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
cellClick={() => { }}
|
||||
onPageChange={() => { }}
|
||||
/>
|
||||
</>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -528,7 +528,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
} else {
|
||||
// 재이용시설 목록
|
||||
return (
|
||||
<>
|
||||
<div style={{overflowY:'auto'}}>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="하수재이용시설 현황" />
|
||||
{/* <div className="flex gap-2 mt-8">
|
||||
@@ -550,7 +550,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />
|
||||
</UiTable>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -600,7 +600,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
} else {
|
||||
// 중수도시설 목록
|
||||
return (
|
||||
<>
|
||||
<div style={{overflowY:'auto'}}>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="중수도시설 현황" />
|
||||
{/* <div className="flex gap-2 mt-8">
|
||||
@@ -622,7 +622,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />
|
||||
</UiTable>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -636,7 +636,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
} else {
|
||||
// 온배수시설 목록
|
||||
return (
|
||||
<>
|
||||
<div style={{overflowY:'auto'}}>
|
||||
<div className="flex justify-between items-center">
|
||||
<Title title="온배수시설 현황" />
|
||||
{/* <div className="flex gap-2 mt-8">
|
||||
@@ -658,7 +658,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
>
|
||||
<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />
|
||||
</UiTable>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -668,7 +668,13 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({
|
||||
|
||||
return (
|
||||
viewMode == 'nothing' ? <></> :
|
||||
<NewWindowPortal onClose={memoizedOnClose} title="시설 상세 정보">
|
||||
<NewWindowPortal
|
||||
onClose={memoizedOnClose}
|
||||
title="시설 상세 정보"
|
||||
width={viewMode == 'list' ? window.screen.availWidth : 1100}
|
||||
height={viewMode == 'list' ? window.screen.availHeight : layerId == '하수처리시설' && viewMode == 'detail' ? 450 : 900}
|
||||
scrollbars='yes'
|
||||
resizable='no'>
|
||||
{/* 체크된 레이어에 따른 조건부 렌더링 */}
|
||||
{renderConditionalContent()}
|
||||
</NewWindowPortal>
|
||||
|
||||
@@ -27,9 +27,13 @@ interface NewWindowPortalProps {
|
||||
children: React.ReactNode;
|
||||
onClose: () => void;
|
||||
title?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
resizable?: string;
|
||||
scrollbars?: string;
|
||||
}
|
||||
|
||||
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "Detail" }) => {
|
||||
const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "", width = 900, height = 800, resizable = "yes", scrollbars = "yes" }) => {
|
||||
const [container, setContainer] = useState<HTMLElement | null>(null);
|
||||
const newWindowRef = useRef<Window | null>(null);
|
||||
const onCloseRef = useRef(onClose);
|
||||
@@ -46,13 +50,23 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti
|
||||
useEffect(() => {
|
||||
// 이미 창이 열려있으면 새로 열지 않음
|
||||
if (newWindowRef.current && !newWindowRef.current.closed) return;
|
||||
// 브라우저 화면 정보
|
||||
const screenLeft = window.screenLeft ?? window.screenX;
|
||||
const screenTop = window.screenTop ?? window.screenY;
|
||||
const screenWidth = window.innerWidth ?? document.documentElement.clientWidth;
|
||||
const screenHeight = window.innerHeight ?? document.documentElement.clientHeight;
|
||||
|
||||
// 중앙 위치 계산
|
||||
const left = screenLeft + (screenWidth - width) / 2;
|
||||
const top = screenTop + (screenHeight - height) / 2;
|
||||
|
||||
// 1. 새 창을 열고 참조를 저장합니다.
|
||||
newWindowRef.current = window.open('', title, 'width=900,height=800,resizable=yes,scrollbars=yes');
|
||||
|
||||
const popupOptions = `width=${width},height=${height},left=${left}, top=${top}, resizable=${resizable},scrollbars=${scrollbars}`;
|
||||
newWindowRef.current = window.open('', title, popupOptions);
|
||||
|
||||
if (newWindowRef.current) {
|
||||
newWindowRef.current.document.title = title;
|
||||
|
||||
|
||||
// 2. React 컴포넌트를 마운트할 컨테이너 div를 생성하여 새 창의 body에 추가합니다.
|
||||
const div = newWindowRef.current.document.createElement('div');
|
||||
newWindowRef.current.document.body.appendChild(div);
|
||||
@@ -60,7 +74,6 @@ const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, ti
|
||||
|
||||
// 3. 메인 창의 스타일을 새 창으로 복사합니다. (매우 중요)
|
||||
copyStyles(document, newWindowRef.current.document);
|
||||
|
||||
// 4. 사용자가 창을 닫을 때 부모에게 알리기 위한 이벤트 리스너를 설정합니다.
|
||||
// const handleUnload = () => {
|
||||
// onClose();
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import { useEffect, useRef, useState, useCallback } from "react";
|
||||
import { mapService } from "../service";
|
||||
import NewWindowPopup from "../newWindowPopup";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
|
||||
const Map = () => {
|
||||
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
|
||||
const gisMaps = useRef<any | null>(null);
|
||||
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태
|
||||
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
|
||||
const [checkedLayers, setCheckedLayers] = useState<string[]>([]);
|
||||
const [checkedLayersData, setCheckedLayersData] = useState<any[]>([]);
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const response = await mapService.getLyrStngs();
|
||||
setTreeData(response); // API 응답 저장
|
||||
} catch (error) {
|
||||
console.error("데이터 불러오기 실패:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 팝업 닫기 핸들러를 useCallback으로 메모이제이션
|
||||
const handlePopupClose = useCallback(() => {
|
||||
setIsOpen(false);
|
||||
setFeatureData(null);
|
||||
}, []);
|
||||
|
||||
// 컴포넌트 마운트 시 1회 실행
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// 체크된 레이어 정보를 받는 핸들러
|
||||
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => {
|
||||
setCheckedLayers(checkedIds);
|
||||
setCheckedLayersData(checkedNodesData);
|
||||
};
|
||||
|
||||
// SearchModal, GisContent 등에서 호출
|
||||
const onFeatureSelect = (data: any) => {
|
||||
const map = gisMaps.current;
|
||||
if (!map) {
|
||||
console.warn("지도가 초기화되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 항상 같은 select 객체 사용
|
||||
const select = GisApp.Module.select as any;
|
||||
if (!select) return;
|
||||
|
||||
// 1. 기존 선택 초기화
|
||||
select.resetSelectedFeature();
|
||||
|
||||
// 2. data가 OpenLayers Feature 인스턴스인지 확인
|
||||
let foundFeature: any = 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: any) => {
|
||||
const source = layer.getSource?.();
|
||||
if (source?.getFeatureById) {
|
||||
const feature = source.getFeatureById(String(featureId).trim());
|
||||
if (feature) foundFeature = feature;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 피처 선택
|
||||
if (foundFeature) {
|
||||
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);
|
||||
};
|
||||
|
||||
// MapSearchBar 래퍼 컴포넌트
|
||||
// const CustomMapSearchBar = (props: any) => {
|
||||
// return (
|
||||
// <MapSearchBar
|
||||
// {...props}
|
||||
// searchType="공급처 후보지 조회"
|
||||
// showApplyButton={false}
|
||||
// />
|
||||
// );
|
||||
// };
|
||||
|
||||
return (
|
||||
<div className="bg-container">
|
||||
<Gis
|
||||
treeData={treeData}
|
||||
isSearch={false}
|
||||
gisMaps={gisMaps}
|
||||
isMiniMap={false}
|
||||
searchView={undefined}
|
||||
// searchView={(props: any) => (
|
||||
// <MapSearchBar
|
||||
// searchType="공급처 후보지 조회"
|
||||
// showApplyButton={false}
|
||||
// checkedLayers={checkedLayers}
|
||||
// checkedLayersData={checkedLayersData}
|
||||
// {...props}
|
||||
// />
|
||||
// )
|
||||
// }
|
||||
detailView={(props: any) =>(
|
||||
<NewWindowPopup
|
||||
onClose={handlePopupClose} // 메모이제이션된 함수 사용
|
||||
checkedLayers={checkedLayers}
|
||||
checkedLayersData={checkedLayersData}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
setIsOpen={setIsOpen}
|
||||
setFeatureData={setFeatureData}
|
||||
featureData={featureData}
|
||||
setIsSearchModalOpen={setIsSearchModalOpen}
|
||||
isOpen={isOpen}
|
||||
isSearchModalOpen={isSearchModalOpen}
|
||||
onCheckedLayersChange={handleCheckedLayersChange}
|
||||
imgVisble={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Map;
|
||||
@@ -195,7 +195,7 @@ export const StoreZus = (
|
||||
getMiddleRegionCd(set);
|
||||
getUnitWaterShedCd(set);
|
||||
getEnvPartCd(set);
|
||||
LoginApi.login({userId: "tuser10003", password: "test1234!"})
|
||||
LoginApi.login({userId: "admin1gmail", password: "test1234!"})
|
||||
return {
|
||||
...initState,
|
||||
...comFcltSearchAction(set),
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"dependencies": {
|
||||
"chart.js": "^4.4.8",
|
||||
"react": "^18.3.1",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
|
||||
@@ -94,7 +94,6 @@ export default function LineBarchart({
|
||||
|
||||
// 데이터의 최대값 계산
|
||||
const getMaxValue = (data: any[], keyPrefix: string) => {
|
||||
if (!data || data.length === 0) return 0;
|
||||
return Math.max(...data.map(item =>
|
||||
Object.keys(item)
|
||||
.filter(key => key.includes(keyPrefix))
|
||||
|
||||
@@ -1,186 +0,0 @@
|
||||
"use client"
|
||||
import type { CSSProperties } from "react";
|
||||
import { Bar, CartesianGrid, ComposedChart, Legend, Line, Tooltip, XAxis, YAxis } from "recharts";
|
||||
import {
|
||||
ChartContainer,
|
||||
type ChartConfig
|
||||
} from "./lib/chart";
|
||||
import type { ContentType } from "recharts/types/component/DefaultLegendContent";
|
||||
|
||||
// 툴크 커스텀 css
|
||||
/** 차트 기본 스타일 */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
gap: "20px",
|
||||
fontSize: "9px",
|
||||
fontWeight : "bold",
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
type LineBarChartProps = {
|
||||
chartConfig: ChartConfig | undefined;
|
||||
chartData: any[] | undefined;
|
||||
chartTitle: string;
|
||||
chartClassName?: string;
|
||||
graphSize?: number;
|
||||
yFieldText?: string | undefined;
|
||||
yFieldText2?: string | undefined;
|
||||
showLegend?: boolean;
|
||||
}
|
||||
|
||||
type LegendEntry = {
|
||||
value: string;
|
||||
color: string;
|
||||
type?: string;
|
||||
payload?: unknown;
|
||||
};
|
||||
|
||||
type LegendProps = {
|
||||
payload?: LegendEntry[];
|
||||
};
|
||||
|
||||
// 커스텀 툴팁
|
||||
const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="w-[180px] rounded bg-white p-2.5 shadow-[0px_2px_10px_rgba(0,0,0,0.2)]">
|
||||
<p className="text-[12px] font-bold m-3">{label}</p>
|
||||
{payload.map((item, index) => (
|
||||
<div key={index} style={{ marginTop: "10px", display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
{/* 색상 박스 추가 (범례 색상과 동일) */}
|
||||
<div style={{ width: "10px", height: "10px", backgroundColor: item.color, borderRadius: "50%" }}></div>
|
||||
<span className="text-[11px] font-bold">{` ${item.name} : ${item.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: LegendEntry, index: number) => (
|
||||
<div key={`legend-${index}`} className="flex items-center gap-2">
|
||||
<div className="w-[14px] h-[14px] ml-10" style={{ backgroundColor: entry.color }}></div>
|
||||
<span className="text-[12px]">{entry.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function LineBarchart2({
|
||||
chartConfig,
|
||||
chartData,
|
||||
chartTitle,
|
||||
chartClassName = 'w-full h-full',
|
||||
graphSize = 40,
|
||||
yFieldText,
|
||||
yFieldText2,
|
||||
showLegend = true
|
||||
}: LineBarChartProps) {
|
||||
if (!chartData || chartData.length === 0) {
|
||||
return <div>데이터가 없습니다.</div>;
|
||||
}
|
||||
|
||||
// 데이터의 최대값 계산
|
||||
const getMaxValue = (data: any[], keyPrefix: string) => {
|
||||
if (!data || data.length === 0) return 0;
|
||||
return Math.max(...data.map(item =>
|
||||
Object.keys(item)
|
||||
.filter(key => key.includes(keyPrefix))
|
||||
.map(key => item[key])
|
||||
).flat());
|
||||
};
|
||||
|
||||
const lineMaxValue = getMaxValue(chartData, 'lineVal');
|
||||
const barMaxValue = getMaxValue(chartData, 'barVal');
|
||||
return (
|
||||
<div className={chartClassName}>
|
||||
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
||||
<div style={{ textAlign: "left", fontSize: "13px", fontWeight: "bold", marginTop:"13px", marginBottom: "10px" }}>
|
||||
{chartTitle}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="h-[calc(100%-42px)] flex justify-center">
|
||||
<ChartContainer config={chartConfig ?? {}} className="h-full w-full">
|
||||
<ComposedChart
|
||||
data={chartData}
|
||||
height={graphSize * 7.5}
|
||||
width={graphSize * 12.5}
|
||||
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="label" fontSize="8px" fontWeight="bold"/>
|
||||
{/* 왼쪽 Y축: lineVal */}
|
||||
{yFieldText && <YAxis
|
||||
yAxisId="left"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
orientation="left"
|
||||
domain={[0, Math.ceil(lineMaxValue * 1)+170]}
|
||||
label={{ value: yFieldText, angle: -90, position: 'insideLeft', offset: -8 }}
|
||||
/>}
|
||||
{/* 오른쪽 Y축: barVal */}
|
||||
{yFieldText2 && <YAxis
|
||||
yAxisId="right"
|
||||
className="text-[12px] font-bold"
|
||||
tickCount={6}
|
||||
orientation="right"
|
||||
domain={[0, Math.ceil(barMaxValue * 1)+20]}
|
||||
label={{ value: yFieldText2, angle: -90, position: 'insideRight' }}
|
||||
reversed={true}
|
||||
/>}
|
||||
<Tooltip content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />} />
|
||||
{showLegend && <Legend content={CustomLegend as ContentType}/>}
|
||||
{/* 바 차트 (오른쪽 Y축 사용) */}
|
||||
{Object.keys(chartData[0]).map((key, index) => {
|
||||
const config = chartConfig?.[key];
|
||||
if(key.includes("barVal")) {
|
||||
return (
|
||||
<Bar
|
||||
key={`bar-${key}-${index}`}
|
||||
yAxisId="right"
|
||||
dataKey={key}
|
||||
fill={config?.color || "black"}
|
||||
name={String(config?.label || "Y축")}
|
||||
maxBarSize={80}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{/* 라인 차트 (왼쪽 Y축 사용) */}
|
||||
{Object.keys(chartData[0]).map((key, index) => {
|
||||
const config = chartConfig?.[key];
|
||||
if(key.includes("lineVal")) {
|
||||
return (
|
||||
<Line
|
||||
key={`line-${key}-${index}`}
|
||||
yAxisId="left"
|
||||
type="linear"
|
||||
dataKey={key}
|
||||
stroke={config?.color || "black"}
|
||||
strokeWidth={2}
|
||||
dot={{r: 0}}
|
||||
name={String(config?.label || "X축")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</ComposedChart>
|
||||
</ChartContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
import DoubleBarChart from './DoubleBar';
|
||||
import DoughnutChart from './DoughnutChart';
|
||||
import LineBarchart from './LineBarChart';
|
||||
import LineBarchart2 from './LineBarChart2';
|
||||
import LineChart from './LineChart';
|
||||
|
||||
/**차트 FORM */
|
||||
@@ -15,8 +14,6 @@ const CHART_FORM = {
|
||||
DN :'daughnut',
|
||||
/**LINE,BAR차트 */
|
||||
LINEBAR : 'linebar',
|
||||
/**LINE,BAR차트 */
|
||||
LINEBAR2 : 'linebar2',
|
||||
/**더블바 차트 형태*/
|
||||
DOUBLEBAR : 'doublebar'
|
||||
};
|
||||
@@ -75,10 +72,6 @@ function MainChart(props: MainChartProps) {
|
||||
return (
|
||||
<LineBarchart chartConfig={props.chartConfig} chartData={props.chartData} chartTitle={props.chartTitle} chartClassName={props.chartClassName} graphSize={props.graphSize} yFieldText={props.yFieldText} yFieldText2={props.yFieldText2} showLegend={props.showLegend}/>
|
||||
);
|
||||
} else if (props.chartForm == CHART_FORM.LINEBAR2){
|
||||
return (
|
||||
<LineBarchart2 chartConfig={props.chartConfig} chartData={props.chartData} chartTitle={props.chartTitle} chartClassName={props.chartClassName} graphSize={props.graphSize} yFieldText={props.yFieldText} yFieldText2={props.yFieldText2} showLegend={props.showLegend}/>
|
||||
);
|
||||
} else if (props.chartForm == CHART_FORM.DOUBLEBAR){
|
||||
return (
|
||||
<DoubleBarChart chartConfig={props.chartConfig} chartData={props.chartData} chartTitle={props.chartTitle} chartClassName={props.chartClassName} graphSize={props.graphSize} yFieldText={props.yFieldText} showLegend={props.showLegend}/>
|
||||
|
||||
@@ -20,73 +20,6 @@ import GisApp from './lib/gis.core';
|
||||
|
||||
export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null });
|
||||
export const useGisContext = () => useContext(GisContext);
|
||||
export const searchUpperPpls = async (fcltMngNo) => {
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
|
||||
|
||||
const res = await fetch(`${BASE_URL}/v1/fcltmhl/upperPpls/${fcltMngNo}`, {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
let data = null;
|
||||
if (res.ok) {
|
||||
try {
|
||||
// 응답이 비어있는지 확인
|
||||
const contentLength = res.headers.get('content-length');
|
||||
if (contentLength === '0') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
// 응답 텍스트를 먼저 가져와서 빈 문자열인지 확인
|
||||
const text = await res.text();
|
||||
if (!text || text.trim() === '') alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
// JSON 파싱 시도
|
||||
data = JSON.parse(text);
|
||||
} catch (error) {
|
||||
console.error('JSON parsing error:', error);
|
||||
alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
}
|
||||
}
|
||||
|
||||
const select = GisApp.Module.select;
|
||||
|
||||
const gisMap = GisApp.Module.core?.map;
|
||||
|
||||
let foundFeature = null;
|
||||
|
||||
data.forEach(item => {
|
||||
const pplMngNo = item.ppl_mng_no.trim();
|
||||
|
||||
gisMap.getLayers().getArray().filter(e => e.get("lyrTyNm") == 'tb_ms_swage_ppl').forEach(layer => {
|
||||
const source = layer.getSource?.();
|
||||
if (source?.getFeatures) {
|
||||
const features = source.getFeatures();
|
||||
for (const f of features) {
|
||||
if (f.values_.fclt_mng_no.trim() === pplMngNo) {
|
||||
foundFeature = f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (foundFeature) select.selectMultiFeature(foundFeature, true);
|
||||
});
|
||||
|
||||
if (!foundFeature) alert(`해당 맨홀의 상류관로를 찾을 수 없습니다.`);
|
||||
|
||||
let geometry = foundFeature?.getGeometry();
|
||||
if (geometry) {
|
||||
let extent = geometry.getExtent();
|
||||
|
||||
gisMap.getView().fit(extent, {
|
||||
duration: 300,
|
||||
padding: [50, 50, 50, 50],
|
||||
maxZoom: 14,
|
||||
constrainResolution: true,
|
||||
nearest: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function Gis({
|
||||
isOpen,
|
||||
@@ -485,7 +418,6 @@ function Gis({
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* <GisLonLat /> */}
|
||||
<GisToolBar />
|
||||
<GisMapToggle />
|
||||
<GisSelect onFeatureSelect={handleFeatureSelect} />
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import {
|
||||
MultiBackend,
|
||||
Tree,
|
||||
getBackendOptions
|
||||
getBackendOptions,
|
||||
DndProvider
|
||||
} from "@minoru/react-dnd-treeview";
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { DndProvider } from "react-dnd";
|
||||
|
||||
import { useGisContext } from './Gis';
|
||||
import GisApp from './lib/gis.core';
|
||||
|
||||
@@ -42,10 +42,15 @@ GisApp.GOOGLE_SATELLITE_URL = `https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z
|
||||
GisApp.GOOGLE_HYBRID_URL = `https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}&key=${GisApp.GOOGLE_API_KEY}`;
|
||||
GisApp.GOOGLE_3D_URL = `https://tile.googleapis.com/v1/3dtiles/root.json?key=${GisApp.GOOGLE_API_KEY}`;
|
||||
|
||||
// TODO: NGII 정보
|
||||
// 인터넷망: NGII 정보
|
||||
GisApp.NGII_API_KEY = '04trYP9_xwLAfALjwZ-B8g'; // NGII에서 발급받은 API 키
|
||||
GisApp.NGII_WMTS_URL = `https://map.ngii.go.kr/openapi/Gettile.do?apikey=${GisApp.NGII_API_KEY}`;
|
||||
|
||||
//행정망: NGII 정보
|
||||
//GisApp.NGII_API_KEY = 'BDE2BEF39DAFEC293AB6730150F222AC'; // NGII에서 발급받은 API 키
|
||||
//GisApp.NGII_WMTS_URL = `http://10.98.25.94:18080/openapi/Gettile.do?apikey=${GisApp.NGII_API_KEY}`;
|
||||
|
||||
|
||||
GisApp.EMAP_URL = '/emap/';
|
||||
GisApp.EMAP_SATELLITE_URL = '/emap/satellite/';
|
||||
GisApp.VWORLD_URL = 'https://xdworld.vworld.kr/2d/Base/service/{z}/{x}/{y}.png';
|
||||
|
||||
@@ -71,7 +71,7 @@ GisOlLayer.prototype = {
|
||||
/**
|
||||
* [사용]WFS 벡터 소스 반환
|
||||
*/
|
||||
getVectorSourceGeoserver: function (options) {
|
||||
getVectorSourceBbox: function (options) {
|
||||
let url = GisApp.GEOSERVER_URL;
|
||||
let toProj = GisApp.TO_PROJ;
|
||||
let lyrTyNm = options.lyrTyNm;
|
||||
@@ -110,8 +110,11 @@ GisOlLayer.prototype = {
|
||||
getVectorTileSource: function (options) {
|
||||
let url = GisApp.GEOSERVER_URL;
|
||||
let toProj = GisApp.TO_PROJ;
|
||||
let lyrTyNm = options.lyrTyNm;
|
||||
let typename = this.storageNm + lyrTyNm;
|
||||
|
||||
let params = {};
|
||||
params['TYPENAME'] = this.storageNm + options.typeName;
|
||||
params['TYPENAME'] = typename;
|
||||
params['SERVICE'] = 'WFS';
|
||||
params['VERSION'] = '1.1.0';
|
||||
params['REQUEST'] = 'GetFeature';
|
||||
@@ -119,19 +122,14 @@ GisOlLayer.prototype = {
|
||||
params['OUTPUTFORMAT'] = 'application/json';
|
||||
let strParams = new URLSearchParams(params).toString();
|
||||
|
||||
let source = new VectorTile({
|
||||
let source = new VectorSource({
|
||||
format: new GeoJSON(),
|
||||
|
||||
tileUrlFunction: function (tileCoord, pixelRatio, projection) {
|
||||
let filterStr = '&CQL_FILTER=bbox(GEOM,' + source.getTileGrid().getTileCoordExtent(tileCoord).join(',') + ",'" + projection.getCode() + "')";
|
||||
if (options.flterSql) {
|
||||
filterStr += ' AND ' + options.flterSql;
|
||||
}
|
||||
let paramWithUrl = url + '?' + strParams + filterStr;
|
||||
return paramWithUrl;
|
||||
url: function (extent) {
|
||||
return url + '?' + strParams;
|
||||
},
|
||||
tileGrid: createXYZ,
|
||||
strategy: all,
|
||||
});
|
||||
return source;
|
||||
|
||||
return source;
|
||||
},
|
||||
@@ -142,8 +140,9 @@ GisOlLayer.prototype = {
|
||||
option = option || {};
|
||||
//레이어 생성
|
||||
let returnLayer = this.makeLayer(option);
|
||||
|
||||
//레이어 소스 생성
|
||||
let source = this.getVectorSourceGeoserver(option);
|
||||
let source = this.getVectorSourceBbox(option);
|
||||
returnLayer.setSource(source);
|
||||
//스타일 추가
|
||||
returnLayer.setStyle(this.gisStyle.getLayerStyleFunction(returnLayer, option));
|
||||
|
||||
@@ -429,19 +429,6 @@ GisOlSelect.prototype = {
|
||||
auto: auto,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 피처 다중 셀렉트
|
||||
*/
|
||||
selectMultiFeature: function (feature, auto) {
|
||||
let me = this;
|
||||
me.selectedFeatures.push(feature);
|
||||
me.selectInteraction.dispatchEvent({
|
||||
type: 'select',
|
||||
selected: me.selectedFeatures,
|
||||
deselected: [],
|
||||
auto: auto,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default GisOlSelect;
|
||||
|
||||
@@ -322,7 +322,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
},
|
||||
{
|
||||
name: "bznmTotNo",
|
||||
label: "",
|
||||
label: "종사업자등록번호",
|
||||
component: (field) => (
|
||||
<FormUi
|
||||
label="종사업자등록번호"
|
||||
@@ -419,7 +419,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<div className="mb-4">
|
||||
<label htmlFor={inputId} className="block text-gray-800 font-medium text-xl">
|
||||
<label htmlFor={inputId} className="block text-gray-800 font-medium">
|
||||
{label}
|
||||
{isRequired && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
@@ -432,7 +432,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
})}
|
||||
|
||||
{error?.message && (
|
||||
<p id={errorId} role="alert" className="text-red-500 text-lg mt-1">
|
||||
<p id={errorId} role="alert" className="text-red-500 text-sm mt-1">
|
||||
{error.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -122,8 +122,8 @@ export function UiTable({
|
||||
: cellClick
|
||||
? "cursor-pointer"
|
||||
: ""
|
||||
} sticky top-20 z-40 bg-secondary !font-bold`}
|
||||
style={{ top: `${headerHeight + index * 32-4}px` }}
|
||||
} sticky z-40 bg-secondary !font-bold`}
|
||||
style={{ top: `${headerHeight + index * 39}px` }}
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={`cell-${head.id}-${index}`}
|
||||
>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.474.0",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getApiClient } from "./client";
|
||||
import { ENDPOINT } from "../config";
|
||||
import { getConfig } from "@common/config";
|
||||
|
||||
export interface MenuItem {
|
||||
menuCd: number;
|
||||
@@ -26,6 +27,8 @@ export interface TreeMenu extends MenuItem {
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
const { PROXY_HOST } = getConfig();
|
||||
const BASE_URL = `${PROXY_HOST}/server`;
|
||||
|
||||
export const getMenuList = async (siteId: string) => {
|
||||
const response = await client.get<MenuItem[]>(
|
||||
@@ -40,3 +43,18 @@ export const getPreviewTreeMenuList = async (siteId: string) => {
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getAnonymouseMenuList = async (
|
||||
siteId: string,
|
||||
): Promise<MenuItem[]> => {
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/${ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS}/user/${siteId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
return response.json();
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { getApiClient } from "./client";
|
||||
import { getConfig } from "@common/config";
|
||||
import { ENDPOINT } from "../config";
|
||||
|
||||
export interface SiteInfo {
|
||||
@@ -23,11 +23,18 @@ export interface SiteInfo {
|
||||
mdfrNm: string;
|
||||
}
|
||||
|
||||
const client = getApiClient();
|
||||
const { PROXY_HOST } = getConfig();
|
||||
const BASE_URL = `${PROXY_HOST}/server`;
|
||||
|
||||
export const getSiteInfo = async (siteId: string) => {
|
||||
const response = await client.get<SiteInfo>(
|
||||
`${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`,
|
||||
const response = await fetch(
|
||||
`${BASE_URL}/${ENDPOINT.CMS_SERVICE.SITES}/${siteId}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
return response.data;
|
||||
return response.json();
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ export const ENDPOINT = {
|
||||
CMS_SERVICE: {
|
||||
MENUS: "/cms-service/api/v1/menus",
|
||||
MENU_ROLES: "/cms-service/api/v1/menu-roles",
|
||||
MENU_ANONYMOUS: "/cms-service/api/v1/menu/anonymous",
|
||||
SITES: "/cms-service/api/v1/sites",
|
||||
POPUPS: "/cms-service/api/v1/popups",
|
||||
IPS: "/cms-service/api/v1/site-ips",
|
||||
|
||||
@@ -3,6 +3,7 @@ import "./header.css";
|
||||
import { useEffect, useCallback, useState } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { images } from "@common/assets";
|
||||
import { getConfig } from "@common/config";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { setCookie, getCookie } from "../../lib";
|
||||
import {
|
||||
@@ -22,7 +23,10 @@ import { Button } from "../button";
|
||||
import { SidebarTrigger } from "../sidebar";
|
||||
import { Skeleton } from "../skeleton";
|
||||
|
||||
const siteId = getCookie("site-id-key") ?? "";
|
||||
const appConfig = getConfig();
|
||||
const siteId = appConfig.SITE_ID ?? "";
|
||||
const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
|
||||
const isLoggedIn = !!userId;
|
||||
|
||||
export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
@@ -30,7 +34,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
const { data: userInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
queryFn: () => getUserInfo(),
|
||||
enabled: !viewOnly,
|
||||
enabled: isLoggedIn && !viewOnly,
|
||||
});
|
||||
|
||||
const { data: siteInfo } = useQuery({
|
||||
@@ -50,6 +54,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
const { data: userPopupList } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.POPUPS],
|
||||
queryFn: () => getUserPopupList(),
|
||||
enabled: isLoggedIn && !viewOnly,
|
||||
});
|
||||
|
||||
const logout = async () => {
|
||||
@@ -130,30 +135,36 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) {
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="group m-0 flex h-[3rem] items-center !bg-background px-4 py-0 text-label"
|
||||
data-trigger="dropdown"
|
||||
>
|
||||
<i className="diveicon di-account-circle static translate-y-0.5 pr-2 text-3xl" />
|
||||
{userInfo?.userName}
|
||||
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>프로필 설정</DropdownMenuItem>
|
||||
<div className="flex flex-col items-start gap-0 px-4 py-3">
|
||||
<p className="text-base text-label">최종접속일</p>
|
||||
<p>{userInfo?.lastLoginDate}</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-0 px-4 py-3">
|
||||
<p className="text-base text-label">접속 IP</p>
|
||||
<p></p>
|
||||
</div>
|
||||
<DropdownMenuItem onClick={logout}>로그아웃</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
{mounted ? (
|
||||
isLoggedIn && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="group m-0 flex h-[3rem] items-center !bg-background px-4 py-0 text-label"
|
||||
data-trigger="dropdown"
|
||||
>
|
||||
<i className="diveicon di-account-circle static translate-y-0.5 pr-2 text-3xl" />
|
||||
{userInfo?.userName}
|
||||
<i className="diveicon di-chevron-down static p-0 transition-transform group-data-[state=open]:rotate-180" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem>프로필 설정</DropdownMenuItem>
|
||||
<div className="flex flex-col items-start gap-0 px-4 py-3">
|
||||
<p className="text-base text-label">최종접속일</p>
|
||||
<p>{userInfo?.lastLoginDate}</p>
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-0 px-4 py-3">
|
||||
<p className="text-base text-label">접속 IP</p>
|
||||
<p></p>
|
||||
</div>
|
||||
<DropdownMenuItem onClick={logout}>로그아웃</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
) : (
|
||||
<Skeleton className="mx-4 h-[3rem] w-[15rem]" />
|
||||
)}
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export * from "./carousel";
|
||||
export * from "./checkbox";
|
||||
export * from "./data-table";
|
||||
export * from "./date-picker";
|
||||
export * from "./input-date-picker";
|
||||
export * from "./date-range-picker";
|
||||
export * from "./dialog";
|
||||
export * from "./dropdown-menu";
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
"use client";
|
||||
|
||||
import dayjs from "dayjs";
|
||||
import { useEffect, useState } from "react";
|
||||
import { CalendarIcon } from "lucide-react";
|
||||
|
||||
import { Button } from "../button";
|
||||
import { Calendar } from "../calendar";
|
||||
import { Input } from "../input";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
||||
|
||||
import "./input-date-picker.css";
|
||||
|
||||
export interface InputDatePickerProps {
|
||||
value?: Date;
|
||||
defaultValue?: Date;
|
||||
onChange?: (date: Date | undefined) => void;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function InputDatePicker({
|
||||
value: controlledValue,
|
||||
defaultValue,
|
||||
onChange,
|
||||
placeholder,
|
||||
disabled,
|
||||
className,
|
||||
}: InputDatePickerProps) {
|
||||
const isControlled = controlledValue !== undefined;
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
const [internalDate, setInternalDate] = useState<Date | undefined>(
|
||||
defaultValue,
|
||||
);
|
||||
const [month, setMonth] = useState<Date | undefined>(
|
||||
isControlled ? controlledValue : defaultValue,
|
||||
);
|
||||
|
||||
// 실제로 사용할 date 값 (제어/비제어 모두 지원)
|
||||
const date = isControlled ? controlledValue : internalDate;
|
||||
|
||||
// 표시할 문자열 값
|
||||
const [displayValue, setDisplayValue] = useState(date);
|
||||
|
||||
// 제어 컴포넌트일 경우 외부 값 변경 감지
|
||||
useEffect(() => {
|
||||
if (isControlled) {
|
||||
setDisplayValue(controlledValue);
|
||||
setMonth(controlledValue);
|
||||
}
|
||||
}, [isControlled, controlledValue]);
|
||||
|
||||
// 내부 상태 변경 핸들러
|
||||
const handleDateChange = (newDate: Date | undefined) => {
|
||||
if (!isControlled) {
|
||||
setInternalDate(newDate);
|
||||
}
|
||||
|
||||
if (onChange) {
|
||||
onChange(newDate);
|
||||
}
|
||||
|
||||
setDisplayValue(newDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="relative flex gap-2">
|
||||
<Input
|
||||
id="date"
|
||||
type="date"
|
||||
value={displayValue ? dayjs(displayValue).format("YYYY-MM-DD") : ""}
|
||||
disabled={disabled}
|
||||
className={`input-date-picker bg-background pr-10 ${className || ""}`}
|
||||
onChange={(e) => {
|
||||
const newDate = new Date(e.target.value);
|
||||
setDisplayValue(newDate);
|
||||
|
||||
if (isValidDate(newDate)) {
|
||||
handleDateChange(newDate);
|
||||
setMonth(newDate);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "ArrowDown") {
|
||||
e.preventDefault();
|
||||
setOpen(true);
|
||||
}
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
id="date-picker"
|
||||
variant="ghost"
|
||||
color="white"
|
||||
disabled={disabled}
|
||||
className="absolute right-2 top-1/2 size-7 -translate-y-1/2 rounded-[0.4rem] border-none p-0"
|
||||
>
|
||||
<CalendarIcon className="!size-full" />
|
||||
<span className="sr-only">Select date</span>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className="w-auto overflow-hidden p-0"
|
||||
align="end"
|
||||
alignOffset={-8}
|
||||
sideOffset={10}
|
||||
>
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={date}
|
||||
captionLayout="dropdown"
|
||||
month={month}
|
||||
onMonthChange={setMonth}
|
||||
onSelect={(newDate) => {
|
||||
handleDateChange(newDate);
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function isValidDate(date: Date | undefined) {
|
||||
if (!date) {
|
||||
return false;
|
||||
}
|
||||
return !isNaN(date.getTime());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.input-date-picker::-webkit-calendar-picker-indicator {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { getConfig } from "@common/config";
|
||||
import { getParentIds } from "./lib";
|
||||
import {
|
||||
Sidebar,
|
||||
@@ -22,7 +23,17 @@ import {
|
||||
} from "../collapsible";
|
||||
import { ENDPOINT } from "../../config";
|
||||
import { cn, getCookie, setCookie } from "../../lib";
|
||||
import { getMenuList, getPreviewTreeMenuList, type MenuItem } from "../../api";
|
||||
import {
|
||||
getAnonymouseMenuList,
|
||||
getMenuList,
|
||||
getPreviewTreeMenuList,
|
||||
type MenuItem,
|
||||
} from "../../api";
|
||||
|
||||
const appConfig = getConfig();
|
||||
const appSiteId = appConfig.SITE_ID ?? "";
|
||||
const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
|
||||
const isLoggedIn = !!userId;
|
||||
|
||||
export function LNB({
|
||||
viewOnly,
|
||||
@@ -42,24 +53,34 @@ export function LNB({
|
||||
handleMenuClick?: (item: MenuItem) => void;
|
||||
}) {
|
||||
const [defaultOpenMenuIds, setDefaultOpenMenuIds] = useState<number[]>([]);
|
||||
const [currentMenuCode, setCurrentMenuCode] = useState<number | null>(null);
|
||||
const prevPathRef = useRef(currentPath);
|
||||
|
||||
const cookieSiteId = getCookie("site-id-key") ?? "";
|
||||
const siteId = propSiteId ?? cookieSiteId;
|
||||
const siteId = propSiteId ?? appSiteId;
|
||||
|
||||
const { data: menuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(siteId),
|
||||
enabled: !viewOnly && !previewMode,
|
||||
enabled: isLoggedIn && !viewOnly && !previewMode,
|
||||
});
|
||||
|
||||
const { data: previewTreeMenuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId, "preview"],
|
||||
queryFn: () => getPreviewTreeMenuList(siteId),
|
||||
enabled: previewMode,
|
||||
enabled: isLoggedIn && previewMode,
|
||||
});
|
||||
|
||||
const menuList = previewMode ? previewTreeMenuListData : menuListData;
|
||||
const { data: anonymousMenuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS, siteId],
|
||||
queryFn: () => getAnonymouseMenuList(siteId),
|
||||
enabled: !isLoggedIn,
|
||||
});
|
||||
|
||||
const menuList = isLoggedIn
|
||||
? previewMode
|
||||
? previewTreeMenuListData
|
||||
: menuListData
|
||||
: anonymousMenuListData;
|
||||
|
||||
function isPathMatched(path: string) {
|
||||
if (!path) return false;
|
||||
@@ -70,6 +91,7 @@ export function LNB({
|
||||
if (!menuList || !currentPath) return;
|
||||
|
||||
const matchedMenuIds: number[] = [];
|
||||
let matchedMenuCode: number | null = null;
|
||||
|
||||
const findMatchedMenus = (items: MenuItem[]) => {
|
||||
items.forEach((item) => {
|
||||
@@ -77,25 +99,32 @@ export function LNB({
|
||||
const parentIds = getParentIds(item.menuCd)(menuList);
|
||||
matchedMenuIds.push(...parentIds, item.menuCd);
|
||||
}
|
||||
if (item.children) findMatchedMenus(item.children);
|
||||
});
|
||||
};
|
||||
|
||||
if (item.children) {
|
||||
findMatchedMenus(item.children);
|
||||
}
|
||||
const findMatchedMenuCode = (items: MenuItem[]) => {
|
||||
items.forEach((item) => {
|
||||
if (matchedMenuCode) return;
|
||||
if (currentPath === item.urlPath) matchedMenuCode = item.menuCd;
|
||||
if (item.children) findMatchedMenuCode(item.children);
|
||||
});
|
||||
};
|
||||
|
||||
findMatchedMenus(menuList);
|
||||
findMatchedMenuCode(menuList);
|
||||
|
||||
if (matchedMenuIds.length > 0) {
|
||||
if (matchedMenuIds.length > 0)
|
||||
setDefaultOpenMenuIds((prev) => [
|
||||
...new Set([...prev, ...matchedMenuIds]),
|
||||
]);
|
||||
}
|
||||
|
||||
if (prevPathRef.current !== currentPath) {
|
||||
setDefaultOpenMenuIds(matchedMenuIds);
|
||||
prevPathRef.current = currentPath;
|
||||
}
|
||||
|
||||
if (matchedMenuCode) setCurrentMenuCode(matchedMenuCode);
|
||||
}, [menuList, currentPath]);
|
||||
|
||||
function MenuItem({
|
||||
@@ -106,7 +135,8 @@ export function LNB({
|
||||
isSubMenu?: boolean;
|
||||
}) {
|
||||
const { open: isSidebarOpen, setOpen } = useSidebar();
|
||||
const isMatched = isPathMatched(item.urlPath);
|
||||
const isMatched =
|
||||
isPathMatched(item.urlPath) && currentMenuCode === item.menuCd;
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isSidebarOpen) setOpen(true);
|
||||
|
||||
@@ -31,7 +31,7 @@ const TableHeader = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<thead
|
||||
ref={ref}
|
||||
className={cn("sticky -top-3 z-10 bg-secondary [&_tr]:border-b", className)}
|
||||
className={cn("sticky top-0 z-10 bg-secondary [&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
Generated
+10836
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user