This commit is contained in:
yschoi
2025-07-24 11:13:44 +09:00
138 changed files with 14895 additions and 2197 deletions
+33 -21
View File
@@ -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();
+49
View File
@@ -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 };
+37 -24
View File
@@ -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]"
+1
View File
@@ -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>
+33 -25
View File
@@ -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}
+32 -19
View File
@@ -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">
+1
View File
@@ -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",
+19
View File
@@ -0,0 +1,19 @@
NEXT_PUBLIC_SITE_ID = DM
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
# NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
+19
View File
@@ -0,0 +1,19 @@
NEXT_PUBLIC_SITE_ID = DM
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
# NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
+17
View File
@@ -0,0 +1,17 @@
NEXT_PUBLIC_SITE_ID = DM
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://wr.hasuon.or.kr
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/api/v1
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/operation-service/api/v1
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
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_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://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
-2
View File
@@ -1,7 +1,6 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
@@ -31,7 +30,6 @@ yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
+22
View File
@@ -0,0 +1,22 @@
"use client";
import { BaseLayout } from "@common/components";
import type { MenuItem } from "./store/common";
import { useDmStore } from "./store";
import { useRouter } from "next/navigation";
export default function Base({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { getMenuAuth } = useDmStore((state) => state);
const router = useRouter();
const handleClick = (id:MenuItem) =>{
getMenuAuth(id.menuCd);
router.push(id.urlPath);
}
return <BaseLayout handleMenuClick={handleClick}>{children}</BaseLayout>;
}
+4 -7
View File
@@ -1,6 +1,5 @@
"use client";
import { BaseLayout } from "@common/components";
"use client"
import Base from "./base";
import "./globals.css";
import { StoreProvider } from "./store";
@@ -13,11 +12,9 @@ export default function RootLayout({
<html lang="en">
<body>
<StoreProvider>
<BaseLayout>
{children}
</BaseLayout>
<Base>{children}</Base>
</StoreProvider>
</body>
</html>
);
}
}
@@ -52,7 +52,7 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }:
return spans;
}, [similarCaseList]);
const handleCellClick = (index: number, headId: string) => {
const handleCellClick = (index: number) => {
setMoneyDetailList([{
reqBizGrssCst: similarCaseList[index]?.reqBizGrssCst ?? '',
reqBizReuseFcltCst: similarCaseList[index]?.reqBizReuseFcltCst ?? '',
@@ -124,10 +124,10 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }:
key={`similar-case-cell-${head.id}-${index}-${el.ruseBizNm}`}
rowSpan={(head.id === 'businessName' || head.id ==='businessDuration' || head.id === 'money') ? rowSpans[index] : undefined}
style={(head.id === 'businessName' || head.id === 'businessDuration' || head.id === 'money') && index > 0 && similarCaseList[index - 1]?.ruseBizNm === el.ruseBizNm ? { display: 'none' } : undefined}
onClick={() => handleCellClick(index, head.id)}
onClick={() => handleCellClick(index)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleCellClick(index, head.id);
handleCellClick(index);
}
}}
>
@@ -9,7 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useSearchParams } from "next/navigation";
import { Suspense, useEffect, useState } from "react";
import { FormProvider, useForm, type SubmitHandler } from "react-hook-form";
import { Toaster, toast } from "react-hot-toast";
import { Toaster } from "react-hot-toast";
import * as z from "zod";
import SdmLogin from "../sdmLogin/page";
+4 -13
View File
@@ -1,8 +1,7 @@
'use client';
import React, { useState, useEffect, useRef } from 'react';
import Image from 'next/image';
import { CHART_FORM, MainChart, type ChartConfig } from '@common/business_components';
import React, { useEffect, useRef, useState } from 'react';
interface RegionData {
name: string;
@@ -22,12 +21,10 @@ interface TableData {
const WrStatusPage: React.FC = () => {
const [selectedRegion, setSelectedRegion] = useState<string | null>(null);
const [originalFills, setOriginalFills] = useState<Record<string, string>>({});
const [hoveredRegion, setHoveredRegion] = useState<string | null>(null);
const [tooltipPosition, setTooltipPosition] = useState<{ x: number; y: number } | null>(null);
const [favorites, setFavorites] = useState<Record<string, boolean>>({});
const svgContainerRef = useRef<HTMLDivElement>(null);
const [chartData, setChartData] = useState<any[]>([
const [chartData] = useState<any[]>([
{
label : '서울',
하수 : 4,
@@ -244,12 +241,6 @@ const WrStatusPage: React.FC = () => {
}
};
const handleFavoriteClick = (region: string) => {
setFavorites(prev => ({
...prev,
[region]: !prev[region]
}));
};
const createDonutMarker = (svg: SVGSVGElement, x: number, y: number, name: string, values: number[]) => {
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FED16A'];
@@ -395,13 +386,13 @@ const WrStatusPage: React.FC = () => {
if (selectedRegion === pathId) {
path.setAttribute('fill', '#2563eb');
} else {
const original = originalFills[pathId] || '#ffffff';
const original = '#ffffff';
path.setAttribute('fill', original);
}
}
});
}
}, [selectedRegion, originalFills]);
}, [selectedRegion]);
return (
<div className="flex h-screen bg-[#F4F7FB] text-gray-800">
+39
View File
@@ -14,6 +14,26 @@ export type SelectDataType = {
val: string;
}
export interface MenuItem {
menuCd: number;
menuKornNm: string;
menuEngNm: string;
parentId: number | null;
sortSeq: number;
iconNm?: string;
children: MenuItem[] | null;
levelNo: number;
urlPath: string;
menuLnkgTypeCd: string;
menuLnkgSn: number;
menuPath: string;
qrcd: string;
useYn: string;
otptYn: string;
newWndwYn: string;
requiredUrlPath: boolean;
}
// grid header
export type TableUpperProps = {
id: string;
@@ -312,6 +332,7 @@ const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
====================================*/
export type CommonAction = {
initializeCommonData: () => Promise<void>;
getMenuAuth: (menuCd:number) => Promise<void>;
}
export type CommonType = {
@@ -358,6 +379,24 @@ export const CommonState:CommonType = {
export const CommonReducer:(set:any)=>CommonAction=(set: any) => {
return {
getMenuAuth : async (menuCd : number) =>{
const response = await getFetch(
"local",
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
set((state: CommonType) => {
return {
...state,
auth: {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
},
};
});
},
// 시도 변경 이벤트
initializeCommonData: async () => {
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
-2
View File
@@ -253,8 +253,6 @@ export const upHeadSdmInfoStatus: TableUpperProps[] = [
]
// 수요정보 신청현황 데이터
const sdmInfoStatusList : DemandCompType[] = [
]
/**====================================
* Store - Api
+4 -12
View File
@@ -1,13 +1,5 @@
const LOCAL_PORT = "3000";
const DEV_PORT = "11080";
const DEV_PORT_FO = "11082";
import { PROXY_HOST, SERVER_API_FO_URL, SERVER_API_WR_URL } from "@/src/shared/config/env";
export const LOCAL_URL_WR = `http://localhost:${LOCAL_PORT}/api/v1`;
export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT}/api/v1`;
//export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`;
export const LOCAL_URL_FO = `http://192.168.1.129:${DEV_PORT_FO}/api/v1`;
export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`;
export const CS_URL= 'http://dev.onasys.co.kr:8000'
export const CS_TUS_UPLOAD= "/attach-service/api/v1/tus/upload";
export const LOCAL_URL = PROXY_HOST;
export const DEV_URL_FO = SERVER_API_FO_URL;
export const DEV_URL_WR = SERVER_API_WR_URL;
@@ -12,7 +12,6 @@ interface AccessibleFormFieldProps<T extends FieldValues> {
}
export function AccessibleFormField<T extends FieldValues>({
field,
label,
required = false,
error,
+4
View File
@@ -11,6 +11,10 @@ const compat = new FlatCompat({
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
{rules:{
// other rules..
"@typescript-eslint/no-explicit-any": "off",
}},
];
export default eslintConfig;
+1 -2
View File
@@ -1,10 +1,9 @@
import { NextRequest, NextResponse } from "next/server";
import { CS_URL } from "./components/ui/config";
import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
// const CLAIM_NAME = "Authorization";
// const AUTH_USER_ID = "token-id";
const serverApiUrl = CS_URL;
const serverApiUrl = process.env.NEXT_PUBLIC_CS_SERVER_API_URL;
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
+5
View File
@@ -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.
+26 -34
View File
@@ -1,41 +1,33 @@
import type { NextConfig } from "next";
import webpack from "webpack";
const subDomain =
process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:3000";
const nextConfig: NextConfig = {
output: "standalone",
eslint: {
ignoreDuringBuilds: true,
},
webpack: (config) => {
config.plugins.push(
new webpack.DefinePlugin({
"import.meta.env.VITE_PORT": JSON.stringify(
process.env.NEXT_PUBLIC_PORT
),
"import.meta.env.VITE_PROXY_HOST": JSON.stringify(
process.env.NEXT_PUBLIC_PROXY_HOST
),
"import.meta.env.VITE_SERVER_API_URL": JSON.stringify(
process.env.NEXT_PUBLIC_SERVER_API_URL
),
"import.meta.env.VITE_SITE_ID": JSON.stringify(process.env.SITE_ID),
"import.meta.env.VITE_CLAIM_NAME": JSON.stringify(
process.env.NEXT_PUBLIC_CLAIM_NAME
),
"import.meta.env.VITE_ACCESS_TOKEN": JSON.stringify(
process.env.NEXT_PUBLIC_ACCESS_TOKEN
),
"import.meta.env.VITE_REFRESH_TOKEN": JSON.stringify(
process.env.NEXT_PUBLIC_REFRESH_TOKEN
),
"import.meta.env.VITE_AUTH_USER_ID": JSON.stringify(
process.env.NEXT_PUBLIC_AUTH_USER_ID
),
"process.env.SITE_ID": JSON.stringify(process.env.NEXT_PUBLIC_SITE_ID),
})
);
return config;
async rewrites() {
return {
beforeFiles: [
{
source: "/_next/static/:path*",
destination: `${subDomain}/_next/static/:path*`,
has: [
{
type: "header",
key: "referer",
value: "(.*)/pst(.*)",
},
],
},
],
afterFiles: [
{
source: "/pst/:path*",
destination: `${subDomain}/pst/:path*`,
},
],
fallback: [],
};
},
};
+2
View File
@@ -9,11 +9,13 @@
"lint": "next lint"
},
"dependencies": {
"@common/assets": "workspace:*",
"@common/business_components": "workspace:*",
"@common/components": "workspace:*",
"@hookform/resolvers": "^4.1.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"js-cookie": "^3.0.5",
"js-sha512": "^0.9.0",
"lucide-react": "^0.474.0",
"next": "15.1.7",
@@ -1,4 +1,3 @@
import type { LoginService, ILogin } from "../model/login-interface";
import { tokenManager } from "../lib/token-manager";
import {PROXY_HOST, ACCESS_TOKEN, AUTH_USER_ID,SITE_ID, CLAIM_NAME, ENDPOINT, JWT_EXPIRATION_TIME } from "../../../shared/config";
+1 -1
View File
@@ -1,2 +1,2 @@
export * from "./api";
export * from "./env";
export * from "./env";
-1
View File
@@ -1,4 +1,3 @@
import Cookies from "js-cookie";
+1
View File
@@ -2,6 +2,7 @@
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"noImplicitAny": false,
"baseUrl": ".",
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
+5 -1
View File
@@ -1,7 +1,9 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 11300
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_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
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
@@ -10,3 +12,5 @@ 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
+7 -4
View File
@@ -1,7 +1,9 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_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
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
@@ -10,4 +12,5 @@ NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 PC 서버 API
+22 -6
View File
@@ -1,12 +1,28 @@
# NEXT_PUBLIC_SITE_ID = WR
# NEXT_PUBLIC_PORT = 11301
# NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11301
# NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
# NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
# NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
# NEXT_PUBLIC_CLAIM_NAME = Authorization
# NEXT_PUBLIC_AUTH_USER_ID = token-id
# NEXT_PUBLIC_ACCESS_TOKEN = access-token
# NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://fo.hasuon.or.kr
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000
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_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
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
NEXT_PUBLIC_SEWERAGE_API_URL= http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api
+22
View File
@@ -0,0 +1,22 @@
"use client";
import { BaseLayout } from "@common/components";
import type { MenuItem } from "./store/common";
import { useFoStore } from "./store";
import { useRouter } from "next/navigation";
export default function Base({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const { getMenuAuth } = useFoStore((state) => state);
const router = useRouter();
const handleClick = (id:MenuItem) =>{
getMenuAuth(id.menuCd);
router.push(id.urlPath);
}
return <BaseLayout handleMenuClick={handleClick}>{children}</BaseLayout>;
}
+4 -11
View File
@@ -1,12 +1,5 @@
const DEV_PORT_COMMON = "11081";
const DEV_PORT_FO = "11082";
const DEV_PORT_WR = "11080";
import { PROXY_HOST, SERVER_API_URL, SERVER_API_WR_URL } from "@/src/shared/config/env";
export const LOCAL_URL_COMMON = `http://192.168.1.129:${DEV_PORT_COMMON}/api/v1`;
export const DEV_URL_COMMON = `http://con.mind-one.co.kr:${DEV_PORT_COMMON}/api/v1`;
export const LOCAL_URL_FO = `http://192.168.1.129:${DEV_PORT_FO}/api/v1`;
export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`;
export const LOCAL_URL_WR = `http://192.168.1.129:${DEV_PORT_WR}/api/v1`;
export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT_WR}/api/v1`;
export const DEV_URL_FO = SERVER_API_URL;
export const LOCAL_URL = PROXY_HOST;
export const DEV_URL_WR = SERVER_API_WR_URL;
+5 -5
View File
@@ -1,8 +1,8 @@
import { DEV_URL_COMMON, DEV_URL_FO, DEV_URL_WR } from "./config/env";
import { DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "./config/env";
export const getFetch = async (part : string, url: string) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : part === "wr" ? DEV_URL_WR+url : DEV_URL_COMMON+url;
reqUrl = part === "fo" ? DEV_URL_FO+url : part === 'local' ? LOCAL_URL+url : DEV_URL_WR+url;
const response = await fetch(reqUrl , {
method: "GET",
headers: {
@@ -32,7 +32,7 @@ export const getFetchAreaList = async (url: string) => {
export const postFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_WR+url;
const response = await fetch(reqUrl, {
method: "POST",
headers: {
@@ -48,7 +48,7 @@ export const postFetch = async (part : string, url: string, params: unknown) =>
export const putFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_WR+url;
const response = await fetch(reqUrl, {
method: "PUT",
headers: {
@@ -64,7 +64,7 @@ export const putFetch = async (part : string, url: string, params: unknown) => {
export const deleteFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_WR+url;
const response = await fetch(reqUrl, {
method: "DELETE",
headers: {
+2 -6
View File
@@ -1,10 +1,8 @@
"use client"
import { BaseLayout } from "@common/components/ui";
import Base from "./base";
import "./globals.css";
import { StoreProvider } from "./store";
export default function RootLayout({
children,
}: Readonly<{
@@ -14,9 +12,7 @@ export default function RootLayout({
<html lang="en">
<body>
<StoreProvider>
<BaseLayout>
{children}
</BaseLayout>
<Base>{children}</Base>
</StoreProvider>
</body>
</html>
+2 -2
View File
@@ -51,8 +51,8 @@ export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm
console.log('bscFctNm : ', bscFctNm);
return (
<>
<div className="z-50">
<SearchInput label={'시설명'} setValue={fncNextPage} sidoSigun={sidoSigun} fcltyName={bscFctNm}/>
</>
</div>
);
}
+79 -10
View File
@@ -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
@@ -63,6 +65,26 @@ export type envPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
export interface MenuItem {
menuCd: number;
menuKornNm: string;
menuEngNm: string;
parentId: number | null;
sortSeq: number;
iconNm?: string;
children: MenuItem[] | null;
levelNo: number;
urlPath: string;
menuLnkgTypeCd: string;
menuLnkgSn: number;
menuPath: string;
qrcd: string;
useYn: string;
otptYn: string;
newWndwYn: string;
requiredUrlPath: boolean;
}
/**
* STORE - DATA
*/
@@ -305,6 +327,7 @@ const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
* Store - expaort
====================================*/
export type CommonAction = {
getMenuAuth : (menuCd : number) => Promise<void>;
initializeCommonData: () => Promise<void>;
}
@@ -326,10 +349,18 @@ export type CommonType = {
selectFlowPanel: SelectDataType[];
selectEnvPartData: envPartSelectDataType[];
btnClass: string;
}
}
};
export const commonState:CommonType = {
auth: {
mngAuthrtYn: string;
inqAuthrtYn: string;
regAuthrtYn: string;
mdfcnAuthrtYn: string;
delAuthrtYn: string;
};
};
export const commonState: CommonType = {
Common: {
isInit: false,
selectSidoData: selectSidoData,
@@ -341,22 +372,60 @@ export const commonState:CommonType = {
selectRegionData: selectRegionData,
selectUnitWaterShedData: selectUnitWaterShedData,
selectMiddleRegionData: selectMiddleRegionData,
selectEnvPartSidoData : selectEnvPartSidoData,
selectEnvPartSigunData : selectEnvPartSigunData,
selectEnvPartSidoData: selectEnvPartSidoData,
selectEnvPartSigunData: selectEnvPartSigunData,
selectFlowTerm: selectFlowTerm,
selectFlowPanel: selectFlowPanel,
selectEnvPartData : selectEnvPartData,
btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium"
}
}
selectEnvPartData: selectEnvPartData,
btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium",
},
auth: {
mngAuthrtYn: "N",
inqAuthrtYn: "N",
regAuthrtYn: "N",
mdfcnAuthrtYn: "N",
delAuthrtYn: "N",
},
};
export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)=>CommonAction=(set: (fn: (state: CommonType) => CommonType) => void) => {
return {
// 메뉴 권한 확인
getMenuAuth : async (menuCd : number) =>{
const response = await getFetch(
"local",
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
set((state: CommonType) => {
return {
...state,
auth: {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
},
};
});
},
// 시도 변경 이벤트
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) => ({
...state,
Common: {
...state.Common,
selectSidoData: sidoData,
@@ -371,4 +440,4 @@ export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)
}
}
export type CommonStore = CommonType & CommonAction;
export type CommonStore = CommonType & CommonAction;
+80 -285
View File
@@ -356,7 +356,7 @@ const upHeadListRainManage : TableUpperProps[] = [
{ id: "A", title: "유입(㎥/일)", row:0, colSpan:2 },
{ id: "inflAmnt", title: "유입유량", row:1 },
{ id: "fctCpc", title: "시설용량대비", row:1 },
{ id: "B", title: "설계유입수질(㎎/L)", row:0, colSpan:5 },
{ id: "B", title: "설계유입수질(㎎/L)", row:0, colSpan:5 },
{ id: "dsInflWqltBod", title: "BOD", row:1 },
{ id: "dsInflWqltCod", title: "TOC\n(COD)", row:1 },
{ id: "dsInflWqltSs", title: "SS", row:1 },
@@ -896,6 +896,49 @@ export const dataFormSetting=(item : OperationalStatusListProps)=> {
DataForm({ type: 'float2', column: 'rainRate', item: item as unknown as Record<string, unknown>});
};
const changeValue=(operationalStatus, data, keyTxt = 'INFL_AMNT')=>{
operationalStatus.yearAvg = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['AVG_'+keyTxt] : '';
operationalStatus.yearMax = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['MAX_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['MAX_'+keyTxt] : '';
operationalStatus.yearMin = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['MIN_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['MIN_'+keyTxt] : '';
// 여름
operationalStatus.summerAll = data.summer ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summer?Number(Math.round(data.summer['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summer['AVG_'+keyTxt] : '';
operationalStatus.summerThree = data.summerRainy ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summerRainy?Number(Math.round(data.summerRainy["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summerRainy["AVG_" + keyTxt] : '';
operationalStatus.summerRain = data.summerSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summerSunny?Number(Math.round(data.summerSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summerSunny["AVG_" + keyTxt] : '';
// 겨울
operationalStatus.winterAll = data.winter ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winter?Number(Math.round(data.winter["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winter["AVG_" + keyTxt] : '';
operationalStatus.winterSunnyMin = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["MIN_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["MIN_" + keyTxt] : '';
operationalStatus.winterSunnyAvg = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["AVG_" + keyTxt] : '';
operationalStatus.winterSunnyMax = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["MAX_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["MAX_" + keyTxt] : '';
// 지하수
operationalStatus.groundWaterAvg = 10;
operationalStatus.groundWaterSummer = 11;
operationalStatus.groundWaterWinter = 12;
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod && data.summerBod.INFL_AMNT && data.summerBod.INFL_AMNT != 'null' ? Number(data.summerBod.INFL_AMNT) : 0;
const winterAvgBod = data.winterSunny && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD) : 0;
const summerLowBod = data.summerBod && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD) : 0;
const summerLowRain = data.summerBod && data.summerBod.INFO_RN_DAY && data.summerBod.INFO_RN_DAY != 'null' ? Number(data.summerBod.INFO_RN_DAY) : 0;
operationalStatus.rdi = winterAvgBod === 0 ? 0 : summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatus.rdiRain = summerLowRain;
}
const calcValue = (operationalStatus, data, keyTxt='INFL_ASSCT_BFR_WQLT_TP') => {
// 연간
operationalStatus.yearAvg = data.yearData ? Number(Math.round(data.yearData['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.yearMax = data.yearData ? Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.yearMin = data.yearData ? Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
// 여름
operationalStatus.summerAll = data.summer ? Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.summerThree = data.summerRainy ? Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.summerRain = data.summerSunny ? Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
// 겨울
operationalStatus.winterAll = data.winter ? Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.winterSunnyMin = data.winterSunny ? Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.winterSunnyAvg = data.winterSunny ? Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
operationalStatus.winterSunnyMax = data.winterSunny ? Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString() : '';
}
export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) => void)=>DashBoardActions=(set: (fn: (state: DashBoardType) => DashBoardType) => void) => {
return {
initializeDashBoardData: async () => {
@@ -969,18 +1012,33 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
// 유량 - 유입
if (operationalStatusList?.[0]) {
// 연간
operationalStatusList[0].yearAvg = data.yearData===null?'':data.yearData.AVG_INFL_AMNT?data.yearData.AVG_INFL_AMNT:0+data.yearData.AVG_SUM_FLOW?data.yearData.AVG_SUM_FLOW:0+data.yearData.AVG_TP_INFL_WQLT_TP?data.yearData.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].yearMax = data.yearData===null?'':data.yearData.MAX_INFL_AMNT?data.yearData.MAX_INFL_AMNT:0+data.yearData.MAX_SUM_FLOW?data.yearData.MAX_SUM_FLOW:0+data.yearData.MAX_TP_INFL_WQLT_TP?data.yearData.MAX_TP_INFL_WQLT_TP:0;
operationalStatusList[0].yearMin = data.yearData===null?'':data.yearData.MIN_INFL_AMNT?data.yearData.MIN_INFL_AMNT:0+data.yearData.MIN_SUM_FLOW?data.yearData.MIN_SUM_FLOW:0+data.yearData.MIN_TP_INFL_WQLT_TP?data.yearData.MIN_TP_INFL_WQLT_TP:0;
const yearAvg = data.yearData===null?'':(data.yearData.AVG_INFL_AMNT?data.yearData.AVG_INFL_AMNT:0)+(data.yearData.AVG_SUM_FLOW?data.yearData.AVG_SUM_FLOW:0)+(data.yearData.AVG_RETURN_AMNT?data.yearData.AVG_RETURN_AMNT:0);
const yearMax = data.yearData===null?'':(data.yearData.MAX_INFL_AMNT?data.yearData.MAX_INFL_AMNT:0)+(data.yearData.MAX_SUM_FLOW?data.yearData.MAX_SUM_FLOW:0)+(data.yearData.MAX_RETURN_AMNT?data.yearData.MAX_RETURN_AMNT:0);
const yearMin = data.yearData===null?'':(data.yearData.MIN_INFL_AMNT?data.yearData.MIN_INFL_AMNT:0)+(data.yearData.MIN_SUM_FLOW?data.yearData.MIN_SUM_FLOW:0)+(data.yearData.MIN_RETURN_AMNT?data.yearData.MIN_RETURN_AMNT:0);
operationalStatusList[0].yearAvg = yearAvg;
operationalStatusList[0].yearMax = yearMax;
operationalStatusList[0].yearMin = yearMin;
// 여름
operationalStatusList[0].summerAll = data.summer===null?'':data.summer.AVG_INFL_AMNT?data.summer.AVG_INFL_AMNT:0+data.summer.AVG_SUM_FLOW?data.summer.AVG_SUM_FLOW:0+data.summer.AVG_TP_INFL_WQLT_TP?data.summer.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].summerThree = data.summerRainy===null?'':data.summerRainy.AVG_INFL_AMNT?data.summerRainy.AVG_INFL_AMNT:0+data.summerRainy.AVG_SUM_FLOW?data.summerRainy.AVG_SUM_FLOW:0+data.summerRainy.AVG_TP_INFL_WQLT_TP?data.summerRainy.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].summerRain = data.summerSunny===null?'':data.summerSunny.AVG_INFL_AMNT?data.summerSunny.AVG_INFL_AMNT:0+data.summerSunny.AVG_SUM_FLOW?data.summerSunny.AVG_SUM_FLOW:0+data.summerSunny.AVG_TP_INFL_WQLT_TP?data.summerSunny.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].summerAll = data.summer === null ? '' : (
(data.summer.AVG_INFL_AMNT ? data.summer.AVG_INFL_AMNT : 0) +
(data.summer.AVG_SUM_FLOW ? data.summer.AVG_SUM_FLOW : 0) +
(data.summer.AVG_TP_INFL_WQLT_TP ? data.summer.AVG_TP_INFL_WQLT_TP : 0)
);
operationalStatusList[0].summerThree = data.summerRainy === null ? '' : (
(data.summerRainy.AVG_INFL_AMNT ? data.summerRainy.AVG_INFL_AMNT : 0) +
(data.summerRainy.AVG_SUM_FLOW ? data.summerRainy.AVG_SUM_FLOW : 0) +
(data.summerRainy.AVG_TP_INFL_WQLT_TP ? data.summerRainy.AVG_TP_INFL_WQLT_TP : 0)
);
operationalStatusList[0].summerRain = data.summerSunny === null ? '' : (
(data.summerSunny.AVG_INFL_AMNT ? data.summerSunny.AVG_INFL_AMNT : 0) +
(data.summerSunny.AVG_SUM_FLOW ? data.summerSunny.AVG_SUM_FLOW : 0) +
(data.summerSunny.AVG_TP_INFL_WQLT_TP ? data.summerSunny.AVG_TP_INFL_WQLT_TP : 0)
);
// 겨울
operationalStatusList[0].winterAll = data.winter===null?'':data.winter.AVG_INFL_AMNT?data.winter.AVG_INFL_AMNT:0+data.winter.AVG_SUM_FLOW?data.winter.AVG_SUM_FLOW:0+data.winter.AVG_TP_INFL_WQLT_TP?data.winter.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].winterSunnyMin = data.winterSunny===null?'':data.winterSunny.MIN_INFL_AMNT?data.winterSunny.MIN_INFL_AMNT:0+data.winterSunny.MIN_SUM_FLOW?data.winterSunny.MIN_SUM_FLOW:0+data.winterSunny.MIN_TP_INFL_WQLT_TP?data.winterSunny.MIN_TP_INFL_WQLT_TP:0;
operationalStatusList[0].winterSunnyAvg = data.winterSunny===null?'':data.winterSunny.AVG_INFL_AMNT?data.winterSunny.AVG_INFL_AMNT:0+data.winterSunny.AVG_SUM_FLOW?data.winterSunny.AVG_SUM_FLOW:0+data.winterSunny.AVG_TP_INFL_WQLT_TP?data.winterSunny.AVG_TP_INFL_WQLT_TP:0;
operationalStatusList[0].winterSunnyMax = data.winterSunny===null?'':data.winterSunny.MAX_INFL_AMNT?data.winterSunny.MAX_INFL_AMNT:0+data.winterSunny.MAX_SUM_FLOW?data.winterSunny.MAX_SUM_FLOW:0+data.winterSunny.MAX_TP_INFL_WQLT_TP?data.winterSunny.MAX_TP_INFL_WQLT_TP:0;
operationalStatusList[0].winterAll = data.winter === null ? '' : (data.winter.AVG_INFL_AMNT ? data.winter.AVG_INFL_AMNT : 0) + (data.winter.AVG_SUM_FLOW ? data.winter.AVG_SUM_FLOW : 0) + (data.winter.AVG_TP_INFL_WQLT_TP ? data.winter.AVG_TP_INFL_WQLT_TP : 0);
operationalStatusList[0].winterSunnyMin = data.winterSunny === null ? '' : (data.winterSunny.MIN_INFL_AMNT ? data.winterSunny.MIN_INFL_AMNT : 0) + (data.winterSunny.MIN_SUM_FLOW ? data.winterSunny.MIN_SUM_FLOW : 0) + (data.winterSunny.MIN_TP_INFL_WQLT_TP ? data.winterSunny.MIN_TP_INFL_WQLT_TP : 0);
operationalStatusList[0].winterSunnyAvg = data.winterSunny === null ? '' : (data.winterSunny.AVG_INFL_AMNT ? data.winterSunny.AVG_INFL_AMNT : 0) + (data.winterSunny.AVG_SUM_FLOW ? data.winterSunny.AVG_SUM_FLOW : 0) + (data.winterSunny.AVG_TP_INFL_WQLT_TP ? data.winterSunny.AVG_TP_INFL_WQLT_TP : 0);
operationalStatusList[0].winterSunnyMax = data.winterSunny === null ? '' : (data.winterSunny.MAX_INFL_AMNT ? data.winterSunny.MAX_INFL_AMNT : 0) + (data.winterSunny.MAX_SUM_FLOW ? data.winterSunny.MAX_SUM_FLOW : 0) + (data.winterSunny.MAX_TP_INFL_WQLT_TP ? data.winterSunny.MAX_TP_INFL_WQLT_TP : 0);
// 지하수
operationalStatusList[0].groundWaterAvg = 10;
operationalStatusList[0].groundWaterSummer = 11;
@@ -996,280 +1054,16 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
dataFormSetting(operationalStatusList[0]);
}
// 유량 - 순수하수
if (operationalStatusList?.[1]) {
// 연간
operationalStatusList[1].yearAvg = data.yearData?data.yearData.AVG_INFL_AMNT:'';
operationalStatusList[1].yearMax = data.yearData?data.yearData.MAX_INFL_AMNT:'';
operationalStatusList[1].yearMin = data.yearData?data.yearData.MIN_INFL_AMNT:'';
// 여름
operationalStatusList[1].summerAll = data.summer?data.summer.AVG_INFL_AMNT:'';
operationalStatusList[1].summerThree = data.summerRainy?data.summerRainy.AVG_INFL_AMNT:'';
operationalStatusList[1].summerRain = data.summerSunny?data.summerSunny.AVG_INFL_AMNT:'';
// 겨울
operationalStatusList[1].winterAll = data.winter?data.winter.AVG_INFL_AMNT:'';
operationalStatusList[1].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_INFL_AMNT:'';
operationalStatusList[1].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_INFL_AMNT:'';
operationalStatusList[1].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_INFL_AMNT:'';
// 지하수
operationalStatusList[1].groundWaterAvg = 10;
operationalStatusList[1].groundWaterSummer = 11;
operationalStatusList[1].groundWaterWinter = 12;
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0;
operationalStatusList[1].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatusList[1].rdiRain = summerLowRain;
dataFormSetting(operationalStatusList[1]);
}
// 유량 - 연계처리수
if (operationalStatusList?.[2]) {
// 연간
operationalStatusList[2].yearAvg = data.yearData?data.yearData.AVG_SUM_FLOW:'';
operationalStatusList[2].yearMax = data.yearData?data.yearData.MAX_SUM_FLOW:'';
operationalStatusList[2].yearMin = data.yearData?data.yearData.MIN_SUM_FLOW:'';
// 여름
operationalStatusList[2].summerAll = data.summer?data.summer.AVG_SUM_FLOW:'';
operationalStatusList[2].summerThree = data.summerRainy?data.summerRainy.AVG_SUM_FLOW:'';
operationalStatusList[2].summerRain = data.summerSunny?data.summerSunny.AVG_SUM_FLOW:'';
// 겨울
operationalStatusList[2].winterAll = data.winter?data.winter.AVG_SUM_FLOW:'';
operationalStatusList[2].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_SUM_FLOW:'';
operationalStatusList[2].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_SUM_FLOW:'';
operationalStatusList[2].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_SUM_FLOW:'';
// 지하수
operationalStatusList[2].groundWaterAvg = 10;
operationalStatusList[2].groundWaterSummer = 11;
operationalStatusList[2].groundWaterWinter = 12;
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0;
operationalStatusList[2].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatusList[2].rdiRain = summerLowRain;
dataFormSetting(operationalStatusList[2]);
}
// 유량 - 총인설비방류수
if (operationalStatusList?.[3]) {
// 연간
operationalStatusList[3].yearAvg = data.yearData?data.yearData.AVG_TP_INFL_WQLT_TP:'';
operationalStatusList[3].yearMax = data.yearData?data.yearData.MAX_TP_INFL_WQLT_TP:'';
operationalStatusList[3].yearMin = data.yearData?data.yearData.MIN_TP_INFL_WQLT_TP:'';
// 여름
operationalStatusList[3].summerAll = data.summer?data.summer.AVG_TP_INFL_WQLT_TP:'';
operationalStatusList[3].summerThree = data.summerRainy?data.summerRainy.AVG_TP_INFL_WQLT_TP:'';
operationalStatusList[3].summerRain = data.summerSunny?data.summerSunny.AVG_TP_INFL_WQLT_TP:'';
// 겨울
operationalStatusList[3].winterAll = data.winter?data.winter.AVG_TP_INFL_WQLT_TP:'';
operationalStatusList[3].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_TP_INFL_WQLT_TP:'';
operationalStatusList[3].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_TP_INFL_WQLT_TP:'';
operationalStatusList[3].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_TP_INFL_WQLT_TP:'';
// 지하수
operationalStatusList[3].groundWaterAvg = 10;
operationalStatusList[3].groundWaterSummer = 11;
operationalStatusList[3].groundWaterWinter = 12;
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0;
operationalStatusList[3].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatusList[3].rdiRain = summerLowRain;
dataFormSetting(operationalStatusList[3]);
}
// 유량 - 방류수
if (operationalStatusList?.[4]) {
// 연간
operationalStatusList[4].yearAvg = data.yearData?data.yearData.AVG_EFFL_AMNT:'';
operationalStatusList[4].yearMax = data.yearData?data.yearData.MAX_EFFL_AMNT:'';
operationalStatusList[4].yearMin = data.yearData?data.yearData.MIN_EFFL_AMNT:'';
// 여름
operationalStatusList[4].summerAll = data.summer?data.summer.AVG_EFFL_AMNT:'';
operationalStatusList[4].summerThree = data.summerRainy?data.summerRainy.AVG_EFFL_AMNT:'';
operationalStatusList[4].summerRain = data.summerSunny?data.summerSunny.AVG_EFFL_AMNT:'';
// 겨울
operationalStatusList[4].winterAll = data.winter?data.winter.AVG_EFFL_AMNT:'';
operationalStatusList[4].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_EFFL_AMNT:'';
operationalStatusList[4].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_EFFL_AMNT:'';
operationalStatusList[4].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_EFFL_AMNT:'';
// 지하수
operationalStatusList[4].groundWaterAvg = 10;
operationalStatusList[4].groundWaterSummer = 11;
operationalStatusList[4].groundWaterWinter = 12;
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0;
operationalStatusList[4].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatusList[4].rdiRain = summerLowRain;
dataFormSetting(operationalStatusList[4]);
}
/*
* 수질 - 유입(BOD)
*/
if (operationalStatusList?.[5]) {
// 연간
operationalStatusList[5].yearAvg = data.yearData?data.yearData.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].yearMax = data.yearData?data.yearData.MAX_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].yearMin = data.yearData?data.yearData.MIN_INFL_ASSCT_BFR_WQLT_BOD:'';
dataFormSetting(operationalStatusList[5]);
// 여름
operationalStatusList[5].summerAll = data.summer?data.summer.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].summerThree = data.summerRainy?data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].summerRain = data.summerSunny?data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
// 겨울
operationalStatusList[5].winterAll = data.winter?data.winter.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD:'';
operationalStatusList[5].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_BOD:'';
// 오염물질
// RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD))
const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0;
const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0;
const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0;
operationalStatusList[5].rdi = winterAvgBod===0?0:summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod);
operationalStatusList[5].rdiRain = summerLowRain;
dataFormSetting(operationalStatusList[5]);
}
/**
* 수질-방류(BOD)
*/
if (operationalStatusList?.[6]) {
// 연간
operationalStatusList[6].yearAvg = data.yearData?data.yearData.AVG_EFFL_WQLT_BOD:'';
operationalStatusList[6].yearMax = data.yearData?data.yearData.MAX_EFFL_WQLT_BOD:'';
operationalStatusList[6].yearMin = data.yearData?data.yearData.MIN_EFFL_WQLT_BOD:'';
dataFormSetting(operationalStatusList[6]);
// 여름
operationalStatusList[6].summerAll = data.summer?data.summer.AVG_EFFL_WQLT_BOD:'';
operationalStatusList[6].summerThree = data.summerRainy?data.summerRainy.AVG_EFFL_WQLT_BOD:'';
operationalStatusList[6].summerRain = data.summerSunny?data.summerSunny.AVG_EFFL_WQLT_BOD:'';
// 겨울
operationalStatusList[6].winterAll = data.winter?data.winter.AVG_EFFL_WQLT_BOD:'';
operationalStatusList[6].winterSunnyMin = data.winterSunny?data.winterSunny.MIN_EFFL_WQLT_BOD:'';
operationalStatusList[6].winterSunnyAvg = data.winterSunny?data.winterSunny.AVG_EFFL_WQLT_BOD:'';
operationalStatusList[6].winterSunnyMax = data.winterSunny?data.winterSunny.MAX_EFFL_WQLT_BOD:'';
dataFormSetting(operationalStatusList[6]);
}
// 수질-유입(T-N)
if (operationalStatusList?.[7]) {
// 연간
operationalStatusList[7].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[7].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[7].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[7].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
}
// 수질-방류(T-N)
if (operationalStatusList?.[8]) {
// 연간
operationalStatusList[8].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[8].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[8].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[8].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TN * 1000) / 1000).toFixed(3).toString():'';
}
// 수질-유입(T-P)
if (operationalStatusList?.[9]) {
// 연간
operationalStatusList[9].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[9].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[9].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[9].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
}
// 수질-방류(T-P)
if (operationalStatusList?.[10]) {
// 연간
operationalStatusList[10].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[10].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[10].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[10].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
}
/**
* 총인설비
*/
//
// 유입
if (operationalStatusList?.[11]) {
// 연간
operationalStatusList[11].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[11].summerAll = data.summer?Number(Math.round(data.summer.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[11].winterAll = data.winter?Number(Math.round(data.winter.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[11].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_INFL_ASSCT_BFR_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
}
// 방류
if (operationalStatusList?.[12]) {
// 연간
operationalStatusList[12].yearAvg = data.yearData?Number(Math.round(data.yearData.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].yearMax = data.yearData?Number(Math.round(data.yearData.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].yearMin = data.yearData?Number(Math.round(data.yearData.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 여름
operationalStatusList[12].summerAll = data.summer?Number(Math.round(data.summer.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].summerThree = data.summerRainy?Number(Math.round(data.summerRainy.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].summerRain = data.summerSunny?Number(Math.round(data.summerSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
// 겨울
operationalStatusList[12].winterAll = data.winter?Number(Math.round(data.winter.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].winterSunnyMin = data.winterSunny?Number(Math.round(data.winterSunny.MIN_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].winterSunnyAvg = data.winterSunny?Number(Math.round(data.winterSunny.AVG_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
operationalStatusList[12].winterSunnyMax = data.winterSunny?Number(Math.round(data.winterSunny.MAX_EFFL_WQLT_TP * 1000) / 1000).toFixed(3).toString():'';
for(let i=1; i<operationalStatusList.length; i++){
const txtArray = ['1', 'INFL_AMNT', 'SUM_FLOW', 'RETURN_AMNT', 'EFFL_AMNT', 'INFL_ASSCT_BFR_WQLT_BOD', 'EFFL_WQLT_BOD', 'INFL_ASSCT_BFR_WQLT_TN', 'EFFL_WQLT_TN', 'INFL_ASSCT_BFR_WQLT_TP', 'EFFL_WQLT_TP', 'INFL_ASSCT_BFR_WQLT_TP', 'EFFL_WQLT_TP'];
if(operationalStatusList[i]){
if(i < 7){
changeValue(operationalStatusList[i], data, txtArray[i]);
dataFormSetting(operationalStatusList[i]!);
}else{
calcValue(operationalStatusList[i], data, txtArray[i]);
}
}
}
}
//const excelDownList: RainManageExcelType[] = [];
@@ -1446,6 +1240,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType)
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){
rainDate[1] = Number(rainDate[1] || 0) + 1;
+1
View File
@@ -12,6 +12,7 @@
},
"aliases": {
"components": "@/components",
"src": "@/src",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
+8 -5
View File
@@ -1,8 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
import { SERVER_API_CS_URL } from "./src/shared/config/env";
const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id";
const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization";
const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL || "";
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
@@ -14,7 +14,7 @@ export function middleware(req: NextRequest) {
if (pathname.startsWith("/server")) {
const queryParams = searchParams.toString();
const newUrl = `${SERVER_API_URL}${pathname.replace("/server", "")}${
const newUrl = `${SERVER_API_CS_URL}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`);
@@ -76,7 +76,7 @@ export function middleware(req: NextRequest) {
const requestHeaders = new Headers(req.headers);
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${SERVER_API_URL}${pathname}`;
const newUrl = `${SERVER_API_CS_URL}${pathname}`;
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
@@ -91,7 +91,7 @@ export function middleware(req: NextRequest) {
if (pathname.startsWith("/user-service")) {
const requestHeaders = new Headers(req.headers);
const newUrl = `${SERVER_API_URL}${pathname}`;
const newUrl = `${SERVER_API_CS_URL}${pathname}`;
console.log(
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`,
@@ -100,7 +100,9 @@ export function middleware(req: NextRequest) {
headers: requestHeaders,
});
}
if (pathname === "/") {
return NextResponse.redirect(new URL("/pages/dashboard", req.url));
}
return NextResponse.next();
}
@@ -111,5 +113,6 @@ export const config = {
"/attach-service/:path*",
"/user-service/:path*",
"/cms-service/:path*",
"/"
],
};
+5
View File
@@ -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.
+1
View File
@@ -13,6 +13,7 @@
"@common/components": "workspace:*",
"@tailwindcss/vite": "^4.0.10",
"html2canvas": "^1.4.1",
"js-cookie": "^3.0.5",
"next": "15.1.7",
"select": "^1.1.2",
"webpack": "^5.98.0",
@@ -0,0 +1,81 @@
import type { LoginService, ILogin } from "../model/login-interface";
import { tokenManager } from "../lib/token-manager";
import {PROXY_HOST, ACCESS_TOKEN, AUTH_USER_ID,SITE_ID, CLAIM_NAME, ENDPOINT, JWT_EXPIRATION_TIME } from "../../../shared/config";
export class LoginApiService implements LoginService {
private static instance: LoginApiService;
private constructor() {}
public static getInstance(): LoginApiService {
if (!LoginApiService.instance) {
LoginApiService.instance = new LoginApiService();
}
return LoginApiService.instance;
}
public async login(payload: ILogin): Promise<void> {
try {
console.log(' ENDPOINT.USER_SERVICE.LOGIN ', ENDPOINT.USER_SERVICE.LOGIN)
console.log(payload.userId+' '+payload.password)
const response = await fetch( PROXY_HOST+ ENDPOINT.USER_SERVICE.LOGIN, {
method: "POST",
body: JSON.stringify({
userId: payload.userId,
password: payload.password,
}),
});
// const data = response.headers.get(ACCESS_TOKEN);
const accessToken = response.headers.get(ACCESS_TOKEN);
const tokenId = response.headers.get(AUTH_USER_ID);
console.log('accessToken ', accessToken)
console.log('tokenId ', tokenId)
if (!accessToken || !tokenId) {
throw new Error("Missing required authentication tokens");
}
tokenManager.setToken(CLAIM_NAME, accessToken);
tokenManager.setToken(AUTH_USER_ID, tokenId);
tokenManager.setToken("site-id-key", SITE_ID);
tokenManager.setupRefreshTimer(
() => this.silentRefresh(),
JWT_EXPIRATION_TIME,
);
} catch (error) {
console.log(' login err ' , error)
throw new Error(`Login failed: ${error}`);
}
}
public async silentRefresh(): Promise<void> {
console.log(' silentRefresh ')
try {
const response = await fetch(PROXY_HOST+
`${ENDPOINT.USER_SERVICE.SILENT_REFRESH}`,
{ method: "PUT" },
);
const data = await response.json();
const accessToken = data[ACCESS_TOKEN];
const tokenId = data[AUTH_USER_ID];
if (!accessToken || !tokenId) {
throw new Error("Missing required authentication tokens");
}
tokenManager.setToken(CLAIM_NAME, accessToken);
tokenManager.setToken(AUTH_USER_ID, tokenId);
} catch (error) {
tokenManager.clearAuthToken();
throw new Error(`Silent refresh failed: ${error}`);
}
}
}
export const LoginApi = LoginApiService.getInstance();
+2
View File
@@ -0,0 +1,2 @@
export { LoginApi } from "./api/login-service";
//export { AuthWrapper } from "./lib/auth-wrapper";
@@ -0,0 +1,59 @@
import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config";
import { getCookie, removeCookie, setCookie } from "@/src/shared/lib/cookie";
export class TokenManager {
private static instance: TokenManager;
private refreshTimer: NodeJS.Timeout | null = null;
private constructor() {}
public static getInstance(): TokenManager {
if (!TokenManager.instance) {
TokenManager.instance = new TokenManager();
}
return TokenManager.instance;
}
public getToken(key: string): string | undefined {
return getCookie(key);
}
public setToken(key: string, value: string): void {
setCookie(key, value, {
secure: window.location.protocol === "https:",
sameSite: "lax",
});
}
public removeToken(key: string): void {
removeCookie(key);
}
public setupRefreshTimer(
callback: () => Promise<void>,
expirationTime: number,
): void {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
}
this.refreshTimer = setTimeout(() => {
callback().catch((error) => {
console.error("Silent refresh failed:", error);
this.clearAuthToken();
});
}, expirationTime - 60000);
}
public clearAuthToken(): void {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
this.refreshTimer = null;
}
this.removeToken(CLAIM_NAME);
this.removeToken(AUTH_USER_ID);
}
}
export const tokenManager = TokenManager.getInstance();
@@ -0,0 +1,9 @@
export interface ILogin {
userId: string;
password: string;
}
export interface LoginService {
login(payload: ILogin): Promise<void>;
silentRefresh(): Promise<void>;
}
+56
View File
@@ -0,0 +1,56 @@
import { PROXY_HOST } from "./env";
export const BASE_URL = `${PROXY_HOST}/server`;
export const ENDPOINT = {
ATTACH_SERVICE: {
TUS_UPLOAD: "/attach-service/api/v1/tus/upload",
EDITOR_UPLOAD: "/attach-service/api/v1/upload/editor",
IMAGES: "/attach-service/api/v1/images",
MOVIE_THUMBNAIL: "/attach-service/api/v1/movie/thumbnail",
ATTACHMENTS: "/attach-service/api/v1/atfl/attachments",
DOWNLOAD: "/attach-service/api/v1/atflcd/download",
STREAM_VIDEO: "/attach-service/api/v1/stream/video",
},
USER_SERVICE: {
LOGIN: "/user-service/login",
USERS: "/user-service/api/v1/users",
SILENT_REFRESH: "/user-service/api/v1/users/token/refresh",
AUTHORIZATIONS: "/user-service/api/v1/authorizations",
ROLE_AUTHORIZATIONS: "/user-service/api/v1/role-authorizations",
ROLES: "/user-service/api/v1/roles",
},
CMS_SERVICE: {
SITES: "/cms-service/api/v1/sites",
IPS: "/cms-service/api/v1/site-ips",
DOMAINS: "/cms-service/api/v1/site-dmns",
POSTINGS: "/cms-service/api/v1/site-pstgs",
CODE: "/cms-service/api/v1/code/cache",
MENUS: "/cms-service/api/v1/menus",
MENU_ROLES: "/cms-service/api/v1/menu-roles",
MENU_AUTHRTS: "/cms-service/api/v1/menu-authrts",
CONTENTS: "/cms-service/api/v1/contents",
CONTENTS_DETAILS: "/cms-service/api/v1/contents-details",
ORGANIZATIONS: "/cms-service/api/v1/ognzs",
USER_ORGANIZATION: "/cms-service/api/v1/user-ognz",
IMAGES: "/cms-service/api/v1/images",
BANNERS: "/cms-service/api/v1/bnrs",
TEMPLATE: "/cms-service/api/v1/tmplt",
TERMS: "/cms-service/api/v1/trms",
LWPO_CODE: "/cms-service/api/v1/lwpo-cds",
UP_CODE: "/cms-service/api/v1/up-codes",
POPUPS: "/cms-service/api/v1/popups",
SCHEDULES: "/cms-service/api/v1/schdls",
SURVEY: "/cms-service/api/v1/srvy",
SURVEY_QUESTION: "/cms-service/api/v1/srvy-qitem",
SURVEY_ARTICLE: "/cms-service/api/v1/srvy-artcl",
},
BOARD_SERVICE: {
BBS: "/board-service/api/v1/bbs",
BBS_TEMPLATES: "/board-service/api/v1/bbsTmplt",
POSTS: "/board-service/api/v1/posts",
POSTS_COMMENTS: "/board-service/api/v1/posts-cmnts",
},
};
export const JWT_EXPIRATION_TIME = 1800000;
+14
View File
@@ -0,0 +1,14 @@
export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "FO";
export const PORT = process.env.NEXT_PUBLIC_PORT || "3000";
export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`;
export const SERVER_API_WR_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL ||'http://con.mind-one.co.kr:11080/api/v1';
export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1';
export const SERVER_API_CS_URL = process.env.NEXT_PUBLIC_CS_SERVER_API_URL||'http://dev.onasys.co.kr:8000';
export const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization";
export const ACCESS_TOKEN = process.env.NEXT_PUBLIC_ACCESS_TOKEN || "access-token";
export const REFRESH_TOKEN =process.env.NEXT_PUBLIC_REFRESH_TOKEN || "refresh-token";
export const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id";
export const SERVER_SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL;
+2
View File
@@ -0,0 +1,2 @@
export * from "./api";
export * from "./env";
+31
View File
@@ -0,0 +1,31 @@
import Cookies from "js-cookie";
export function getCookie(name: string): string | undefined {
if (typeof window === "undefined") {
return undefined;
}
return Cookies.get(name);
}
export function setCookie(
name: string,
value: string,
options?: Cookies.CookieAttributes,
): void {
if (typeof window === "undefined") {
return;
}
Cookies.set(name, value, options);
}
export function removeCookie(
name: string,
options?: Cookies.CookieAttributes,
): void {
if (typeof window === "undefined") {
return;
}
Cookies.remove(name, options);
}
+1 -1
View File
@@ -5,7 +5,7 @@ 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://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함
NEXT_PUBLIC_RSONE_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
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
+1 -1
View File
@@ -5,7 +5,7 @@ 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://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_RSONE_DC_URL = https://apigateway.rsonesoft.com #자료관 데이터
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
+8 -4
View File
@@ -1,9 +1,13 @@
NEXT_PUBLIC_SITE_ID = SN
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_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
@@ -3,7 +3,7 @@ import React from "react";
interface InputSectionProps {
title: string;
data: { year: string; type: string }[];
data: { year: string; type: string, bizNm: string, bizType: string }[];
sectionKey: string;
onInputChange: (section: string, index: number, field: string, value: string) => void;
}
@@ -13,21 +13,26 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
<div>
{/* 섹션 제목 */}
<div className="card-header">
<h3 className="text-2xl mx-3 font-medium text-label">{title}</h3>
<h3 className="text-2xl mx-3 font-medium text-label whitespace-nowrap">{title}</h3>
</div>
{/* 테이블 */}
<div className="card !m-[1.2rem] !p-0r">
<Table variant="secondary" className="w-full">
<TableHeader>
<TableRow>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px]">년도</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto">구분</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">구분</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">년도</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">사업명</TableHead>
<TableHead className="border border-gray-300 px-2 py-2 text-sm w-auto whitespace-nowrap">유형</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((row, index) => (
<TableRow key={index}>
<TableCell className="border border-gray-300 px-2 py-2 text-sm w-[80px]">
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.type}
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm w-[80px] whitespace-nowrap">
<input
type="text"
value={row.year}
@@ -35,8 +40,11 @@ const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, on
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
/>
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto">
{row.type}
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.bizNm}
</TableCell>
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal w-auto whitespace-nowrap">
{row.bizType}
</TableCell>
</TableRow>
))}
@@ -49,8 +49,8 @@ export const handleInputChange = (section: string,
value: string,
dataMap: {
[key: string]: {
data: { year: string; type: string }[];
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string }[]>>;
data: { year: string; type: string, bizNm: string, bizType: string }[];
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string, bizNm: string, bizType: string }[]>>;
};
}
) => {
@@ -28,22 +28,17 @@ export const MsSwagePplCol = [
// { id: 'lyrTypeNm', title: '레이어타입명' },
]
export const MsSwagePplCol2 = [
{ id: 'fcltMngNo', title: '시설관리번호' },
{ id: 'instMngNo', title: '기관관리번호' },
{ id: 'fcltMngNo', title: '시설 관리번호' },
{ id: 'instMngNo', title: '기관 관리번호' },
{ id: 'instlYmd', title: '설치일자' },
{ id: 'mapshtNo', title: '도엽번호' },
{ id: 'pdmt', title: '관경(mm)' },
{ id: 'prlg', title: '연장(m)' },
{ id: 'avgGrdn', title: '평균구배(m/m)' },
{ id: 'rnysnFvlc', title: '우기 시 유속(m/s)' },
{ id: 'clarFvlc', title: '건기 시 유속(m/s)' },
{ id: 'drtwMltvArea', title: '오수배수면적(m²)' },
{ id: 'exclnMltvArea', title: '우수배수면적(m²)' },
{ id: 'tmptDpth', title: '시점깊이(m)' },
{ id: 'tmptMhlMngNo', title: '시점맨홀관리번호' },
{ id: 'tmptMhlMngNo', title: '시점맨홀 관리번호' },
{ id: 'scendDpth', title: '종점깊이(m)' },
{ id: 'scendMhlMngNo', title: '종점맨홀관리번호' },
{ id: 'trflnCnt', title: '차선수' },
{ id: 'scendMhlMngNo', title: '종점맨홀 관리번호' },
{ id: 'vrtcLen', title: '세로길이(m)' },
{ id: 'wdthLen', title: '가로길이(m)' },
// { id: 'fcltyShpCd', title: '시설물형태코드' },
@@ -306,35 +301,35 @@ export const SwtrmtFcltCol2 = [
{ id: 'rgnExpln', title: '지역설명' },
{ id: 'cmcnYmd', title: '준공일자' },
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
{ id: 'clsgYmd', title: '폐쇄일자' },
// { id: 'clsgYmd', title: '폐쇄일자' },
// { id: 'prcsMsrcCd', title: '처리공법코드' },
// { id: 'laeCd', title: '지자체코드' },
{ id: 'laeNm', title: '지자체명' },
{ id: 'ctpvNm', title: '시도명' },
{ id: 'sggNm', title: '시군구명' },
// { id: 'ctpvNm', title: '시도명' },
// { id: 'sggNm', title: '시군구명' },
// { id: 'sareaCd', title: '권역코드' },
{ id: 'sareaNm', title: '권역명' },
{ id: 'lrgeSareaNm', title: '대권역명' },
{ id: 'rvrNm', title: '하천명' },
{ id: 'addr', title: '주소' },
{ id: 'dtlAddr', title: '상세주소' },
{ id: 'rdnmAddr', title: '도로명주소' },
{ id: 'dtlRdnmAddr', title: '상세도로명주소' },
// { id: 'addr', title: '주소' },
// { id: 'dtlAddr', title: '상세주소' },
// { id: 'rdnmAddr', title: '도로명주소' },
// { id: 'dtlRdnmAddr', title: '상세도로명주소' },
{ id: 'dschgWtstNm', title: '방류수계명' },
{ id: 'pllnArea', title: '부지면적(m²)' },
{ id: 'mngRprtvBzentyNm', title: '관리대행업제명' },
// { id: 'pllnArea', title: '부지면적(m²)' },
// { id: 'mngRprtvBzentyNm', title: '관리대행업체명' },
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수(인)' },
{ id: 'prcsZoneAreaSum', title: '처리구역면적합계(m²)' },
{ id: 'xcrd', title: 'X좌표' },
{ id: 'ycrd', title: 'Y좌표' },
// { id: 'xcrd', title: 'X좌표' },
// { id: 'ycrd', title: 'Y좌표' },
// { id: 'oprtngSttsCd', title: '가동상태코드' },
{ id: 'rprsMsrcCn', title: '대표공법내용' },
{ id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량(mg/L)' },
{ id: 'sttyCrtrToc', title: '법정기준TOC(mg/L)' },
{ id: 'sttyCrtrSs', title: '법정기준SS(mg/L)' },
{ id: 'sttyCrtrTn', title: '법정기준총질소(mg/L)' },
{ id: 'sttyCrtrTph', title: '법정기준총인(mg/L)' },
{ id: 'sttyCrtrClbclCnt', title: '법정기준대장균수(군수/100mL)' },
// { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량(mg/L)' },
// { id: 'sttyCrtrToc', title: '법정기준TOC(mg/L)' },
// { id: 'sttyCrtrSs', title: '법정기준SS(mg/L)' },
// { id: 'sttyCrtrTn', title: '법정기준총질소(mg/L)' },
// { id: 'sttyCrtrTph', title: '법정기준총인(mg/L)' },
// { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수(군수/100mL)' },
{ id: 'bsnNm', title: '유역명' },
// { id: 'obsrvnSptCd', title: '관측지점코드' },
];
@@ -3,6 +3,8 @@ import { VerticalTable } from '@/components/ui/verticalTable';
import React, { useState, useEffect } from 'react'
import InputSection from '../InputSection';
import { handleInputChange } from '../common';
import { getData } from "@/components/lib/api";
// import { useBizByFcltStore } from "../../../../store/StoreProvider";
// 하수관로
const inputColumn = [
@@ -15,33 +17,97 @@ const inputColumn = [
{ id: "layCd", title: "매설위치", type: "select", options: ["도로", "하상"] },
];
type operationDataType = { year: string; type: string, bizNm: string, bizType: string };
const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClick?: () => void }) => {
const [repairData, setRepairData] = useState([
{ year: "2025", type: "개대체" },
{ year: "2025", type: "보수" },
]);
const [cleaningData, setCleaningData] = useState([
{ year: "2025", type: "청소" },
{ year: "2025", type: "준설" },
]);
const [inspectionData, setInspectionData] = useState([
{ year: "2025", type: "관로기술진단" },
{ year: "2025", type: "관로기술진단" },
]);
const [formData, setFormData] = useState<any>({});
const [repairData, setRepairData] = useState<operationDataType[]>([]);
const [cleaningData, setCleaningData] = useState<operationDataType[]>([]);
const [inspectionData, setInspectionData] = useState<operationDataType[]>([]);
const dataMap = {
repair: { data: repairData, setData: setRepairData },
cleaning: { data: cleaningData, setData: setCleaningData },
inspection: { data: inspectionData, setData: setInspectionData },
};
const [formData, setFormData] = useState<any>({});
// featureData가 변경될 때 formData 초기화
// const BIZ_SE_CD = useBizByFcltStore(state => state.BIZ_SE_CD);
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);
const BIZ_TY_CD = [
{ lwpoCd: "SV", lwpoCdNm: "용역" },
{ lwpoCd: "SF", lwpoCdNm: "자체" },
{ lwpoCd: "SC", lwpoCdNm: "공사" },
];
// 마운트 및 업데이트 시 API 호출
useEffect(() => {
if (featureData) {
setFormData(featureData);
}
}, [featureData]);
const fetchAll = async () => {
const operationData = await getData(`v1/bizInfo/bizByFclt?mngInstCd=${featureData?.mng_inst_cd}&fcltMngNo=${featureData?.fclt_mng_no}&page=0&size=1`);
let inspectionArr: operationDataType[] = [];
let cleaningArr: operationDataType[] = [];
let repairArr: operationDataType[] = [];
if (Array.isArray(operationData)) {
operationData.forEach(item => {
if (!item.flfmt_yr) return;
// biz_se_cd에 해당하는 type명 찾기
const seCodeObj = BIZ_SE_CD.find(code => code.lwpoCd === item.biz_se_cd);
const tyCodeObj = BIZ_TY_CD.find(code => code.lwpoCd === item.biz_ty_cd);
const seTypeName = seCodeObj ? seCodeObj.lwpoCdNm : "";
const tyTypeName = tyCodeObj ? tyCodeObj.lwpoCdNm : "";
switch (item.biz_se_cd) {
case "100":
inspectionArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
case "203":
cleaningArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
case "200":
case "201":
case "204":
repairArr.push({
year: String(item.flfmt_yr),
type: seTypeName,
bizNm: String(item.biz_nm),
bizType: tyTypeName,
});
break;
}
});
}
setInspectionData(inspectionArr);
setCleaningData(cleaningArr);
setRepairData(repairArr);
console.log('inspectionArr', inspectionArr);
console.log('cleaningArr', cleaningArr);
console.log('repairArr', repairArr);
};
fetchAll();
}, []);
// 폼 데이터가 변경될 때 호출될 핸들러
const handleFormDataChange = (newFormData: any) => {
@@ -56,6 +122,18 @@ const MsSwagePpl = ({ featureData, onSaveClick }: { featureData: any, onSaveClic
}
};
// 변환 함수: 객체 → [{ year, type }]
// function convertToYearTypeArray(obj: any): { year: string, type: string }[] {
// if (!obj || typeof obj !== "object") return [];
// // 이미 배열이면 그대로 반환
// if (Array.isArray(obj)) return obj;
// // 객체의 각 key를 type, value를 year로 변환
// return Object.entries(obj).map(([type, year]) => ({
// year: String(year ?? ""),
// type: String(type ?? ""),
// }));
// }
return (
<>
@@ -8,7 +8,7 @@ import { getData } from "@/components/lib/api";
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
const inputColumn = [
{ id: "prrtymngYn", title: "중점여부", type: "select", options: ["중점우수토실", "우수토실"] },
{ id: "prrtymngYn", title: "중점여부", type: "select", options: ["우수토실", "중점우수토실"] },
{ id: "swtrmtZoneCd", title: "처리구역", type: "input" },
];
@@ -152,7 +152,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
<div className="bg-white px-4 py-2 ">
<span className="text-2xl text-red-600 font-bold">● 상수사용량 적용연월</span>
<div className="flex items-center mt-2">
<span className="text-black text-xl font-bold">
<span className="ml-5 text-black text-xl font-bold">
{(selectedAplcnYm) ? moment(selectedAplcnYm, "YYYYMM").format("YYYY-MM") : ""}
</span>
</div>
@@ -161,7 +161,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => {
<div className="bg-white px-4 py-2 ">
<span className="text-2xl text-red-600 font-bold">● 침입수량 산정결과</span>
<div className="flex items-center mt-2">
<span className="ml-2 text-black text-xl">침입수량 : </span>
<span className="ml-5 text-black text-xl">침입수량 : </span>
<span className="w-[35px] inline-block text-right text-black text-xl font-bold">{inftwtQty}</span>
<span className="ml-2 text-black text-xl">m³/d</span>
</div>
@@ -129,7 +129,7 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
<div className="bg-white px-4 py-2 ">
<span className="text-2xl text-red-600 font-bold">● 상수사용량 적용연월</span>
<div className="flex items-center mt-2">
<span className="text-black text-xl font-bold">
<span className="ml-5 text-black text-xl font-bold">
{(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""}
</span>
</div>
@@ -139,12 +139,12 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
<div className="bg-white px-4 py-2 ">
<span className="text-2xl text-red-600 font-bold">● 강우유입수량 분석결과</span>
<div className="flex items-center mt-2">
<span className="w-[90px] inline-block text-right ml-2 text-black text-xl">평균RDII : </span>
<span className="w-[95px] inline-block text-right text-black text-xl">평균RDII : </span>
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {rdiiQty}</span>
<span className="ml-2 text-black"> m³/d</span>
</div>
<div className="flex items-center mt-2">
<span className="w-[90px] inline-block text-right ml-2 text-black text-xl">강우량대비RDII : </span>
<span className="w-[95px] inline-block text-right text-black text-xl">강우량대비RDII : </span>
<span className="w-[30px] inline-block text-right text-black text-xl font-bold"> {totalVolume}</span>
<span className="ml-2 text-black"> m³/mm</span>
</div>
@@ -110,7 +110,8 @@ export const addFileName = async (data:FlattenedFolder[]) => {
data.map(async (item) => {
const filenameArr:ReportDataFile[] = await getDcFile(String(item.clsfSystmSn));
const f = filenameArr[0];
const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:""};
//console.log("clsfSystmSn:", item.clsfSystmSn, "f:", f);
const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:f?.filePath};
// if (f !== undefined && f.fileSn !== "") {
// const fprcs = await getDcFileprcs(f?.fileSn);
// if (fprcs.length>0)
@@ -149,11 +150,12 @@ export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터
link.remove();
window.URL.revokeObjectURL(url);
}
export const getFileDownload = async (fileSn:string, fileName:string) => {
const fileprcsData = await getDcFileprcs(fileSn); // filePath 조회
if (fileprcsData?.length === 0 ) { alert("filePath 조회 오류"); }
if (fileprcsData[0].filePath === "") alert("filePath 정보 없으므로 다운로드 불가");
const blobData = await getDcFileDownload(fileprcsData[0].filePath); // 실제 다운로드 API
export const getFileDownload = async (fileSn:string, fileName:string, filePath:string) => {
//const fileprcsData = await getDcFileprcs(fileSn); // filePath 조회
//if (fileprcsData?.length === 0 ) { alert("filePath 조회 오류"); }
//if (fileprcsData[0].filePath === "") alert("filePath 정보 없으므로 다운로드 불가");
if (filePath === "") alert("filePath 정보 없으므로 다운로드 불가:filesn"+ fileSn);
const blobData = await getDcFileDownload(filePath); // 실제 다운로드 API
fileDownload(blobData, fileName);
}
// 시도 데이터
@@ -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, SwtrmtPrcsZoneCol, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, ExclnSewagePumpCol, MsSwageMhlCol, stofcbrCol, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "./components/common/table";
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 InfilChart from './components/popupComponent/chart/infilChart';
import RdiiChart from './components/popupComponent/chart/rdiiChart';
import { useInfilChartModalStore, useRdiiChartModalStore } from "./components/popupComponent/chartModalStore";
@@ -33,6 +33,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
const { setIsOpen: setIsModalInfil } = useInfilChartModalStore();
const { setIsOpen: setIsModalRdii } = useRdiiChartModalStore();
const [curPage, setCurPage] = useState(1); // 현재 페이지 상태
const [isClicked, setIsClicked] = useState(true); // 클릭 여부 상태
// 🎯 관로관리번호 추출
const pplMngNo = featureData?.values_?.fclt_mng_no || "";
@@ -66,8 +67,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
}
});
// console.log("featureLayer.get('lyrTyNm');", featureLayer.get('lyrTyNm'));
// SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기
// 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 = '우수토실';
@@ -77,15 +79,17 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
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(true);
} else {
setIsDrtwSewagePump(false);
} else {
setIsDrtwSewagePump(true);
}
result = '하수펌프장';
}
else if (featureData.lyrTypeNm === 'tb_ms_swage_mhl') result = '하수맨홀';
}
else {
// 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 = '하수처리구역';
@@ -96,9 +100,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
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(true);
} else {
setIsDrtwSewagePump(false);
} else {
setIsDrtwSewagePump(true);
}
result = '하수펌프장';
}
@@ -135,15 +139,16 @@ 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);
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 '하수관로':
setHeadlist(MsSwagePplCol)
if (isClicked) setHeadlist(MsSwagePplCol)
else setHeadlist(MsSwagePplCol2)
setButtonChk(true)
// ✅ codeMapper 사용하여 API 결과도 변환
@@ -155,7 +160,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수맨홀':
setHeadlist(MsSwageMhlCol)
if (isClicked) setHeadlist(MsSwageMhlCol)
else setHeadlist(MsSwageMhlCol2)
setButtonChk(true)
// ✅ codeMapper 사용하여 API 결과도 변환
@@ -167,10 +173,17 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수펌프장':
setHeadlist(ExclnSewagePumpCol)
if (isDrtwSewagePump) {
if (isClicked) setHeadlist(DrtwSewagePumpCol1)
else setHeadlist(DrtwSewagePumpCol2)
} else {
if (isClicked) setHeadlist(ExclnSewagePumpCol)
else setHeadlist(ExclnSewagePumpCol2)
}
setButtonChk(true)
setTableData(codeMapper({
setTableData(codeMapper({
layerName: selectedLayerName,
data: { ...formattedData }
}));
@@ -178,15 +191,18 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
// setTableData(mappedData);
break;
case '하수처리시설':
setHeadlist(SwtrmtFcltCol);
if (isClicked) setHeadlist(SwtrmtFcltCol)
else setHeadlist(SwtrmtFcltCol2)
setButtonChk(false)
break;
case '하수처리구역':
setHeadlist(SwtrmtPrcsZoneCol)
if (isClicked) setHeadlist(SwtrmtPrcsZoneCol)
else setHeadlist(SwtrmtPrcsZoneCol2)
setButtonChk(false)
break;
case '우수토실':
setHeadlist(stofcbrCol)
if (isClicked) setHeadlist(stofcbrCol)
else setHeadlist(stofcbrCol2)
setButtonChk(true)
setIsMainSsc(false)
break;
@@ -477,7 +493,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void,
<Modal isModalOpen={isModalOpen} title="자체입력정보 저장" setIsModalOpen={setIsModalOpen} closeModal={handleModalClose} saveModal={handleModalSave}><div className="text-lg">정보를 저장하시겠습니까?</div></Modal>
{ContentComponent &&
<ContentComponent
featureData={tableData}
featureData={fetchData}
isMainSsc={isMainSsc}
isDrtwSewagePump={isDrtwSewagePump}
rainMeasureData={rainMeasureData}
+17 -17
View File
@@ -4,7 +4,7 @@ import Gis, { useGisContext } from "@common/business_components/ui/map/Gis";
import { getData } from "../../components/lib/api";
import { useCallback, useEffect, useRef, useState } from "react";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import SearchModal from "./SearchModal";
import SearchModal from "./searchModal/searchModal";
import FeatureClickEventPopup from "./featureClickEventPopup";
// 타입 정의 추가
@@ -115,22 +115,22 @@ const Map = () => {
};
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={true}
gisMaps={gisMaps}
isMiniMap={false}
searchView={SearchModal}
detailView={FeatureClickEventPopup}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
<Gis
treeData={treeData}
isSearch={true}
gisMaps={gisMaps}
isMiniMap={false}
searchView={SearchModal}
detailView={FeatureClickEventPopup}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
</div>
);
};
@@ -219,7 +219,7 @@ const BaseLayout = ({
<div className="flex items-center w-full">
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
<>
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">분석기간</Label>
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">상수사용기간</Label>
<DateMonthPicker
field={{
value: aplcnYmFrom || (() => { // aplcnYm이 없으면 현재 연월을 기본값으로 설정
+41 -30
View File
@@ -1,5 +1,18 @@
import {useState, useEffect} from 'react';
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
import { useEffect, useState } from "react";
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;
}
throw new Error('Failed to fetch data');
};
export const postData = async (url: string, data: object): Promise<any> => {
const options: RequestInit = {
@@ -15,11 +28,6 @@ export const postData = async (url: string, data: object): Promise<any> => {
if (res.ok) {
const resData = await res.json();
return resData;
// const text = await res.text();
// if (text) {
// return JSON.parse(text); // JSON 데이터가 있으면 파싱하여 반환
// }
// return true; // 응답 본문이 비어 있으면 true 반환
}
};
@@ -46,29 +54,6 @@ export const putData = async (url: string, data: object): Promise<any> => {
return resData;
};
export const getData = async (url: string, data?: object): Promise<any> => {
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service'+url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export const deleteData = async (url: string, data?: object): Promise<any> => {
const options: RequestInit = {
method: "DELETE",
@@ -82,6 +67,32 @@ export const deleteData = async (url: string, data?: object): Promise<any> => {
return res;
};
// RSONE DC API 호출
export const getDcData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/dc-service' + url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
// RSONE DC API 호출
export const getCmsData = async (url: string, data?: object): Promise<any> => {
console.log(url);
const res = await fetch('/cms-service' + url, {
method: "GET",
});
if (res.ok) {
const data = await res.json();
return data;
}
throw new Error('Failed to fetch data');
};
export function getCookie(name:string) {
const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
if (match && match[2]) {
@@ -90,7 +90,7 @@ export function YearRangeSelectBox({
return (
<div className={`flex items-center ${className}`}>
<SelectBox2
className="flex-1 mr-3 min-w-[120px] max-w-[170px]"
className="flex-1 mr-3"
selectClass={selectClass}
label={startLabel}
selectArray={getStartYearArray()}
@@ -99,7 +99,7 @@ export function YearRangeSelectBox({
/>
<span className="font-semibold text-xl mx-2">~</span>
<SelectBox2
className="flex-1 ml-1 mr-6 min-w-[70px] max-w-[150px]"
className="flex-1 ml-1"
selectClass={selectClass}
label={endLabel}
selectArray={getEndYearArray()}
+2 -2
View File
@@ -7,7 +7,7 @@ const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
const RSONE_DC_URL = process.env.NEXT_PUBLIC_RSONE_DC_URL || "";
const DC_URL = process.env.NEXT_PUBLIC_DC_URL || "";
export function middleware(req: NextRequest) {
console.log("Middleware 진입:", req.nextUrl.pathname);
@@ -106,7 +106,7 @@ export function middleware(req: NextRequest) {
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
.join("&");
const newUrl = `${RSONE_DC_URL}${pathname}${decodedParams ? `?${decodedParams}` : ""}`;
const newUrl = `${DC_URL}${pathname}${decodedParams ? `?${decodedParams}` : ""}`;
console.log(
`[Middleware] Rewriting from '/dc-service' path to: ${newUrl}`,
);
+24
View File
@@ -0,0 +1,24 @@
"use client"
import { BaseLayout } from "@common/components/ui"
import { useWrStore } from "./store"
import { MainDiv } from "@common/business_components/ui"
import type { MenuItem } from "./store/common/type/menu"
import { useRouter } from "next/navigation"
export default function Base({
children,
}: Readonly<{
children: React.ReactNode
}>) {
const { getMenuAuthrts } = useWrStore((state) => state)
const router = useRouter()
const changeMenu = (path: MenuItem) => {
getMenuAuthrts(path.menuCd)
router.push(path.urlPath)
}
return (
<BaseLayout handleMenuClick={changeMenu}>
<MainDiv>{children}</MainDiv>
</BaseLayout>
)
}
+11 -21
View File
@@ -1,12 +1,10 @@
'use client'
import { BaseLayout } from "@common/components";
"use client";
import "./globals.css";
import { StoreProvider } from "./store";
import { MainDiv } from "@common/business_components";
import koBase from '../public/locales/ko/message.json';
import { StoreProvider} from "./store";
import koBase from "../public/locales/ko/message.json";
import i18next from "i18next";
import { I18nextProvider } from "react-i18next";
import { useRouter } from "next/navigation";
import Base from "./base";
export default function RootLayout({
children,
@@ -14,34 +12,26 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
i18next.init({
lng: 'ko',
fallbackLng: 'ko',
lng: "ko",
fallbackLng: "ko",
resources: {
ko: {
base: koBase,
},
}
},
});
const router = useRouter();
return (
<html lang="en">
<body>
<I18nextProvider i18n={i18next}>
<StoreProvider>
<BaseLayout
handleMenuClick={(e) =>{
router.push(e.urlPath)
}}
>
<MainDiv>
{children}
</MainDiv>
</BaseLayout>
<Base>
{children}
</Base>
</StoreProvider>
</I18nextProvider>
</body>
</html >
</html>
);
}
+1 -1
View File
@@ -316,7 +316,7 @@ export default function CoMatRvw() {
control={form.control}
name="dmndQty"
render={({ field }) => (
<FormUi label="요구수량(M3/d)">
<FormUi label="요구수량(m³/일)">
<Input
placeholder=""
type="number"
+56 -55
View File
@@ -1,12 +1,9 @@
"use client";
import { useWrStore } from "@/app/store";
import { type RvwHistTableType } from "@/app/store/matDmd/coMatRvw/type";
import {
DmdCoReqSchema,
type DmdCoReqType,
type RvwHistTableType,
} from "@/app/store/matDmd/coMatRvw/type";
import {
confirmToast,
formatPhoneNumber,
getCity,
getdmdMatFlag,
@@ -27,11 +24,10 @@ import {
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
import { zodResolver } from "@hookform/resolvers/zod";
import moment from "moment";
import { useCallback, useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RuseRvwHstry() {
const {
code,
@@ -51,6 +47,7 @@ export default function RuseRvwHstry() {
sgg: "",
city: "",
});
const { t } = useTranslation('base');
const [fromDate, setFromDate] = useState<string>(
String(new Date().getFullYear())
@@ -116,53 +113,65 @@ export default function RuseRvwHstry() {
}
};
const form = useForm<DmdCoReqType>({
resolver: zodResolver(DmdCoReqSchema),
mode: "onChange",
});
const TableCellClick = useCallback(
(index: number | undefined) => {
if (
index !== undefined &&
index >= 0 &&
rvwHistRes &&
rvwHistRes[index]
) {
form.reset(rvwHistRes[index]);
(index: number | undefined, headId: string) => {
if (index !== undefined && index >= 0) {
const selectedData = rvwHistData;
if (selectedData && selectedData.length > 0) {
if (
selectedData[index]?.splyFcltFlg === "검토완료" &&
headId === "completeYn"
) {
setRvwHistData([...selectedData]);
} else if (
selectedData[index]?.splyFcltFlg === "매칭완료" &&
headId === "completeYn"
) {
toast.error(t("canNotChangeMatStateMsg"));
return;
}
}
}
},
[rvwHistRes, form]
[rvwHistData]
);
const confrirmToast = () => {
const completeData = rvwHistData
?.map((item) => {
form.clearErrors();
if (item?.completeYn) {
if (item?.completeYn.chk && item.splyFcltFlg === "검토완료") {
return Number(item.dmdInfoSn);
}
})
.filter((item) => item !== undefined);
if (form.formState.isDirty) {
toast((t) => (
<span className="flex flex-col items-center">
표준공사비 정보가 변경되었습니다. 검토 완료 진행시 입력한 정보는
초기화 됩니다.
<br />
진행하시겠습니까?
<button
onClick={() => {
toast.dismiss(t.id);
ruseRvwComplete(completeData as number[]);
}}
>
확인
</button>
<button onClick={() => toast.dismiss(t.id)}>취소</button>
</span>
));
if (completeData && completeData?.length > 0) {
confirmToast(t('isMatchFinishMsg'), () => ruseRvwComplete(completeData as number[]));
// const handleConfirm = async () => {
// ruseRvwComplete(completeData as number[]);
// };
// toast((t) => (
// <span className="flex flex-col items-center">
// 매칭완료를 진행하시겠습니까?
// <br />
// <div className="mt-2 flex flex-row space-x-2">
// <Button
// size={"sm"}
// onClick={() => {
// toast.dismiss(t.id);
// ruseRvwComplete(completeData as number[]);
// }}
// >
// 확인
// </Button>
// <Button size={"sm"} onClick={() => toast.dismiss(t.id)}>
// 취소
// </Button>
// </div>
// </span>
// ));
} else {
ruseRvwComplete(completeData as number[]);
toast.error( t('isNotMatchFinishMsg'));
}
};
useEffect(() => {
@@ -203,11 +212,10 @@ export default function RuseRvwHstry() {
rvwCmptnYmd: formatDate(item.demandMatching?.rvwCmptnYmd),
mtchnCmptnYmd: formatDate(item.demandMatching?.mtchnCmptnYmd),
ruseUtztnFcltNm: item.ruseUtztnFcltNm,
dmndQty: item.dmndQty,
splyFcltFlg: flag,
completeYn: false,
completeYn: {chk:flag=="매칭완료", readonly:flag=="매칭완료"},
} as unknown as RvwHistTableType;
});
@@ -215,9 +223,6 @@ export default function RuseRvwHstry() {
}
}, [rvwHistRes]);
useEffect(() => {
console.log("변경여부 ", form.formState.isDirty);
}, [form.formState]);
return (
<ContentsDiv>
@@ -301,7 +306,7 @@ export default function RuseRvwHstry() {
조회
</Button>
<Button size={"sm"} onClick={confrirmToast}>
검토완료
저장
</Button>
</div>
</div>
@@ -319,18 +324,14 @@ export default function RuseRvwHstry() {
headlist={ruseHistHeadList}
pageSize={20}
total={rvwHistCount}
cellClick={(index: number | undefined) => {
TableCellClick(index);
cellClick={(index: number | undefined, headId: string) => {
console.log("테이블 클릭", index);
TableCellClick(index, headId);
}}
onPageChange={(page) => {
setPageNatiion(page);
}}
></UiTable>
{/* <hr />
<h3 className="mt-4 ml-2 text-2xl font-medium text-label">
수요신청 상세현황
</h3>
<ConCostForm form={form} type={"hist"} selectionVersion={0} /> */}
</ListDiv>
</div>
</div>
+3 -1
View File
@@ -101,7 +101,9 @@ export default function CoSttn() {
...item,
telno: formatPhoneNumber(item.telno || undefined),
regDt: moment(item.regDt).format("YYYY-MM-DD"),
}));
dmdInfoId: `${item.dmdInfoId.slice(0, 3)}-${item.dmdInfoId.slice(3, 5)}-${item.dmdInfoId.slice(5,10)}-${item.dmdInfoId.slice(10)}`
}));
console.log("chData:", chData)
// .filter((item: any) => item !== undefined);
setBdstTable(chData);
} else if (coSttnRes?.length == 0) {
+26 -28
View File
@@ -43,8 +43,9 @@ export default function LdrmntCoSttn() {
const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" });
const [bdstTable, setBdstTable] = useState<customLdrmnt[]>();
const [searchTxt, setSearchTxt] = useState<string>();
const [excelData, setExcelData] = useState<any[]>();
const [excelData, setExcelData] = useState<any[] | undefined>()
const [resetPage, isResetPage] = useState(false);
const [isDownloadClicked, setIsDownloadClicked] = useState(false);
const { t } = useTranslation('base');
const excelClick = useCallback(() => {
@@ -79,10 +80,31 @@ export default function LdrmntCoSttn() {
const excelDownloadClick = useCallback(() => {
const cityNm = computeCityNm();
setExcelData(undefined);
setIsDownloadClicked(true);
ldrmntSearch(true, 1, String(ldrmntResCount), cityNm, searchTxt || "");
}, [objSearch, searchTxt, code, ldrmntSearch, computeCityNm, ldrmntResCount]);
useEffect(() => {
const transData = Array.isArray(excelList) ? excelList : [excelList];
const exData = tableData(transData, { code });
const data = Array.isArray(exData)
? exData.map((item: customLdrmnt) => {
return {
"시도": item.fctryAddr?.split(" ")[0] || "",
"시군구": item.fctryAddr?.split(" ")[1] || "",
"회사명": item.dmdCoNm,
"공장주소": item.fctryAddr,
"공장관리번호": item.fctryMngNo,
"전화번호": item.telno
}
}) : [];
setExcelData(data)
}, [excelList])
useEffect(() => {
if (!isDownloadClicked) return;
if (excelData === undefined) return;
if (excelData && excelData.length > 0) {
downloadExcel({
data: excelData,
@@ -93,7 +115,8 @@ export default function LdrmntCoSttn() {
} else {
toast.error(t('excelDownErrMsg'));
}
}, [excelData]);
setIsDownloadClicked(false);
}, [excelData, isDownloadClicked]);
const [isExcelOpen, setIsExcelOpen] = useState<{
isModal: boolean;
@@ -125,7 +148,7 @@ export default function LdrmntCoSttn() {
useEffect(() => {
if (!ldrmnt.isInit) {
ldrmntInit("0");
ldrmntInit(false, "0");
}
}, [ldrmnt.isInit, ldrmntInit]);
@@ -144,23 +167,6 @@ export default function LdrmntCoSttn() {
setBdstTable(tbData);
}, [ldrmntRes, code]);
useEffect(() => {
const transData = Array.isArray(excelList) ? excelList : [excelList];
const exData = tableData(transData, { code });
const data = Array.isArray(exData)
? exData.map((item: customLdrmnt) => {
return {
"시도": item.fctryAddr?.split(" ")[0] || "",
"시군구": item.fctryAddr?.split(" ")[1] || "",
"회사명": item.dmdCoNm,
"공장주소": item.fctryAddr,
"공장관리번호": item.fctryMngNo,
"전화번호": item.telno
}
}) : [];
setExcelData(data)
}, [excelList])
useEffect(() => {
ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
isResetPage(false)
@@ -264,10 +270,6 @@ export default function LdrmntCoSttn() {
}}
bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"}
body={<CoDetailModal formObj={detailModal.data} onFormSubmit={async (value) => {
// confirmToast(t('isUpdateMsg'), await ldrmntUpdate(value))
// // await ldrmntUpdate(value)
// setDetailModal({ ...detailModal, isOpen: false })
// ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
const handleConfirm = async () => {
await ldrmntUpdate(value);
setDetailModal({ ...detailModal, isOpen: false });
@@ -286,12 +288,8 @@ export default function LdrmntCoSttn() {
body={<CoRegExcel isModal={(isOpen) => setIsExcelOpen({ isModal: isOpen })} />}
/>
<Toaster position="top-center"></Toaster>
</div>
</ContentsDiv>
</>
)
}
@@ -364,6 +364,7 @@ export default function PublicReuseFaciltyInsert() {
setIndexMng(index);
}}
indexMng={indexMng}
readonly={false}
/>
</TabsContent>
<TabsContent className="max-h-full" value="buse">
@@ -3,7 +3,7 @@ import { useWrStore } from "@/app/store";
import {
type customRainBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { tableData } from "@/components/lib/func/commom";
import { formatCommaNumber, tableData } from "@/components/lib/func/commom";
import RainRegForm from "@/components/ui/form/fcltReg/rainReg/rainRegForm";
import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import {
@@ -52,7 +52,7 @@ export default function Home() {
: "확인불가";
return {
...item,
archArea: archArea,
archArea: formatCommaNumber(archArea),
};
}
@@ -166,8 +166,8 @@ export default function HtdflSttn() {
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
<div className="col-span-2 flex flex-row space-x-2 ml-3 mt-2">
<Label className="w-28 mt-2 text-xl">
{"시설명"}
</Label>
<SearchInput
@@ -124,8 +124,13 @@ export default function RainSttn() {
item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct,
archArea:
Math.floor(item.archArea) === -1 ? "확인불가" : item.archArea,
// archArea:
// Math.floor(item.archArea) === -1 ? "확인불가" : Math.trunc(item.archArea) ? item.archArea,
archArea: isNaN(Number(item.archArea))
? item.archArea
: Math.floor(Number(item.archArea)) === -1
? '확인불가'
: Math.trunc(Number(item.archArea))
};
})
.filter((item: any) => item.rainyObj !== undefined);
@@ -206,12 +211,12 @@ export default function RainSttn() {
value={objSearch.sgg} // value props 추가
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
<div className="col-span-2 flex flex-row space-x-2 ml-3 mt-2">
<Label className="w-28 mt-2 text-xl">
{"시설명"}
</Label>
<SearchInput
className={"w-full h-full text-2xl"}
className={"w-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
@@ -164,8 +164,8 @@ export default function RswtSttn() {
getSgg
}
/>
<div className="col-span-2 flex flex-row space-x-2">
<Label className="w-28 mt-2 text-xl text-gray-600">
<div className="col-span-2 flex flex-row space-x-2 ml-3 mt-2">
<Label className="w-28 mt-2 text-xl">
{"시설명"}
</Label>
<SearchInput
+88 -76
View File
@@ -16,50 +16,44 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RainStat() {
const { code, rainStat, searchRainStatList } = useWrStore((state) => state);
const { rainStatList, excelList, rainDetailList } = rainStat;
const { rainStatList, rainDetailList } = rainStat;
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [exData, setExData] = useState<Array<Record<string, string | number>>>();
//const [exData, setExData] = useState<Array<Record<string, string | number>>>();
const [bdstTable, setBdstTable] = useState<RainStatProps[]>();
const { t } = useTranslation('base');
useEffect(() => {
searchRainStatList(searchYear);
}, [code.cityCd, code.sggCd, searchRainStatList])
searchRainStatList(searchYear ,code.cityCd);
}, [code.cityCd, searchRainStatList])
const excelDetailDownLoad=() =>{
const sidoList = new Set<string>();
const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map
const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map
// 먼저 시도별 상세 데이터를 그룹화
rainDetailList.forEach((item: RainStatProps[]) => {
// 복사된 배열을 사용
const transformedRainDetailList = rainDetailList.map((originItem: RainStatProps[]) => {
const item = [...originItem]; // 원본 복사
const sidoVal = item.at(0)?.toString().trim();
const sigunVal = item.at(1)?.toString().trim();
let sidoText = '';
let sigunText = '';
const sido = code.cityCd.find((city) => city.val === sidoVal);
const sigun = code.sggCd.find((sgg) => sgg.val === sigunVal);
sidoText = sido?.text || '';
sigunText = sigun?.text || '';
// 시도, 시군구 코드를 텍스트로 변환하여 데이터에 할당
if (item.length > 0) {
item[0] = sidoText as unknown as RainStatProps;
}
if (item.length > 1) {
item[1] = sigunText as unknown as RainStatProps;
}
if (item.length > 2) {
item[2] = sidoText+' '+sigunText as unknown as RainStatProps;
}
const sidoText = sido?.text || '';
const sigunText = sigun?.text || '';
if (item.length > 0) item[0] = sidoText as unknown as RainStatProps;
if (item.length > 1) item[1] = sigunText as unknown as RainStatProps;
if (item.length > 2) item[2] = `${sidoText} ${sigunText}` as unknown as RainStatProps;
// 시도별 상세 데이터 수집
if (sidoText) {
@@ -72,53 +66,40 @@ export default function RainStat() {
// 시도별 합계 데이터 수집
if (sidoText && !sidoList.has(sidoText)) {
sidoList.add(sidoText);
// 시도별 초기 데이터 구조 생성 (예: [시도명, 0, 0, 0, ...])
const initialData: (string | number)[] = [sidoText];
// 데이터의 나머지 컬럼들을 0으로 초기화 (실제 컬럼 수에 맞게 조정 필요)
for (let i = 1; i < item.length; i++) {
initialData.push(0);
}
sidoDataMap.set(sidoText, initialData);
}
return item;
});
// 시도별 합계 계산
rainDetailList.forEach((item: RainStatProps[]) => {
// 이후 합계 계산에서도 transformedRainDetailList 사용
transformedRainDetailList.forEach((item: RainStatProps[]) => {
const sidoText = item[0]?.toString();
if (sidoText && sidoDataMap.has(sidoText)) {
const sidoData = sidoDataMap.get(sidoText)!;
// 각 컬럼의 값을 합계에 더하기 (1번 인덱스부터 시작)
for (let i = 1; i < item.length && i < sidoData.length; i++) {
if(i == 2){
sidoData[i] = '합계';
} else if(i == 3){
// 해당 시도의 데이터 개수를 계산
const sidoDataCount = rainDetailList.filter((data: unknown[]) =>
} else if (i == 3) {
const sidoDataCount = transformedRainDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText
).length;
sidoData[i] = sidoDataCount;
} else if( i == 4){
sidoData[i] = '';
} else if(i == 6){
// 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우)
const bumCnt = rainDetailList.filter((data: unknown[]) =>
} else if (i == 6) {
const bumCnt = transformedRainDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText
).filter((data: unknown[]) => String(data[6]) === '대상').length;
sidoData[i] = bumCnt;
} else if(i==8){
const value = Number(item[8]) || 0;
} else if ([8, 11, 12, 14].includes(i)) {
const value = Number(item[i]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==11){
const value = Number(item[11]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==12){
const value = Number(item[12]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==14){
const value = Number(item[14]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
}
else {
} else {
sidoData[i] = '';
}
}
@@ -144,7 +125,7 @@ export default function RainStat() {
});
const sidoSummaryList = Array.from(sidoDataMap.values());
const sidoTotalSummary = [['전체', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '', 0]];
const sidoTotalSummary = [['전국', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '']];
sidoSummaryList.forEach((item: (string | number)[]) => {
item.forEach((value: string | number, index: number) => {
if(typeof value === 'number') {
@@ -158,48 +139,77 @@ export default function RainStat() {
// sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성
const excelData = [...sidoTotalSummary, ...sidoSummaryList, ...summaryExcelData];
console.log(' excelData ', excelData)
excelDownload(
excelData as unknown as [][],
"빗물이용시설 통계",
"sample_rainStats.xlsx",
"A1:V3",
"A1:O3",
4
);
}
const excelClick = () => {
if(bdstTable){
const tbData = bdstTable .map((item: RainStatProps) => {
return {
"시도": item.cityText,
"개소": item.tmp,
"설치비(백만원)": item.instlCst,
"저류조용량(m³/년)": item.dtfcltCpct,
"연간사용량(m³/년)": item.wtrUsqty,
"조경용수": item.lndwtr,
"청소화장실용수": item.cleanToilUswtr,
"세척살수용수": item.resn,
"농업용수": item.farmUswtr,
"공업용수": item.indstUswtr,
"친수용수": item.hydUswtr,
"기타": item.ithlEtcUswtr,
"연간운영비(백만원/년)": item.anlOperCst,
};
})
downloadExcel({
data: (exData as unknown as JSON[]) ?? [],
data: (tbData as unknown as JSON[]) ?? [],
fileName: "재이용통계",
sheetNameArray: ["Sheet1"],
headerRange: "A1:M1"
})
}else{
toast.error(t('excelDownErrMsg'));
}
}
useEffect(() => {
const transExcel = Array.isArray(excelList) ? excelList : [excelList];
const excelData = tableData(transExcel, { code });
const tbData = Array.isArray(excelData)
? excelData.map((item: RainStatProps) => {
return {
"시도": item.cityText,
"개소": item.tmp,
"설치비(백만원)": item.instlCst,
"저류조용량(m³/년)": item.dtfcltCpct,
"연간사용량(m³/년)": item.wtrUsqty,
"조경용수": item.lndwtr,
"청소화장실용수": item.cleanToilUswtr,
"세척살수용수": item.resn,
"농업용수": item.farmUswtr,
"공업용수": item.indstUswtr,
"친수용수": item.hydUswtr,
"기타": item.ithlEtcUswtr,
"연간운영비(백만원/년)": item.anlOperCst,
};
})
: [];
setExData(tbData);
}, [code, excelList])
// useEffect(() => {
// if(excelList && excelList.length>0){
// console.log('excelList ', excelList)
// const transExcel = Array.isArray(excelList) ? excelList : [excelList];
// const excelData = tableData(transExcel, { code });
// const tbData = Array.isArray(excelData)
// ? excelData
// .filter((item: RainStatProps) => item.cityText !== '없음')
// .map((item: RainStatProps) => {
// return {
// "시도": item.cityText,
// "개소": item.tmp,
// "설치비(백만원)": item.instlCst,
// "저류조용량(m³/년)": item.dtfcltCpct,
// "연간사용량(m³/년)": item.wtrUsqty,
// "조경용수": item.lndwtr,
// "청소화장실용수": item.cleanToilUswtr,
// "세척살수용수": item.resn,
// "농업용수": item.farmUswtr,
// "공업용수": item.indstUswtr,
// "친수용수": item.hydUswtr,
// "기타": item.ithlEtcUswtr,
// "연간운영비(백만원/년)": item.anlOperCst,
// };
// })
// : [];
// setExData(tbData);
// }
// }, [code, excelList])
useEffect(() => {
const transData = Array.isArray(rainStatList) ? rainStatList : [rainStatList];
@@ -208,11 +218,13 @@ export default function RainStat() {
? data.map((item: RainStatProps) => {
return {
...item,
ctpvCd: item.cityText
ctpvCd: item.cityText==='전체'?'전국': item.cityText,
cityText: item.cityText==='전체'?'전국': item.cityText,
};
}).filter((item: RainStatProps) => item.cityText !== '없음')
: [];
setBdstTable(tbData);
}, [code, rainStatList]);
@@ -237,7 +249,7 @@ export default function RainStat() {
/>
</div>
<div className="flex flex-row space-x-2 items-center">
<Button className="" size={"sm"} onClick={()=>{searchRainStatList(searchYear);}}>
<Button className="" size={"sm"} onClick={() => { searchRainStatList(searchYear ,code.cityCd); }}>
조회
</Button>
<Button className="" size={"sm"} onClick={() => excelClick()}>
+67 -60
View File
@@ -14,48 +14,26 @@ import {
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useEffect, useState } from "react";
import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RswtStat() {
const { code, rswtStat, searchRswtStatList } = useWrStore((state) => state);
const { rswtStatList, excelList, rswtDetailList } = rswtStat;
const { rswtStatList, rswtDetailList } = rswtStat;
const [rswtTable, setRswtTable] = useState<RswtStatProps[]>();
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
const [excelData, setExceData] = useState<Record<string, string | number>[]>();
const { t } = useTranslation('base');
useEffect(() => {
searchRswtStatList(searchYear)
}, [searchRswtStatList])
searchRswtStatList(searchYear, code.cityCd)
}, [searchRswtStatList, code.cityCd])
const excelClick = () => {
downloadExcel({
data: (excelData as unknown as JSON[]) ?? [],
fileName: "중수도재이용통계",
sheetNameArray: ["Sheet1"],
headerRange: "A1:N1"
})
}
useEffect(() => {
const transData = Array.isArray(rswtStatList) ? rswtStatList : [rswtStatList];
const data = tableData(transData, { code });
const tbData = Array.isArray(data)
? data.map((item: RswtStatProps) => {
return {
...item,
ctpvCd: item.cityText
};
}).filter((item: RswtStatProps) => item.cityText !== '없음')
: [];
setRswtTable(tbData);
}, [code, rswtStatList])
useEffect(() => {
const transExcel = Array.isArray(excelList) ? excelList : [excelList];
const exData = tableData(transExcel, { code });
const tbData = Array.isArray(exData) ?
exData.map((item: RswtStatProps) => {
if (rswtTable) {
const data = rswtTable.map((item: RswtStatProps) => {
return {
"시도": item.cityText,
"개소": item.ctpvCnt,
@@ -73,17 +51,44 @@ export default function RswtStat() {
"기타": item.ithlEtcUswtr,
"연간운영비(백만원/년)": item.anlOperCst,
};
}) : [];
setExceData(tbData);
}, [code, excelList])
})
downloadExcel({
data: (data as unknown as JSON[]) ?? [],
fileName: "중수도재이용통계",
sheetNameArray: ["Sheet1"],
headerRange: "A1:N1"
})
} else {
toast.error(t("excelDownErrMsg"))
}
}
useEffect(() => {
const transData = Array.isArray(rswtStatList) ? rswtStatList : [rswtStatList];
const cityData = tableData(transData, { code });
const tbData = Array.isArray(cityData)
? cityData.map((item: RswtStatProps) => {
return {
...item,
ctpvCd: item.cityText === '전체' ? '전국' : item.cityText,
cityText: item.cityText === '전체' ? '전국' : item.cityText
};
}) : [];
setRswtTable(tbData);
}, [code, rswtStatList]);
const excelDetailDownLoad = () => {
const excelDetailDownLoad=() =>{
const sidoList = new Set<string>();
const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map
const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map
// 먼저 시도별 상세 데이터를 그룹화
rswtDetailList.forEach((item: RswtStatProps[]) => {
const transformedRswtDetailList = rswtDetailList.map((originItem: RswtStatProps[]) => {
// rswtDetailList.forEach((item: RswtStatProps[]) => {
const item = [...originItem]; // 원본 복사
const sidoVal = item.at(0)?.toString().trim();
const sigunVal = item.at(1)?.toString().trim();
let sidoText = '';
@@ -94,7 +99,7 @@ export default function RswtStat() {
sidoText = sido?.text || '';
sigunText = sigun?.text || '';
// 시도, 시군구 코드를 텍스트로 변환하여 데이터에 할당
if (item.length > 0) {
item[0] = sidoText as unknown as RswtStatProps;
@@ -103,7 +108,7 @@ export default function RswtStat() {
item[1] = sigunText as unknown as RswtStatProps;
}
if (item.length > 2) {
item[2] = sidoText+' '+sigunText as unknown as RswtStatProps;
item[2] = sidoText + ' ' + sigunText as unknown as RswtStatProps;
}
// 시도별 상세 데이터 수집
@@ -113,7 +118,7 @@ export default function RswtStat() {
}
sidoDetailMap.get(sidoText)!.push(item as unknown as (string | number)[]);
}
// 시도별 합계 데이터 수집
if (sidoText && !sidoList.has(sidoText)) {
sidoList.add(sidoText);
@@ -125,50 +130,51 @@ export default function RswtStat() {
}
sidoDataMap.set(sidoText, initialData);
}
return item;
});
// 시도별 합계 계산
rswtDetailList.forEach((item: RswtStatProps[]) => {
transformedRswtDetailList.forEach((item: RswtStatProps[]) => {
const sidoText = item[0]?.toString();
if (sidoText && sidoDataMap.has(sidoText)) {
const sidoData = sidoDataMap.get(sidoText)!;
// 각 컬럼의 값을 합계에 더하기 (1번 인덱스부터 시작)
for (let i = 1; i < item.length && i < sidoData.length; i++) {
if(i == 2){
if (i == 2) {
sidoData[i] = '합계';
} else if(i == 3){
} else if (i == 3) {
// 해당 시도의 데이터 개수를 계산
const sidoDataCount = rswtDetailList.filter((data: unknown[]) =>
const sidoDataCount = rswtDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText
).length;
sidoData[i] = sidoDataCount;
} else if( i == 4){
} else if (i == 4) {
sidoData[i] = '';
} else if(i == 6){
} else if (i == 6) {
// 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우)
const bumCnt = rswtDetailList.filter((data: unknown[]) =>
const bumCnt = rswtDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText
).filter((data: unknown[]) => String(data[6]) === '대상').length;
sidoData[i] = bumCnt;
} else if(i==9){
} else if (i == 9) {
const value = Number(item[9]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==10){
} else if (i == 10) {
const value = Number(item[10]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==11){
} else if (i == 11) {
const value = Number(item[11]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==12){
} else if (i == 12) {
const value = Number(item[12]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
}else if(i==13){
} else if (i == 13) {
const value = Number(item[13]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==14){
} else if (i == 14) {
const value = Number(item[14]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else if(i==16){
} else if (i == 16) {
const value = Number(item[16]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else {
@@ -177,15 +183,15 @@ export default function RswtStat() {
}
}
});
// 시도별로 합계와 상세 데이터를 순서대로 배치
const summaryExcelData: (string | number)[][] = [];
// 시도별로 순회하면서 합계와 상세 데이터를 추가
Array.from(sidoList).forEach((sidoText) => {
const sidoSummary = sidoDataMap.get(sidoText);
const sidoDetails = sidoDetailMap.get(sidoText) || [];
if (sidoSummary) {
// 시도 합계 데이터 추가
summaryExcelData.push(sidoSummary);
@@ -197,10 +203,10 @@ export default function RswtStat() {
});
const sidoSummaryList = Array.from(sidoDataMap.values());
const sidoTotalSummary = [['전체', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '', 0]];
const sidoTotalSummary = [['전국', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '']];
sidoSummaryList.forEach((item: (string | number)[]) => {
item.forEach((value: string | number, index: number) => {
if(typeof value === 'number') {
if (typeof value === 'number') {
if (sidoTotalSummary[0] && index < sidoTotalSummary[0].length) {
sidoTotalSummary[0][index] = Number(sidoTotalSummary[0][index]) + Number(value);
}
@@ -209,13 +215,13 @@ export default function RswtStat() {
});
// sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성
const excelData = [...sidoTotalSummary,...sidoSummaryList, ...summaryExcelData];
const excelData = [...sidoTotalSummary, ...sidoSummaryList, ...summaryExcelData];
excelDownload(
excelData as unknown as [][],
"중수도시설 통계",
"sample_rswtStats.xlsx",
"A1:Q3",
"A1:P3",
4
);
}
@@ -242,7 +248,7 @@ export default function RswtStat() {
className=""
size="sm"
onClick={() => {
searchRswtStatList(searchYear)
searchRswtStatList(searchYear, code.cityCd)
}}
>
조회
@@ -283,6 +289,7 @@ export default function RswtStat() {
</ListDiv>
</div>
</div>
<Toaster position="top-center"></Toaster>
</ContentsDiv>
</>
);
+75
View File
@@ -0,0 +1,75 @@
import { getFetch, getFetchWithCount } from "@/components/lib/api";
import toast from "react-hot-toast";
export const getMenuAuthrts = async (menuCd: number) => {
const response = await getFetch(
"LOCAL",
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
authStateExport.auth = {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
};
};
export type authState = {
auth: {
mngAuthrtYn: string;
inqAuthrtYn: string;
regAuthrtYn: string;
mdfcnAuthrtYn: string;
delAuthrtYn: string;
};
};
export type authType = authState & authActions;
export const authStateExport: authState = {
auth: {
mngAuthrtYn: "Y",
inqAuthrtYn: "Y",
regAuthrtYn: "Y",
mdfcnAuthrtYn: "Y",
delAuthrtYn: "Y",
},
};
export type authActions = {
getMenuAuthrts: (menuCd: number) => void;
};
export const authActionsExport: (set: any) => authActions = (set: any) => {
return {
getMenuAuthrts: async (menuCd: number) => {
try{
const response = await getFetchWithCount( 'LOCAL',
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
);
set((state: authState) => {
return {
...state,
auth: {
mngAuthrtYn: response[0].mngAuthrtYn,
inqAuthrtYn: response[0].mngAuthrtYn,
regAuthrtYn: response[0].mngAuthrtYn,
mdfcnAuthrtYn: response[0].mngAuthrtYn,
delAuthrtYn: response[0].mngAuthrtYn,
},
};
});
}catch(e){
toast.error('권한 정보를 가져오는 중 오류가 발생했습니다. 관리자에게 문의하세요.');
console.error('Error fetching menu authorities:', e);
}
},
};
};
@@ -0,0 +1,19 @@
export interface MenuItem {
menuCd: number;
menuKornNm: string;
menuEngNm: string;
parentId: number | null;
sortSeq: number;
iconNm?: string;
children: MenuItem[] | null;
levelNo: number;
urlPath: string;
menuLnkgTypeCd: string;
menuLnkgSn: number;
menuPath: string;
qrcd: string;
useYn: string;
otptYn: string;
newWndwYn: string;
requiredUrlPath: boolean;
}
@@ -20,7 +20,7 @@ export const coRvwHeadListExport: CommonTableProps[]= [
},
{
id: "daddr",
title: "공장주소",
title: "주소",
visible: true,
},
{
+2 -1
View File
@@ -1,3 +1,4 @@
import type { ChkClass } from "@common/business_components/ui";
import { z } from "zod";
const DemandInfoSchema = z.object({
@@ -130,7 +131,7 @@ export type RvwHistTableType = {
dmndQty: string;
useUsgCd: string;
splyFcltFlg: string;
completeYn: boolean;
completeYn: ChkClass;
dmdInfoSn: string;
};
+52 -33
View File
@@ -29,11 +29,11 @@ type ExcelType = {
[key: string]: string | number | undefined;
};
const excelDownList: ExcelType[] = [];
// const excelDownList: ExcelType[] = [];
type ldrmntActions = {
ldrmntRegData: (data: any[]) => void,
ldrmntInit: (con: string) => void
ldrmntInit: (isExcel: boolean, con: string) => void
ldrmntSearch: (isExcel: boolean, page: number, sggCd: string, total: string, coNm: string) => void;
ldrmntUpdate: (ldrmntRes: customLdrmnt) => void;
ldrmntDelete: () => void;
@@ -58,25 +58,47 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
set: any
) => {
return {
ldrmntInit: async (con: string) => {
ldrmntInit: async (isExcel: boolean,con: string) => {
try {
const ldrmntRes = await getFetchWithCount(
fetchType.WR,
`/reuseFc/request/temp/1/20/${con}`
);
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes.data,
ldrmntResCount: ldrmntRes.count,
isInit: false,
excelList: excelDownList,
},
}
});
if (isExcel) {
const excelDownList: ExcelType[] = [];
ldrmntRes.data.forEach((item: customLdrmnt) => {
excelDownList.push({
ctpvCd: item.ctpvCd,
sggCd: item.sggCd,
dmdCoNm: item.dmdCoNm,
fctryAddr: item.fctryAddr,
fctryMngNo: item.fctryMngNo,
telno: item.telno,
})
})
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
excelList: excelDownList,
ldrmntResCount: ldrmntRes.count,
},
};
});
} else {
set((state: ldrmntState) => {
return {
...state,
ldrmnt: {
...state.ldrmnt,
ldrmntRes: ldrmntRes.data,
ldrmntResCount: ldrmntRes.count,
isInit: false,
},
}
});
}
} catch (e) {
console.log(e)
}
@@ -96,6 +118,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
await deleteFectch(
`/reuseFc/request/temp`,
);
console.log("deleteFectch", deleteFectch)
const response = await postFectch(
"/reuseFc/request/temp",
@@ -121,7 +144,6 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
fetchType.WR,
searchUrl
);
console.log("알이에스", ldrmntRes)
if (isExcel) {
const excelDownList: ExcelType[] = [];
ldrmntRes.data.forEach((item: customLdrmnt) => {
@@ -144,7 +166,6 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
},
};
});
return excelDownList;
} else
set((state: ldrmntState) => {
return {
@@ -157,27 +178,26 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
},
};
});
return ldrmntRes;
} catch (e) {
console.log(e)
}
},
ldrmntUpdate: async (ldrmntRes: customLdrmnt) => {
if(ldrmntRes)
try {
ldrmntRes.rgtr = TEST_UUID;
const res = await putFectch(
`/reuseFc/request/temp`,
ldrmntRes
);
if (ldrmntRes)
try {
ldrmntRes.rgtr = TEST_UUID;
const res = await putFectch(
`/reuseFc/request/temp`,
ldrmntRes
);
if (res.ok) {
toast.success("잠재수요처 상세정보가 수정되었습니다.",)
if (res.ok) {
toast.success("잠재수요처 상세정보가 수정되었습니다.",)
}
} catch (error) {
console.error("Error ldrmntUpdate:", error);
toast.error("잠재수요처 상세정보 수정에 실패하였습니다.")
}
} catch (error) {
console.error("Error ldrmntUpdate:", error);
toast.error("잠재수요처 상세정보 수정에 실패하였습니다.")
}
},
ldrmntDelete: async () => {
try {
@@ -193,7 +213,6 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
await deleteFectch(
`/reuseFc/request/temp`,
);
} catch (error) {
console.log("error", error)
set((state: ldrmntState) => {
@@ -6,6 +6,7 @@ export const ruseHstryHeadListExport:TableUpperProps[]= [
id: "cityText",
title: "시도",
visible: true,
},
{
id: "sggText",
@@ -19,7 +20,7 @@ export const ruseHstryHeadListExport:TableUpperProps[]= [
},
{
id: "daddr",
title: "공장주소",
title: "주소",
visible: true,
},
{
@@ -49,7 +50,7 @@ export const ruseHstryHeadListExport:TableUpperProps[]= [
},
{
id: "dmndQty", //바꾸기
title: "요구수량(M3/d)",
title: "요구수량(m³/일)",
visible: true,
},
{
@@ -130,7 +130,7 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
toast.success("매칭 계산 저장이 완료입니다.");
const rvwHistRes = await irvwHistInit(
1,
10,
13,
new Date().getFullYear().toString(),
new Date().getFullYear().toString(),
);
@@ -217,10 +217,10 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
fetchType.WR
);
if (complet.status === 200) {
toast.success("완료 처리가 완료되었습니다.");
toast.success("매칭완료가 완료되었습니다.");
const rvwHistRes = await irvwHistInit(
1,
10,
13,
new Date().getFullYear().toString(),
new Date().getFullYear().toString(),
);
@@ -236,7 +236,7 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = (
}
} catch (e) {
console.log(" error ", e);
toast.error("완료 처리 중 오류가 발생했습니다.");
toast.error("매칭 완료 처리 중 오류가 발생했습니다.");
}
},
};
@@ -195,7 +195,7 @@ export const rainChkgdnHeadListExport: TableUpperProps[] = [
title: "부과금액(천원)",
upName: "feeHist",
upSequnce: 3,
type: "number",
type: "text",
edit: true,
},
];
@@ -42,17 +42,17 @@ const hdtflObjSchema = z.object({
utztnUsg: z
.string()
.min(1, { message: validationMessages.required("이용 용도") }),
grssClfg: z.number().default(0), // Measurements, 0 is fine as default
combResdch: z.number().default(0), // Measurements, 0 is fine as default
trbt: z.number().default(0), // Measurements, 0 is fine as default
bod: z.number().default(0), // Measurements, 0 is fine as default
smll: z.number().default(0), // Measurements, 0 is fine as default
crmty: z.number().default(0), // Measurements, 0 is fine as default
tn: z.number().default(0), // Measurements, 0 is fine as default
tph: z.number().default(0), // Measurements, 0 is fine as default
ph: z.number().default(0), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0), // Measurements, 0 is fine as default
cdtnv: z.number().default(0), // Measurements, 0 is fine as default
grssClfg: z.number().default(0).nullish(), // Measurements, 0 is fine as default
combResdch: z.number().default(0).nullish(), // Measurements, 0 is fine as default
trbt: z.number().default(0).nullish(), // Measurements, 0 is fine as default
bod: z.number().default(0).nullish(), // Measurements, 0 is fine as default
smll:z.string().default('').nullish(), // Measurements, 0 is fine as default
crmty: z.number().default(0).nullish(), // Measurements, 0 is fine as default
tn: z.number().default(0).nullish(), // Measurements, 0 is fine as default
tph:z.number().default(0).nullish(),// Measurements, 0 is fine as default
ph: z.string().default('').nullish(), // Measurements, 0 is fine as default
clIonQnt: z.number().default(0).nullish(), // Measurements, 0 is fine as default
cdtnv: z.number().default(0).nullish(), // Measurements, 0 is fine as default
emsnExitTp: z.number()
.max(100, { message: "온도는 100이상이 될 수 없습니다." })
.default(0), // Changed max to 100 based on message, // Measurements, 0 is fine as default
@@ -53,7 +53,7 @@ export const pbnstDemandHeadListExport: TableUpperProps[] = [
export const pbnstOrgwtHeadListRxport: TableUpperProps[] = [
{ id: "ruseUtztnFcltNm", title: "하수처리장명" },
{ id: "orgwtWtritQnt", title: "원수취수량", type: "number" },
{ id: "orgwtWtritQnt", title: "원수취수량(m³/일)", type: "number" },
{ id: "tmsYn", title: "하수처리량TMS 전후" },
{ id: "dschgFmYn", title: "하수처리장 방류 유량계 전·후​" },
{ id: "dtlPstnExpln", title: "상세위치", edit: true },

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