Merge branch 'main' into iipbs/sn/dev
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
pnpm_lock.yaml
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
|
||||
@@ -19,14 +19,37 @@ export class TokenManager {
|
||||
}
|
||||
|
||||
public setToken(key: string, value: string): void {
|
||||
// 환경에 따른 도메인 설정
|
||||
const domain = this.getCookieDomain();
|
||||
|
||||
setCookie(key, value, {
|
||||
secure: window.location.protocol === "https:",
|
||||
sameSite: "lax",
|
||||
domain: domain,
|
||||
path: "/", // 모든 경로에서 접근 가능
|
||||
});
|
||||
}
|
||||
|
||||
private getCookieDomain(): string | undefined {
|
||||
const hostname = window.location.hostname;
|
||||
|
||||
// 개발 환경에서는 localhost 도메인 사용
|
||||
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
||||
return undefined; // localhost에서는 도메인 설정하지 않음
|
||||
}
|
||||
|
||||
// 운영 환경에서는 서브도메인을 포함한 최상위 도메인 사용
|
||||
if (hostname.includes("hasuon.or.kr")) {
|
||||
return ".hasuon.or.kr";
|
||||
}
|
||||
|
||||
// 기타 환경에서는 현재 호스트명 사용
|
||||
return hostname;
|
||||
}
|
||||
|
||||
public removeToken(key: string): void {
|
||||
removeCookie(key);
|
||||
const domain = this.getCookieDomain();
|
||||
removeCookie(key, { domain, path: "/" });
|
||||
}
|
||||
|
||||
public setupRefreshTimer(
|
||||
|
||||
+12
-12
@@ -1,22 +1,22 @@
|
||||
# 공단 개발
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_PORT = 11035
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_DM_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_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = https://devreuse.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.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_REUSE_API_URL=http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
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 # 박이정
|
||||
NEXT_PUBLIC_REUSE_API_URL= http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@ NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
NEXT_PUBLIC_PORT = 3005
|
||||
#NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3005/
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3005
|
||||
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_PORT = 11035
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_DM_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_REUSE_API_URL=http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
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 # 박이정
|
||||
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
@@ -1,7 +1,8 @@
|
||||
# 공단 운영
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_PORT = 11035
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = https://reuse.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
|
||||
@@ -5,7 +5,9 @@ import { useDmStore } from '@/app/store';
|
||||
import type { DmStatusDataType } from '@/app/store/wrStatus/type';
|
||||
import { CHART_FORM, MainChart, type ChartConfig } from '@common/business_components';
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { setCookie } from "../../../../src/shared/lib";
|
||||
|
||||
setCookie("site-id-key", process.env.NEXT_PUBLIC_SITE_ID || "DM");
|
||||
|
||||
interface WrStatusChartProps {
|
||||
chartForm: string;
|
||||
@@ -149,7 +151,7 @@ export default function WrStatusPage() {
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(Common.selectSidoData&&Common.selectSidoData.length>0)
|
||||
if(Common.selectSidoData&&Common.selectSidoData.length>1)
|
||||
getDmStatusData(Common.selectSidoData);
|
||||
},[Common.selectSidoData])
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
|
||||
/**
|
||||
* 관망도 API
|
||||
*/
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
|
||||
const REUSE_API_URL = `${process.env.NEXT_PUBLIC_REUSE_API_URL}`
|
||||
import { DEV_URL_WR, DEV_URL_SN } from "@/components/ui/config";
|
||||
|
||||
export type LyrStngProps = {
|
||||
lyr_sn: string
|
||||
@@ -31,7 +29,7 @@ export type LyrStngProps = {
|
||||
export const mapService = {
|
||||
//레이어 설정 조회
|
||||
getLyrStngs: async () => {
|
||||
const response = await fetch(`${API_URL}/lyrStngs/DM_NETWORK`, {
|
||||
const response = await fetch(`${DEV_URL_SN}/app/v1/map/lyrStngs/DM_NETWORK`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
@@ -45,7 +43,7 @@ export const mapService = {
|
||||
return response.json();
|
||||
},
|
||||
getPbnstRuseFclts: async (ruseFcltPk:any) => {
|
||||
const response = await fetch(`${REUSE_API_URL}/reuseFc/pubFc/${ruseFcltPk}`, {
|
||||
const response = await fetch(`${DEV_URL_WR}/api/v1/reuseFc/pubFc/${ruseFcltPk}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
|
||||
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_DM_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 DC_URL = process.env.NEXT_PUBLIC_DC_URL || "";
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
console.log("Middleware 진입:", req.nextUrl.pathname);
|
||||
const pathname = req.nextUrl.pathname;
|
||||
const searchParams = req.nextUrl.searchParams;
|
||||
|
||||
if (pathname.startsWith("/server")) {
|
||||
|
||||
const decodedParams = Array.from(searchParams.entries())
|
||||
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
|
||||
const newUrl = `${SERVER_API_URL}${pathname.replace("/server", "")}${
|
||||
decodedParams ? `?${decodedParams}` : ""
|
||||
}`;
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/geoserver")) {
|
||||
let queryParams = searchParams.toString();
|
||||
const typename = searchParams.get("TYPENAME");
|
||||
|
||||
let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||
|
||||
if (
|
||||
typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설
|
||||
|| typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역
|
||||
|| typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도
|
||||
|| typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
|
||||
) {
|
||||
newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||
}
|
||||
else if (
|
||||
typename == "hasudo:tb_ms_swage_ppl" //관로
|
||||
|| typename == "hasudo:tb_ms_swage_mhl" //맨홀
|
||||
|| typename == "hasudo:tb_ms_swage_pmstn" //펌프장
|
||||
|| typename == "hasudo:tb_ms_stofcbr" //우수토실
|
||||
) {
|
||||
newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
|
||||
}
|
||||
else if (
|
||||
typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실
|
||||
|| typename == "hasudo:om_wqlt_station" //관측소
|
||||
|| typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설
|
||||
|| typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설
|
||||
|| typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설
|
||||
|| typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설
|
||||
|| typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처
|
||||
|| typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
|
||||
){
|
||||
const url = req.nextUrl.clone();
|
||||
url.searchParams.set('TYPENAME', typename.replace("hasudo:",""));
|
||||
queryParams = url.searchParams.toString();
|
||||
newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`;
|
||||
}
|
||||
|
||||
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (
|
||||
pathname.startsWith("/attach-service") ||
|
||||
pathname.startsWith("/cms-service")
|
||||
) {
|
||||
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}`;
|
||||
|
||||
if (token) requestHeaders.set(CLAIM_NAME, token);
|
||||
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
|
||||
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`,
|
||||
);
|
||||
return NextResponse.rewrite(new URL(newUrl), {
|
||||
headers: requestHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/user-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const newUrl = `${SERVER_API_URL}${pathname}`;
|
||||
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`,
|
||||
);
|
||||
return NextResponse.rewrite(new URL(newUrl), {
|
||||
headers: requestHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/dc-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const decodedParams = Array.from(searchParams.entries())
|
||||
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
|
||||
const newUrl = `${DC_URL}${pathname}${decodedParams ? `?${decodedParams}` : ""}`;
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/dc-service' path to: ${newUrl}`,
|
||||
);
|
||||
return NextResponse.rewrite(new URL(newUrl), {
|
||||
headers: requestHeaders,
|
||||
});
|
||||
}
|
||||
return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
"/server/:path*",
|
||||
"/geoserver/:path*",
|
||||
"/attach-service/:path*",
|
||||
"/user-service/:path*",
|
||||
"/cms-service/:path*",
|
||||
"/dc-service/:path*",
|
||||
],
|
||||
|
||||
};
|
||||
@@ -1,7 +1,12 @@
|
||||
import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config";
|
||||
import { fetchType, getFetch } from "@/components/ui/api";
|
||||
import { DEV_URL_FO } from "@/components/ui/config";
|
||||
import { getCookie } from "@common/components/lib/cookie";
|
||||
import type { FacilityInfo, SubsInfo } from "./type";
|
||||
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
const checkWrRegList = async () => {
|
||||
const wrRegList = await getFetch(fetchType.WR, `/reuseFc/pubFc`);
|
||||
return wrRegList;
|
||||
@@ -26,6 +31,8 @@ const getRuseFclt = async (
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(ruseList),
|
||||
});
|
||||
|
||||
@@ -32,7 +32,7 @@ export const DmStatusReducer = (set: any): DmStatusActions => {
|
||||
return {
|
||||
getDmStatusData: async (cityCd: SelectDataType[]) => {
|
||||
const data = await getFetch(fetchType.FO, "/dashBoard/daemin");
|
||||
|
||||
console.log('cityCd ', cityCd)
|
||||
const dataSum = data.data.reduce(
|
||||
(acc: DmStatusDataType, item: DmStatusDataType) => {
|
||||
acc.sewage = (
|
||||
@@ -89,15 +89,19 @@ export const DmStatusReducer = (set: any): DmStatusActions => {
|
||||
const sewageCnt = item.sewageCnt;
|
||||
const rainwaterCnt = item.rainwaterCnt;
|
||||
const reclaimedCnt = item.reclaimedCnt;
|
||||
|
||||
return {
|
||||
region: regionText ? regionText.text : item.region,
|
||||
sewage: sewageCnt + "(" + sewage + ")",
|
||||
rainwater: rainwaterCnt + "(" + rainwater + ")",
|
||||
reclaimed: reclaimedCnt + "(" + reclaimed + ")",
|
||||
};
|
||||
console.log("regionText ", regionText);
|
||||
if (regionText && regionText.text) {
|
||||
return {
|
||||
region: regionText ? regionText.text : item.region,
|
||||
sewage: sewageCnt + "(" + sewage + ")",
|
||||
rainwater: rainwaterCnt + "(" + rainwater + ")",
|
||||
reclaimed: reclaimedCnt + "(" + reclaimed + ")",
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
).filter((item): item is DmStatusBoardType => item !== undefined);
|
||||
|
||||
console.log("boardData ", boardData);
|
||||
|
||||
const chartData = data.data.map((item) => ({
|
||||
label: makeRegion(
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
//8880
|
||||
import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config";
|
||||
import { DEV_URL_FO, DEV_URL_WR } from "../config";
|
||||
import { getCookie } from "@common/components/lib/cookie";
|
||||
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
export const fetchType = {
|
||||
COMMON: "common",
|
||||
@@ -17,6 +22,8 @@ export const getFetch = async (type: string, url: string) => {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,6 +39,8 @@ export const postFetch = async (url: string, params: RequestParams) => {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -47,6 +56,8 @@ export const putFetch = async (url: string, params: RequestParams) => {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -62,6 +73,8 @@ export const deleteFetch = async (url: string, params: RequestParams) => {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { PROXY_HOST, SERVER_API_FO_URL, SERVER_API_WR_URL } from "@/src/shared/config/env";
|
||||
import { PROXY_HOST } from "@/src/shared/config/env";
|
||||
|
||||
export const LOCAL_URL = PROXY_HOST;
|
||||
export const DEV_URL_FO = SERVER_API_FO_URL;
|
||||
export const DEV_URL_WR = SERVER_API_WR_URL;
|
||||
export const DEV_URL_FO = "/operation-service";
|
||||
export const DEV_URL_WR = "/reuse-service";
|
||||
export const DEV_URL_SN = "/sewerage-service";
|
||||
|
||||
+24
-3
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
|
||||
import { SERVER_API_FO_URL, SERVER_API_WR_URL } from "./src/shared/config/env";
|
||||
|
||||
// const CLAIM_NAME = "Authorization";
|
||||
// const AUTH_USER_ID = "token-id";
|
||||
@@ -115,12 +116,30 @@ export function middleware(req: NextRequest) {
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/sewerage-service")) {
|
||||
if (pathname.startsWith("/reuse-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${
|
||||
const newUrl = `${SERVER_API_WR_URL}${pathname.replace("/reuse-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
|
||||
console.log(`[Middleware] Rewriting from '/reuse-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/operation-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SERVER_API_FO_URL}${pathname.replace("/operation-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/operation-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/sewerage-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/sewerage-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
if (pathname.startsWith("/bussiness-juso-service")) {
|
||||
@@ -150,6 +169,8 @@ export const config = {
|
||||
"/user-service/:path*",
|
||||
"/attach-service/:path*",
|
||||
"/geoserver/:path*",
|
||||
"/operation-service/:path*",
|
||||
"/reuse-service/:path*",
|
||||
"/sewerage-service/:path*",
|
||||
"/bussiness-juso-service/:path*",
|
||||
"/",
|
||||
|
||||
+11
-3
@@ -1,7 +1,7 @@
|
||||
import type { NextConfig } from "next";
|
||||
|
||||
const subDomain =
|
||||
process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:3000";
|
||||
process.env.NEXT_PUBLIC_PROXY_HOST || "http://localhost:3000";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "standalone",
|
||||
@@ -22,8 +22,16 @@ const nextConfig: NextConfig = {
|
||||
],
|
||||
afterFiles: [
|
||||
{
|
||||
source: "/pst/:path*",
|
||||
destination: `${subDomain}/pst/:path*`,
|
||||
source: "/sewerage-service/:path*",
|
||||
destination: `${subDomain}/sewerage-service/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/reuse-service/:path*",
|
||||
destination: `${subDomain}/reuse-service/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/operation-service/:path*",
|
||||
destination: `${subDomain}/operation-service/:path*`,
|
||||
},
|
||||
],
|
||||
fallback: [],
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Client } from "@common/network";
|
||||
import { BASE_URL } from "../config/api";
|
||||
import { SITE_ID } from "../config/env";
|
||||
import { setCookie } from "../lib";
|
||||
|
||||
setCookie("site-id-key", SITE_ID);
|
||||
|
||||
export const client = Client.getInstance({
|
||||
baseURL: BASE_URL,
|
||||
siteId: SITE_ID,
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export { client } from "./client";
|
||||
@@ -0,0 +1,8 @@
|
||||
export { getCookie, setCookie, removeCookie } from "./cookie";
|
||||
// export { useMutationWrapper } from "./use-mutation-wrapper";
|
||||
// export { useAlertStore } from "./use-alert-store";
|
||||
// export { useDataTable } from "./use-data-table";
|
||||
// export { useMenuStore } from "./use-menu-store";
|
||||
// export { useUserStore } from "./use-user-store";
|
||||
// export { usePrompt } from "./use-prompt";
|
||||
// export { useSelectPrompt } from "./use-select-prompt";
|
||||
+12
-13
@@ -1,18 +1,17 @@
|
||||
# 공단 개발
|
||||
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://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
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_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
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_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||
|
||||
|
||||
NEXT_PUBLIC_PROXY_HOST = https://devlarva.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.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_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/dc-service
|
||||
+11
-10
@@ -1,18 +1,19 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1
|
||||
#NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
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_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/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_DEV_CM_LOGIN_PAGE_URL = http://localhost:3001/auth/login
|
||||
NEXT_PUBLIC_VDI_DEV_CM_LOGIN_PAGE_URL = http://localhost:3001/auth/login
|
||||
NEXT_PUBLIC_VDI_DEV_CM_LOGIN_PAGE_URL = http://localhost:3001/auth/login
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082
|
||||
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 = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||
|
||||
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com/dc-service
|
||||
REACT_EDITOR=atom
|
||||
@@ -0,0 +1,14 @@
|
||||
NEXT_PUBLIC_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 11300
|
||||
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_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 = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api
|
||||
+8
-23
@@ -1,32 +1,17 @@
|
||||
# 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://con.mind-one.co.kr:11082/api/v1
|
||||
# #NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
# 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_SITE_ID = FO
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
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_PROXY_HOST = https://larva.hasuon.or.kr
|
||||
# NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.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_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/dc-service
|
||||
@@ -1,27 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "./store/common";
|
||||
import { useFoStore } from "./store";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
export default function Base({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { getMenuAuth } = useFoStore((state) => state);
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const handleClick = (id: MenuItem) => {
|
||||
getMenuAuth(id.menuCd);
|
||||
router.push(id.urlPath);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleClick}>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PROXY_HOST, SERVER_API_URL, SERVER_API_WR_URL } from "@/src/shared/config/env";
|
||||
import { PROXY_HOST } from "@/src/shared/config/env";
|
||||
|
||||
export const DEV_URL_FO = SERVER_API_URL;
|
||||
export const DEV_URL_FO = "/operation-service/api/v1";
|
||||
export const LOCAL_URL = PROXY_HOST;
|
||||
export const DEV_URL_WR = SERVER_API_WR_URL;
|
||||
export const DEV_URL_WR = "/reuse-service/api/v1";
|
||||
@@ -1,12 +1,22 @@
|
||||
import { DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "./config/env";
|
||||
import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config";
|
||||
import { DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "./config/env";
|
||||
import { getCookie } from "@common/components/lib/cookie";
|
||||
|
||||
const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || "";
|
||||
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
export const getFetch = async (part : string, url: string) => {
|
||||
let reqUrl = "";
|
||||
reqUrl = part === "fo" ? DEV_URL_FO+url : part === 'local' ? LOCAL_URL+url : DEV_URL_WR+url;
|
||||
reqUrl = PROXY_HOST + reqUrl;
|
||||
const response = await fetch(reqUrl , {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
accept: "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
}
|
||||
});
|
||||
if(!response.ok) {
|
||||
@@ -17,10 +27,12 @@ export const getFetch = async (part : string, url: string) => {
|
||||
}
|
||||
|
||||
export const getFetchAreaList = async (url: string) => {
|
||||
const response = await fetch(url , {
|
||||
const response = await fetch(PROXY_HOST+url , {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
accept: "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
}
|
||||
});
|
||||
if(!response.ok) {
|
||||
@@ -33,10 +45,13 @@ 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_WR+url;
|
||||
reqUrl = PROXY_HOST + reqUrl;
|
||||
const response = await fetch(reqUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -49,10 +64,13 @@ 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_WR+url;
|
||||
reqUrl = PROXY_HOST + reqUrl;
|
||||
const response = await fetch(reqUrl, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -65,10 +83,13 @@ 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_WR+url;
|
||||
const response = await fetch(reqUrl, {
|
||||
reqUrl = PROXY_HOST + reqUrl;
|
||||
const response = await fetch(reqUrl, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
|
||||
+12
-27
@@ -1,29 +1,14 @@
|
||||
"use client"
|
||||
import "./globals.css";
|
||||
import { StoreProvider } from "./store";
|
||||
import { LoginApi } from "@/src/entities/auth";
|
||||
import { useEffect } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { App } from "@/src/app";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
export const metadata: Metadata = {
|
||||
title: "디지털 하수관로",
|
||||
description: "디지털 하수관로 시스템입니다",
|
||||
keywords: ["하수관로"],
|
||||
openGraph: {
|
||||
title: "디지털 하수관로",
|
||||
description: "디지털 하수관로 시스템입니다"
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 렌더 중이 아니라 클라이언트에서만 실행 + 에러 캐치
|
||||
void LoginApi.login({ userId: "CLEANWATER", password: "gktneh12!@" })
|
||||
.catch((e) => {
|
||||
console.error("Auto login failed:", e);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<StoreProvider>
|
||||
{children}
|
||||
</StoreProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
export default App;
|
||||
|
||||
+25
-41
@@ -1,44 +1,28 @@
|
||||
"use client"
|
||||
"use client";
|
||||
import { LoginApi } from "@/src/entities/auth";
|
||||
import { useEffect } from "react";
|
||||
import { AUTH_USER_ID } from "@/src/shared/config";
|
||||
import { getCookie } from "@/src/shared/lib/cookie";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Button } from "@common/components/ui";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2">
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/processFacility"}><Button>처리시설 시설정보</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/flowRate"}><Button>시설별 유량현황</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/dashboard"}><Button>대시보드</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/processArea"}><Button>처리구역정보</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/facilityPrecipitation"}><Button>시설별 강수현황</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/facilityWaterQlty"}><Button>시설별 수질현황</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/planSewage"}><Button>계획하수량 산정지표 현황</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/sewageOccur"}><Button>오수발생량 추정</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/underWaterInput"}><Button>지하수 유입량 추정</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/rdi"}><Button>RDII 조회</Button></Link>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Link href={"./pages/map"}><Button>지도보기</Button></Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const router = useRouter();
|
||||
const checkToken = getCookie(AUTH_USER_ID);
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkToken) {
|
||||
async function doLogin() {
|
||||
try {
|
||||
await LoginApi.login({ userId: "CLEANWATER", password: "gktneh12!@" });
|
||||
router.push("/pages/dashboard");
|
||||
} catch (err) {
|
||||
console.error("로그인 실패", err);
|
||||
}
|
||||
}
|
||||
doLogin();
|
||||
} else {
|
||||
router.push("/pages/dashboard");
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
@@ -1,13 +1,30 @@
|
||||
"use client"
|
||||
|
||||
import Base from "@/app/base";
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "@/app/store/common";
|
||||
import { useFoStore } from "@/app/store";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { getMenuAuth } = useFoStore((state) => state);
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
const handleClick = (id: MenuItem) => {
|
||||
getMenuAuth(id.menuCd);
|
||||
router.push(id.urlPath);
|
||||
};
|
||||
|
||||
return (
|
||||
<Base>{children}</Base>
|
||||
<BaseLayout
|
||||
currentPath={pathname ?? ""}
|
||||
handleMenuClick={handleClick}
|
||||
>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -210,13 +210,13 @@ export default function ProcessFacilitySearch() {
|
||||
const cellClick = (index: number) => {
|
||||
const facility = processFacilityList[index];
|
||||
if (facility) {
|
||||
const fctCpc = String(facility.fctCpc).replace(/,/g, "").trim();
|
||||
const fctCpc = Number(String(facility.fctCpc).replace(/,/g, "").trim());
|
||||
setModalFcltyNm(facility.bscFctNm);
|
||||
setModalFcltyCd(facility.bscFctCd);
|
||||
setModalSido(facility.sido);
|
||||
setModalGugun(facility.gugun);
|
||||
setModalAddr(facility.daddr);
|
||||
setModalFctCpc(Number(fctCpc));
|
||||
setModalFctCpc(fctCpc);
|
||||
setModalTrtFrfNm(facility.trtFrfNm);
|
||||
setModalRnDt(facility.rnDt);
|
||||
setModalCmplDt(facility.cmplDt);
|
||||
|
||||
@@ -232,7 +232,7 @@ export const COD_YEAR = 2021;
|
||||
*/
|
||||
|
||||
// 처리시설 계획정보 호출 (알에스원)
|
||||
export const planInfoUrl = 'https://apigateway.apps.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy';
|
||||
export const planInfoUrl = '/dc-service/api/v1/nowDmgMtncGnsvy';
|
||||
|
||||
export const fetchPlanInfo = async (targetYear : string | undefined, ctpvNm : string | undefined, sggNm : string | undefined, fcltyCd : string | undefined) => {
|
||||
try {
|
||||
|
||||
+45
-39
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { SERVER_API_CS_URL } from "./src/shared/config/env";
|
||||
import { SERVER_API_CS_URL, SERVER_API_FO_URL, SERVER_API_WR_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";
|
||||
@@ -7,6 +7,8 @@ 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 || "";
|
||||
const serverApiUrl = SERVER_API_CS_URL;
|
||||
const DC_URL = process.env.NEXT_PUBLIC_DC_URL || "";
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
console.log("Middleware 진입:", req.nextUrl.pathname);
|
||||
const pathname = req.nextUrl.pathname;
|
||||
@@ -63,12 +65,12 @@ export function middleware(req: NextRequest) {
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/sewerage-service/api/v1/map/geometry")) {
|
||||
if (pathname.startsWith("/sewerage-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${
|
||||
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
|
||||
console.log(`[Middleware] Rewriting from '/sewerage-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
@@ -89,40 +91,42 @@ export function middleware(req: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/operation-service")) {
|
||||
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 = `${serverApiUrl}${pathname}`;
|
||||
|
||||
if (token) requestHeaders.set(CLAIM_NAME, token);
|
||||
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
|
||||
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/operation-service' path to: ${newUrl}`
|
||||
);
|
||||
return NextResponse.rewrite(new URL(newUrl), {
|
||||
headers: requestHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/reuse-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SERVER_API_WR_URL}${pathname.replace("/reuse-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/reuse-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/operation-service")) {
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${SERVER_API_FO_URL}${pathname.replace("/operation-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/operation-service' path to: ${newUrl}`);
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/dc-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const queryParams = searchParams.toString();
|
||||
const newUrl = `${DC_URL}${pathname.replace("/dc-service", "")}${
|
||||
queryParams ? `?${queryParams}` : ""
|
||||
}`;
|
||||
console.log(`[Middleware] Rewriting from '/dc-service' path to: ${newUrl}`);
|
||||
|
||||
const token = req.cookies.get(CLAIM_NAME)?.value;
|
||||
const userId = req.cookies.get(AUTH_USER_ID)?.value;
|
||||
const newUrl = `${serverApiUrl}${pathname}`;
|
||||
|
||||
if (token) requestHeaders.set(CLAIM_NAME, token);
|
||||
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
|
||||
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/reuse-service' path to: ${newUrl}`
|
||||
);
|
||||
return NextResponse.rewrite(new URL(newUrl), {
|
||||
headers: requestHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (pathname.startsWith("/user-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const newUrl = `${SERVER_API_CS_URL}${pathname}`;
|
||||
@@ -135,21 +139,23 @@ export function middleware(req: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
if (pathname === "/") {
|
||||
return NextResponse.redirect(new URL("/pages/dashboard", req.url));
|
||||
}
|
||||
return NextResponse.next();
|
||||
// if (pathname === "/") {
|
||||
// return NextResponse.redirect(new URL("/pages/dashboard", req.url));
|
||||
// }
|
||||
// return NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
matcher: [
|
||||
"/",
|
||||
"/server/:path*",
|
||||
"/geoserver/:path*",
|
||||
"/attach-service/:path*",
|
||||
"/user-service/:path*",
|
||||
"/cms-service/:path*",
|
||||
"/operation-service/:path*",
|
||||
"/reuse-service/:path*",
|
||||
"/",
|
||||
"/server/:path*",
|
||||
"/geoserver/:path*",
|
||||
"/sewerage-service/:path*",
|
||||
"/attach-service/:path*",
|
||||
"/user-service/:path*",
|
||||
"/cms-service/:path*",
|
||||
"/operation-service/:path*",
|
||||
"/reuse-service/:path*",
|
||||
"/dc-service/:path*",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@common/assets": "workspace:*",
|
||||
"@common/business_components": "workspace:*",
|
||||
"@common/components": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.0.10",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import "./styles/base.css";
|
||||
import "./styles/components.css";
|
||||
import "@fontsource-variable/noto-sans-kr";
|
||||
|
||||
import { AppProvider } from "./providers";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const App = async ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||
const theme = (await cookies()).get("site-theme-key");
|
||||
|
||||
return (
|
||||
<html lang="ko" data-theme={theme?.value}>
|
||||
<body>
|
||||
<AppProvider>{children}</AppProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
};
|
||||
|
||||
export { App };
|
||||
@@ -0,0 +1,50 @@
|
||||
"use client";
|
||||
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { MultiBackend, getBackendOptions } from "@minoru/react-dnd-treeview";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { ConfirmProvider } from "@frontend-opensource/use-react-hooks";
|
||||
import { AuthWrapper } from "@/src/entities/auth/lib/auth-wrapper";
|
||||
import { MenuWrapper } from "@/src/entities/menu";
|
||||
import { PromptProvider, StoreProvider } from "@/src/shared/providers";
|
||||
import { AlertDialog } from "@/src/shared/ui/alert-dialog";
|
||||
import { PromptDialog } from "@/src/shared/ui/prompt-dialog";
|
||||
import { ConfirmDialog } from "@/src/shared/ui/confirm-dialog";
|
||||
import { StoreProvider as FoStoreProvider } from "@/app/store";
|
||||
|
||||
|
||||
const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||
const queryClient = new QueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StoreProvider>
|
||||
<PromptProvider>
|
||||
<ConfirmProvider>
|
||||
<AuthWrapper>
|
||||
<MenuWrapper>
|
||||
<DndProvider
|
||||
backend={MultiBackend}
|
||||
options={getBackendOptions()}
|
||||
>
|
||||
<FoStoreProvider>
|
||||
{children}
|
||||
</FoStoreProvider>
|
||||
</DndProvider>
|
||||
</MenuWrapper>
|
||||
<AlertDialog />
|
||||
<PromptDialog />
|
||||
<ConfirmDialog />
|
||||
|
||||
</AuthWrapper>
|
||||
</ConfirmProvider>
|
||||
</PromptProvider>
|
||||
</StoreProvider>
|
||||
<div className="text-2xl">
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</div>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export { AppProvider };
|
||||
@@ -0,0 +1 @@
|
||||
export { AppProvider } from "./app-provider";
|
||||
@@ -0,0 +1,12 @@
|
||||
@import "@common/assets/styles/base.css";
|
||||
@import "@common/assets/styles/diveicon.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.card {
|
||||
@apply relative m-[0.6rem] box-border min-h-[2rem] rounded-[0.5rem] p-[0.6rem] text-[1.3rem] text-[#666];
|
||||
}
|
||||
.card-border {
|
||||
@apply border border-[#dbe2e6] bg-[rgba(255,255,255,0.6)] shadow-blur;
|
||||
}
|
||||
.card-header {
|
||||
@apply -mx-[0.6rem] flex items-center justify-between border-b border-b-[#e1e7eb] p-4;
|
||||
}
|
||||
.card-footer {
|
||||
@apply -mx-[0.6rem] mt-2 flex items-center justify-center border-t border-t-[#e1e7eb] py-4;
|
||||
}
|
||||
|
||||
[data-theme="black"] {
|
||||
.card-border {
|
||||
@apply border-[#3f4042] bg-[rgba(255,255,255,0.065)];
|
||||
}
|
||||
.card-header {
|
||||
@apply border-b-[#3f4042];
|
||||
}
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
@apply w-[200px] h-[40px] px-3 py-2 border border-[#d1d5db] rounded-md bg-white text-sm cursor-pointer;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,54 @@
|
||||
// "use client";
|
||||
"use client";
|
||||
|
||||
// import { useEffect } from "react";
|
||||
// import { useRouter } from "next/navigation";
|
||||
// import { LoginApi } from "../api/login-service";
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { client } from "@/src/shared/api";
|
||||
import type { IUserInfo } from "@/src/shared/store";
|
||||
import { useUserStore, getCookie } from "@/src/shared/lib";
|
||||
import { ENDPOINT, AUTH_USER_ID } from "@/src/shared/config";
|
||||
import { LoginApi } from "../api/login-service";
|
||||
|
||||
// const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
// const router = useRouter();
|
||||
// const { userInfo, setUserInfo } = useUserStore((state) => state);
|
||||
//const LOGIN_PAGE_URL = process.env.NEXT_PUBLIC_DEV_CM_LOGIN_PAGE_URL || "http://localhost:3001/auth/login"; // 개발 환경 URL
|
||||
|
||||
// async function authCheck() {
|
||||
// const userId = getCookie(AUTH_USER_ID);
|
||||
// if (!userId) {
|
||||
// throw new Error("Missing required authentication tokens");
|
||||
// }
|
||||
const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
const router = useRouter();
|
||||
const { userInfo, setUserInfo } = useUserStore((state) => state);
|
||||
|
||||
// const response = await client.get<IUserInfo>(
|
||||
// `${ENDPOINT.USER_SERVICE.USERS}/info/me`,
|
||||
// );
|
||||
// if (!userInfo) setUserInfo(response.data);
|
||||
// }
|
||||
async function authCheck() {
|
||||
const userId = getCookie(AUTH_USER_ID);
|
||||
if (!userId) {
|
||||
throw new Error("Missing required authentication tokens");
|
||||
}
|
||||
|
||||
// useEffect(() => {
|
||||
// (async () => {
|
||||
// try {
|
||||
// await authCheck();
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
const response = await client.get<IUserInfo>(
|
||||
`${ENDPOINT.USER_SERVICE.USERS}/info/me`,
|
||||
);
|
||||
if (!userInfo) setUserInfo(response.data);
|
||||
}
|
||||
|
||||
// try {
|
||||
// await LoginApi.silentRefresh();
|
||||
// await authCheck();
|
||||
// } catch (refreshError) {
|
||||
// console.error("Token refresh failed:", refreshError);
|
||||
// setUserInfo(null);
|
||||
// router.push("/auth/login");
|
||||
// }
|
||||
// }
|
||||
// })();
|
||||
// }, []);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
await authCheck();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
// return <>{children}</>;
|
||||
// };
|
||||
try {
|
||||
await LoginApi.silentRefresh();
|
||||
await authCheck();
|
||||
} catch (refreshError) {
|
||||
console.error("Token refresh failed:", refreshError);
|
||||
setUserInfo(null);
|
||||
router.push("/"); // 개발시 홈으로 돌아가기
|
||||
|
||||
// export { AuthWrapper };
|
||||
// window.location.href = LOGIN_PAGE_URL; // 세션 만료 시 CM 로그인 페이지로 리다이렉트
|
||||
// router.push는 같은 Next.js 앱 내에서만 작동하므로 다른 앱으로 이동할 때는 window.location.href 사용
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export { AuthWrapper };
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { MenuAuthorityApi } from "../api/menu-authority-service";
|
||||
@@ -0,0 +1 @@
|
||||
export { AuthorityButton } from "../ui/authority-button";
|
||||
@@ -0,0 +1,40 @@
|
||||
import { client } from "@/src/shared/api";
|
||||
import { ENDPOINT } from "@/src/shared/config";
|
||||
import type {
|
||||
MenuAuthorityList,
|
||||
MenuAuthorityParams,
|
||||
MenuAuthorityPayload,
|
||||
MenuAuthorityService,
|
||||
PageMenuAuthority,
|
||||
} from "../model/menu-authority-interface";
|
||||
|
||||
export class MenuAuthorityApiService implements MenuAuthorityService {
|
||||
public async getMenuAuthorityList(
|
||||
params: MenuAuthorityParams,
|
||||
): Promise<MenuAuthorityList> {
|
||||
const response = await client.get<MenuAuthorityList>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}`,
|
||||
{ params },
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async getPageMenuAuthority(
|
||||
menuCd: number,
|
||||
): Promise<PageMenuAuthority[]> {
|
||||
const response = await client.get<PageMenuAuthority[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}/pageinfo/${menuCd}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async saveMenuAuthority(data: MenuAuthorityPayload[]): Promise<void> {
|
||||
const response = await client.post<void>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}`,
|
||||
data,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuAuthorityApi = new MenuAuthorityApiService();
|
||||
@@ -0,0 +1,2 @@
|
||||
export { AuthorityButton } from "./ui/authority-button";
|
||||
export { ManagementAuthorityDialog } from "./ui/management-authority-dialog";
|
||||
@@ -0,0 +1,48 @@
|
||||
import { z } from "zod";
|
||||
import type { PageableList, PageableParams } from "@/src/shared/model";
|
||||
|
||||
export interface MenuAuthorityParams extends PageableParams {
|
||||
groupId?: string;
|
||||
menuCd?: number;
|
||||
useYn?: string;
|
||||
}
|
||||
|
||||
export interface PageMenuAuthority {
|
||||
mngAuthrtYn: string;
|
||||
inqAuthrtYn: string;
|
||||
regAuthrtYn: string;
|
||||
mdfcnAuthrtYn: string;
|
||||
delAuthrtYn: string;
|
||||
}
|
||||
|
||||
export const menuAuthoritySchema = z.object({
|
||||
groupId: z.string(),
|
||||
groupNm: z.string(),
|
||||
menuCd: z.number(),
|
||||
mngAuthrtYn: z.string(),
|
||||
inqAuthrtYn: z.string(),
|
||||
regAuthrtYn: z.string(),
|
||||
mdfcnAuthrtYn: z.string(),
|
||||
delAuthrtYn: z.string(),
|
||||
useYn: z.string(),
|
||||
mode: z.string(),
|
||||
});
|
||||
|
||||
export const menuAuthorityPayloadSchema = menuAuthoritySchema.omit({
|
||||
groupNm: true,
|
||||
});
|
||||
|
||||
export const menuAuthorityFormSchema = z.object({
|
||||
menuAuthority: z.array(menuAuthorityPayloadSchema),
|
||||
});
|
||||
|
||||
export type MenuAuthority = z.infer<typeof menuAuthoritySchema>;
|
||||
export type MenuAuthorityList = PageableList<MenuAuthority>;
|
||||
export type MenuAuthorityPayload = z.infer<typeof menuAuthorityPayloadSchema>;
|
||||
export type MenuAuthorityFormData = z.infer<typeof menuAuthorityFormSchema>;
|
||||
|
||||
export interface MenuAuthorityService {
|
||||
getMenuAuthorityList(params: MenuAuthorityParams): Promise<MenuAuthorityList>;
|
||||
getPageMenuAuthority(menuCd: number): Promise<PageMenuAuthority[]>;
|
||||
saveMenuAuthority(data: MenuAuthorityPayload[]): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
import { Checkbox } from "@common/components";
|
||||
import type {
|
||||
MenuAuthority,
|
||||
MenuAuthorityFormData,
|
||||
MenuAuthorityPayload,
|
||||
} from "./menu-authority-interface";
|
||||
|
||||
export const createColumns = (
|
||||
form: UseFormReturn<MenuAuthorityFormData>,
|
||||
): ColumnDef<MenuAuthority>[] => {
|
||||
const getCurrentRow = (i: number) => form.watch("menuAuthority")[i];
|
||||
|
||||
const handleCheckedChange =
|
||||
(i: number, key: keyof MenuAuthorityPayload) => (checked: boolean) => {
|
||||
if (getCurrentRow(i)?.mode === "R") {
|
||||
form.setValue(`menuAuthority.${i}.mode`, "U");
|
||||
}
|
||||
form.setValue(`menuAuthority.${i}.${key}`, checked ? "Y" : "N");
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
accessorKey: "groupNm",
|
||||
header: "그룹명",
|
||||
},
|
||||
{
|
||||
accessorKey: "mngAuthrtYn",
|
||||
header: "관리권한",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={getCurrentRow(row.index)?.mngAuthrtYn === "Y"}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
handleCheckedChange(
|
||||
row.index,
|
||||
"regAuthrtYn",
|
||||
)(checked as boolean);
|
||||
handleCheckedChange(
|
||||
row.index,
|
||||
"mdfcnAuthrtYn",
|
||||
)(checked as boolean);
|
||||
handleCheckedChange(
|
||||
row.index,
|
||||
"delAuthrtYn",
|
||||
)(checked as boolean);
|
||||
handleCheckedChange(
|
||||
row.index,
|
||||
"inqAuthrtYn",
|
||||
)(checked as boolean);
|
||||
}
|
||||
handleCheckedChange(row.index, "mngAuthrtYn")(checked as boolean);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "regAuthrtYn",
|
||||
header: "등록권한",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={getCurrentRow(row.index)?.regAuthrtYn === "Y"}
|
||||
onCheckedChange={(checked) => {
|
||||
if (checked) {
|
||||
handleCheckedChange(
|
||||
row.index,
|
||||
"inqAuthrtYn",
|
||||
)(checked as boolean);
|
||||
}
|
||||
handleCheckedChange(row.index, "regAuthrtYn")(checked as boolean);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "mdfcnAuthrtYn",
|
||||
header: "수정권한 ",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={getCurrentRow(row.index)?.mdfcnAuthrtYn === "Y"}
|
||||
onCheckedChange={handleCheckedChange(row.index, "mdfcnAuthrtYn")}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "delAuthrtYn",
|
||||
header: "삭제권한",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={getCurrentRow(row.index)?.delAuthrtYn === "Y"}
|
||||
onCheckedChange={handleCheckedChange(row.index, "delAuthrtYn")}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: "inqAuthrtYn",
|
||||
header: "조회권한",
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<Checkbox
|
||||
checked={getCurrentRow(row.index)?.inqAuthrtYn === "Y"}
|
||||
onCheckedChange={handleCheckedChange(row.index, "inqAuthrtYn")}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useMenuStore } from "@/src/shared/lib";
|
||||
import { Button, cn, type ButtonProps } from "@common/components";
|
||||
import type { PageMenuAuthority } from "../model/menu-authority-interface";
|
||||
|
||||
type AuthorityButtonProps = {
|
||||
authority: keyof PageMenuAuthority;
|
||||
} & ButtonProps;
|
||||
|
||||
function AuthorityButton({
|
||||
authority,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: AuthorityButtonProps) {
|
||||
const pageMenuAuthority = useMenuStore((state) => state.pageMenuAuthority);
|
||||
|
||||
const isAuthority = pageMenuAuthority?.[authority] === "Y";
|
||||
|
||||
return (
|
||||
<Button className={cn(className, !isAuthority && "hidden")} {...props}>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export { AuthorityButton };
|
||||
@@ -0,0 +1,103 @@
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { getCoreRowModel, useReactTable } from "@tanstack/react-table";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import {
|
||||
Button,
|
||||
DataTable,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@common/components";
|
||||
import { ENDPOINT } from "@/src/shared/config";
|
||||
import { useAlertStore, useMutationWrapper } from "@/src/shared/lib";
|
||||
import {
|
||||
menuAuthorityFormSchema,
|
||||
type MenuAuthorityFormData,
|
||||
type MenuAuthorityPayload,
|
||||
} from "../model/menu-authority-interface";
|
||||
import { createColumns } from "../model/menu-authority-table-columns";
|
||||
import { MenuAuthorityApi } from "../api/menu-authority-service";
|
||||
|
||||
type ManagementAuthorityDialogProps = {
|
||||
menuCd?: number;
|
||||
} & React.ComponentProps<typeof Dialog>;
|
||||
|
||||
function ManagementAuthorityDialog({
|
||||
menuCd,
|
||||
...props
|
||||
}: ManagementAuthorityDialogProps) {
|
||||
const { confirm } = useConfirm();
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const { data: menuAuthorityList } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_AUTHRTS, menuCd],
|
||||
queryFn: () => MenuAuthorityApi.getMenuAuthorityList({ menuCd }),
|
||||
enabled: !!menuCd,
|
||||
});
|
||||
|
||||
const { mutateAsync: saveMenuAuthority } = useMutationWrapper({
|
||||
mutationFn: (data: MenuAuthorityPayload[]) =>
|
||||
MenuAuthorityApi.saveMenuAuthority(data),
|
||||
onSuccess: () => {
|
||||
alert("저장되었습니다.");
|
||||
},
|
||||
});
|
||||
|
||||
const form = useForm<MenuAuthorityFormData>({
|
||||
resolver: zodResolver(menuAuthorityFormSchema),
|
||||
values: {
|
||||
menuAuthority: menuAuthorityList?.content ?? [],
|
||||
},
|
||||
});
|
||||
|
||||
const table = useReactTable({
|
||||
data: menuAuthorityList?.content ?? [],
|
||||
columns: createColumns(form),
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
});
|
||||
|
||||
const handleSave = async () => {
|
||||
if (await confirm("저장하시겠습니까?")) {
|
||||
const data = form.getValues("menuAuthority");
|
||||
const filteredData = data.filter((item) => item.mode !== "R");
|
||||
await saveMenuAuthority(filteredData);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
<DialogTrigger asChild>
|
||||
<Button className="gap-3" disabled={!menuCd}>
|
||||
<i className="diveicon di-personal-information" />
|
||||
권한관리
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>그룹별 메뉴 권한</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="max-h-[40rem] overflow-auto">
|
||||
<DataTable table={table} />
|
||||
</div>
|
||||
<DialogFooter className="!justify-center">
|
||||
<Button size="lg" onClick={handleSave}>
|
||||
저장
|
||||
</Button>
|
||||
<DialogClose asChild>
|
||||
<Button size="lg" color="white">
|
||||
취소
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { ManagementAuthorityDialog };
|
||||
@@ -0,0 +1,14 @@
|
||||
import { client } from "@/src/shared/api";
|
||||
import { ENDPOINT } from "@/src/shared/config";
|
||||
import type { MenuItem, MenuService } from "../model/menu-interface";
|
||||
|
||||
export class MenuApiService implements MenuService {
|
||||
async getMenuRoles(siteId: string): Promise<MenuItem[]> {
|
||||
const response = await client.get<MenuItem[]>(
|
||||
`${ENDPOINT.CMS_SERVICE.MENU_ROLES}/${siteId}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
||||
export const MenuApi = new MenuApiService();
|
||||
@@ -0,0 +1,2 @@
|
||||
export { MenuWrapper } from "./lib/menu-wrapper";
|
||||
export type { MenuItem } from "./model/menu-interface";
|
||||
@@ -0,0 +1,59 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { MenuAuthorityApi } from "@/src/entities/menu-authority/@x/menu";
|
||||
import { useAlertStore, useMenuStore } from "@/src/shared/lib";
|
||||
import { getCookie } from "@/src/shared/lib";;
|
||||
import { SITE_ID } from "@/src/shared/config";
|
||||
import { MenuApi } from "../api/menu-service";
|
||||
import { getCurrentMenu } from "./utils";
|
||||
|
||||
const MenuWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const { pageMenuAuthority, setCurrentMenuInfo, setPageMenuAuthority } =
|
||||
useMenuStore((state) => state);
|
||||
|
||||
const getCurrentMenuInfo = async () => {
|
||||
const menuRoles = await MenuApi.getMenuRoles(SITE_ID);
|
||||
if (!menuRoles) return;
|
||||
return getCurrentMenu(menuRoles, pathname ?? "");
|
||||
};
|
||||
|
||||
const getPageMenuAuthority = async () => {
|
||||
const menuCd = getCookie("menu-code-key");
|
||||
if (!menuCd) return;
|
||||
const response = await MenuAuthorityApi.getPageMenuAuthority(
|
||||
parseInt(menuCd),
|
||||
);
|
||||
return response;
|
||||
};
|
||||
|
||||
const checkInquiryAuthority = () => {
|
||||
return pageMenuAuthority?.inqAuthrtYn === "Y";
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const currentMenu = await getCurrentMenuInfo();
|
||||
if (currentMenu) setCurrentMenuInfo(currentMenu);
|
||||
|
||||
const response = await getPageMenuAuthority();
|
||||
if (response?.length) setPageMenuAuthority(response[0]!);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pageMenuAuthority) return;
|
||||
const inquiryAuthority = checkInquiryAuthority();
|
||||
if (!inquiryAuthority)
|
||||
alert("페이지에 대한 조회 권한이 없습니다.", () => router.push("/"));
|
||||
}, [pageMenuAuthority]);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export { MenuWrapper };
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { MenuItem } from "../model/menu-interface";
|
||||
|
||||
export const getCurrentMenu = (
|
||||
menus: MenuItem[],
|
||||
pathname: string,
|
||||
): MenuItem | null => {
|
||||
for (const menu of menus) {
|
||||
if (
|
||||
menu.urlPath !== "" &&
|
||||
(menu.urlPath === pathname || pathname.startsWith(menu.urlPath))
|
||||
)
|
||||
return menu;
|
||||
|
||||
if (menu.children) {
|
||||
const children = getCurrentMenu(menu.children, pathname);
|
||||
if (children) return children;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
}
|
||||
|
||||
export interface MenuService {
|
||||
getMenuRoles(siteId: string): Promise<MenuItem[]>;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Client } from "@common/network";
|
||||
import { BASE_URL } from "../config/api";
|
||||
import { SITE_ID } from "../config/env";
|
||||
import { setCookie } from "../lib";
|
||||
|
||||
setCookie("site-id-key", SITE_ID);
|
||||
|
||||
export const client = Client.getInstance({
|
||||
baseURL: BASE_URL,
|
||||
siteId: SITE_ID,
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export { client } from "./client";
|
||||
@@ -2,7 +2,7 @@ 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_FO_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";
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export { useMutationWrapper } from "./use-mutation-wrapper";
|
||||
export { useAlertStore } from "./use-alert-store";
|
||||
export { useMenuStore } from "./use-menu-store";
|
||||
export { useUserStore } from "./use-user-store";
|
||||
export { usePrompt } from "./use-prompt";
|
||||
export { getCookie, setCookie, removeCookie } from "./cookie";
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useStore } from "zustand";
|
||||
import { useContext } from "react";
|
||||
import type { AlertStore } from "../store/alert-store";
|
||||
import { StoreContext } from "../providers/store-provider";
|
||||
|
||||
export const useAlertStore = <T>(selector: (store: AlertStore) => T): T => {
|
||||
const storeContext = useContext(StoreContext);
|
||||
|
||||
if (!storeContext) {
|
||||
throw new Error("useAlertStore must be used within StoreProvider");
|
||||
}
|
||||
|
||||
return useStore(storeContext.alertStore, selector);
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useStore } from "zustand";
|
||||
import { useContext } from "react";
|
||||
import type { MenuStore } from "../store/menu-store";
|
||||
import { StoreContext } from "../providers/store-provider";
|
||||
|
||||
export const useMenuStore = <T>(selector: (store: MenuStore) => T): T => {
|
||||
const storeContext = useContext(StoreContext);
|
||||
|
||||
if (!storeContext) {
|
||||
throw new Error("useMenuStore must be used within StoreProvider");
|
||||
}
|
||||
|
||||
return useStore(storeContext.menuStore, selector);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
import { AxiosError } from "axios";
|
||||
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
|
||||
import { useAlertStore } from "./use-alert-store";
|
||||
|
||||
export const useMutationWrapper = <
|
||||
TData = unknown,
|
||||
TError = unknown,
|
||||
TVariables = void,
|
||||
TContext = unknown,
|
||||
>(
|
||||
mutationOptions: UseMutationOptions<TData, TError, TVariables, TContext>,
|
||||
) => {
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
return useMutation({
|
||||
...mutationOptions,
|
||||
onError: (error) => {
|
||||
let errorMessage = "";
|
||||
|
||||
if (error instanceof AxiosError) {
|
||||
if (error.response?.status)
|
||||
errorMessage += `status: ${error.response.status}\n`;
|
||||
if (error.response?.data?.message)
|
||||
errorMessage += `${error.response.data.message}\n`;
|
||||
if (error.response?.data?.errors[0]?.message)
|
||||
errorMessage += `${error.response.data.errors[0].message}\n`;
|
||||
}
|
||||
|
||||
alert(`[Error] ${errorMessage}`);
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { useContext } from "react";
|
||||
import { PromptContext } from "../providers/prompt-provider";
|
||||
|
||||
export const usePrompt = () => {
|
||||
const { message, resolve, setMessage, setResolve } =
|
||||
useContext(PromptContext);
|
||||
|
||||
const prompt = (message: string) => {
|
||||
setMessage(message);
|
||||
return new Promise<{ resolved: boolean; value?: string }>((resolve) => {
|
||||
setResolve(() => resolve);
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = (value: string) => {
|
||||
resolve?.({ resolved: true, value });
|
||||
setMessage("");
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
resolve?.({ resolved: false });
|
||||
setMessage("");
|
||||
};
|
||||
|
||||
return { message, isOpen: !!message, prompt, onSubmit, onCancel };
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useStore } from "zustand";
|
||||
import { useContext } from "react";
|
||||
import type { UserStore } from "../store/user-store";
|
||||
import { StoreContext } from "../providers/store-provider";
|
||||
|
||||
export const useUserStore = <T>(selector: (store: UserStore) => T): T => {
|
||||
const storeContext = useContext(StoreContext);
|
||||
|
||||
if (!storeContext) {
|
||||
throw new Error("useUserStore must be used within StoreProvider");
|
||||
}
|
||||
|
||||
return useStore(storeContext.userStore, selector);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
interface SortConfig {
|
||||
empty: boolean;
|
||||
sorted: boolean;
|
||||
unsorted: boolean;
|
||||
}
|
||||
|
||||
interface PageableConfig {
|
||||
sort: SortConfig;
|
||||
offset: number;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
paged: boolean;
|
||||
unpaged: boolean;
|
||||
}
|
||||
|
||||
export interface PageableList<T> {
|
||||
content: T[];
|
||||
pageable: PageableConfig;
|
||||
sort: SortConfig;
|
||||
totalPages: number;
|
||||
totalElements: number;
|
||||
number: number;
|
||||
numberOfElements: number;
|
||||
size: number;
|
||||
first: boolean;
|
||||
last: boolean;
|
||||
empty: boolean;
|
||||
}
|
||||
|
||||
export interface PageableParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
sort?: string;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./api-interface";
|
||||
@@ -0,0 +1,2 @@
|
||||
export { StoreProvider } from "./store-provider";
|
||||
export { PromptProvider } from "./prompt-provider";
|
||||
@@ -0,0 +1,37 @@
|
||||
import { createContext, useState } from "react";
|
||||
|
||||
interface PromptContext {
|
||||
message?: string;
|
||||
resolve?: ({
|
||||
resolved,
|
||||
value,
|
||||
}: {
|
||||
resolved: boolean;
|
||||
value?: string;
|
||||
}) => void;
|
||||
setMessage: (message: string) => void;
|
||||
setResolve: (resolve: () => void) => void;
|
||||
}
|
||||
|
||||
export const PromptContext = createContext<PromptContext>({
|
||||
message: "",
|
||||
resolve() {},
|
||||
setMessage() {},
|
||||
setResolve() {},
|
||||
});
|
||||
|
||||
export const PromptProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const [message, setMessage] = useState<string>();
|
||||
const [resolve, setResolve] =
|
||||
useState<
|
||||
({ resolved, value }: { resolved: boolean; value?: string }) => void
|
||||
>();
|
||||
|
||||
return (
|
||||
<PromptContext.Provider
|
||||
value={{ message, resolve, setMessage, setResolve }}
|
||||
>
|
||||
{children}
|
||||
</PromptContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
"use client";
|
||||
|
||||
import { createContext, useRef } from "react";
|
||||
import {
|
||||
createAlertStore,
|
||||
createAttachmentStore,
|
||||
createMenuStore,
|
||||
createUserStore,
|
||||
} from "../store";
|
||||
|
||||
type StoreContextType = {
|
||||
alertStore: ReturnType<typeof createAlertStore>;
|
||||
attachmentStore: ReturnType<typeof createAttachmentStore>;
|
||||
menuStore: ReturnType<typeof createMenuStore>;
|
||||
userStore: ReturnType<typeof createUserStore>;
|
||||
};
|
||||
|
||||
export const StoreContext = createContext<StoreContextType | undefined>(
|
||||
undefined,
|
||||
);
|
||||
|
||||
export const StoreProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
const storeRef = useRef<StoreContextType | null>(null);
|
||||
if (storeRef.current === null) {
|
||||
storeRef.current = {
|
||||
alertStore: createAlertStore(),
|
||||
attachmentStore: createAttachmentStore(),
|
||||
menuStore: createMenuStore(),
|
||||
userStore: createUserStore(),
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<StoreContext.Provider value={storeRef.current}>
|
||||
{children}
|
||||
</StoreContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createStore } from "zustand/vanilla";
|
||||
|
||||
export type AlertState = {
|
||||
message: string;
|
||||
callback?: () => void;
|
||||
};
|
||||
|
||||
export type AlertActions = {
|
||||
setMessage: (message: string, callback?: () => void) => void;
|
||||
};
|
||||
|
||||
export type AlertStore = AlertState & AlertActions;
|
||||
|
||||
export const defaultInitState: AlertState = {
|
||||
message: "",
|
||||
callback: undefined,
|
||||
};
|
||||
|
||||
export const createAlertStore = (initState: AlertState = defaultInitState) => {
|
||||
return createStore<AlertStore>()((set) => ({
|
||||
...initState,
|
||||
setMessage: (message: string, callback?: () => void) =>
|
||||
set(() => ({ message, callback })),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
import { createStore } from "zustand/vanilla";
|
||||
|
||||
export type AttachmentState = {
|
||||
attachmentFileCode: string;
|
||||
};
|
||||
|
||||
export type AttachmentActions = {
|
||||
setAttachmentFileCode: (attachmentFileCode: string) => void;
|
||||
};
|
||||
|
||||
export type AttachmentStore = AttachmentState & AttachmentActions;
|
||||
|
||||
export const defaultInitState: AttachmentState = {
|
||||
attachmentFileCode: "",
|
||||
};
|
||||
|
||||
export const createAttachmentStore = (
|
||||
initState: AttachmentState = defaultInitState,
|
||||
) => {
|
||||
return createStore<AttachmentStore>()((set) => ({
|
||||
...initState,
|
||||
setAttachmentFileCode: (attachmentFileCode: string) =>
|
||||
set({ attachmentFileCode }),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
export { createAlertStore } from "./alert-store";
|
||||
export { createAttachmentStore } from "./attachment-store";
|
||||
export { createMenuStore, type BbsType } from "./menu-store";
|
||||
export { createUserStore, type IUserInfo } from "./user-store";
|
||||
@@ -0,0 +1,93 @@
|
||||
import { createStore } from "zustand/vanilla";
|
||||
|
||||
export const BBS_TYPE = {
|
||||
BBSNOTICE: "BBSNOTICE",
|
||||
BBSNORMAL: "BBSNORMAL",
|
||||
BBSFAQ: "BBSFAQ",
|
||||
BBSIMAGE: "BBSIMAGE",
|
||||
BBSMOVIE: "BBSMOVIE",
|
||||
} as const;
|
||||
|
||||
export type BbsType = (typeof BBS_TYPE)[keyof typeof BBS_TYPE];
|
||||
|
||||
export interface IBbs {
|
||||
bbsId: number;
|
||||
bbsTypeCd: BbsType;
|
||||
bbsTypeNm: string;
|
||||
bbsNm: string;
|
||||
bbsExpln: string;
|
||||
rfrncUseYn: string;
|
||||
atflUseYn: string;
|
||||
atflMaxCnt: number;
|
||||
atflMaxFileSz: number;
|
||||
ansUseYn: string;
|
||||
ansUseAuthrt: string;
|
||||
cmntUseYn: string;
|
||||
dgstfnEvlUseYn: string;
|
||||
smsUseYn: string;
|
||||
upndFixUseYn: string;
|
||||
pstgPrdUseYn: string;
|
||||
popupUseYn: string;
|
||||
delYn: string;
|
||||
useYn: string;
|
||||
mdfcnDt: string;
|
||||
mdfrNm: string;
|
||||
}
|
||||
|
||||
interface IMenu {
|
||||
menuCd: number;
|
||||
menuKornNm: string;
|
||||
menuEngNm: string;
|
||||
parentId: number | null;
|
||||
sortSeq: number;
|
||||
iconNm?: string;
|
||||
children: IMenu[] | null;
|
||||
levelNo: number;
|
||||
urlPath: string;
|
||||
menuLnkgTypeCd: string;
|
||||
menuLnkgSn: number;
|
||||
menuPath: string;
|
||||
qrcd: string;
|
||||
useYn: string;
|
||||
otptYn: string;
|
||||
newWndwYn: string;
|
||||
requiredUrlPath: boolean;
|
||||
}
|
||||
|
||||
interface IAuthority {
|
||||
mngAuthrtYn: string;
|
||||
inqAuthrtYn: string;
|
||||
regAuthrtYn: string;
|
||||
mdfcnAuthrtYn: string;
|
||||
delAuthrtYn: string;
|
||||
}
|
||||
|
||||
export type MenuState = {
|
||||
bbsInfo: IBbs | null;
|
||||
currentMenuInfo: IMenu | null;
|
||||
pageMenuAuthority: IAuthority | null;
|
||||
};
|
||||
|
||||
export type MenuActions = {
|
||||
setBbsInfo: (bbs: IBbs) => void;
|
||||
setCurrentMenuInfo: (menu: IMenu) => void;
|
||||
setPageMenuAuthority: (authority: IAuthority) => void;
|
||||
};
|
||||
|
||||
export type MenuStore = MenuState & MenuActions;
|
||||
|
||||
export const defaultInitState: MenuState = {
|
||||
currentMenuInfo: null,
|
||||
bbsInfo: null,
|
||||
pageMenuAuthority: null,
|
||||
};
|
||||
|
||||
export const createMenuStore = (initState: MenuState = defaultInitState) => {
|
||||
return createStore<MenuStore>()((set) => ({
|
||||
...initState,
|
||||
setBbsInfo: (bbs: IBbs) => set({ bbsInfo: bbs }),
|
||||
setCurrentMenuInfo: (menu: IMenu) => set({ currentMenuInfo: menu }),
|
||||
setPageMenuAuthority: (authority: IAuthority) =>
|
||||
set({ pageMenuAuthority: authority }),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
import { createStore } from "zustand/vanilla";
|
||||
|
||||
export interface IUserInfo {
|
||||
regDt: string;
|
||||
mdfcnDt: string;
|
||||
rgtr: string;
|
||||
mdfr: string;
|
||||
adminRoleYn: string;
|
||||
userUnqId: string;
|
||||
userId: string;
|
||||
userName: string;
|
||||
email: string;
|
||||
encryptedPassword: string;
|
||||
refreshToken: string;
|
||||
userStateCode: string;
|
||||
lastLoginDate: string;
|
||||
loginFailCount: number;
|
||||
pswdHnt: string;
|
||||
pswdAns: string;
|
||||
mphn: string;
|
||||
telno: string;
|
||||
zip: string;
|
||||
addr: string;
|
||||
daddr: string;
|
||||
laeSeCd: string;
|
||||
ogdpSeCd: string;
|
||||
aprvYn: string;
|
||||
autzrId: string;
|
||||
expryYmd: string;
|
||||
lastPswdChgDt: string;
|
||||
googleId: string;
|
||||
kakaoId: string;
|
||||
naverId: string;
|
||||
isSocialUser: boolean;
|
||||
hasPassword: boolean;
|
||||
}
|
||||
|
||||
export type UserState = {
|
||||
userInfo: IUserInfo | null;
|
||||
};
|
||||
|
||||
export type UserActions = {
|
||||
setUserInfo: (userInfo: IUserInfo | null) => void;
|
||||
};
|
||||
|
||||
export type UserStore = UserState & UserActions;
|
||||
|
||||
export const defaultInitState: UserState = {
|
||||
userInfo: null,
|
||||
};
|
||||
|
||||
export const createUserStore = (initState: UserState = defaultInitState) => {
|
||||
return createStore<UserStore>()((set) => ({
|
||||
...initState,
|
||||
setUserInfo: (userInfo: IUserInfo | null) => set(() => ({ userInfo })),
|
||||
}));
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
Button,
|
||||
} from "@common/components";
|
||||
import { useAlertStore } from "../lib/use-alert-store";
|
||||
|
||||
function AlertDialog() {
|
||||
const { message, callback, setMessage } = useAlertStore((state) => state);
|
||||
const handleOpenChange = () => {
|
||||
setMessage("");
|
||||
callback?.();
|
||||
};
|
||||
return (
|
||||
<Dialog open={!!message} onOpenChange={handleOpenChange}>
|
||||
<DialogContent aria-describedby={undefined}>
|
||||
<div className="py-10">
|
||||
<p className="whitespace-pre-line text-center text-[1.4rem] text-foreground">
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter className="!justify-center">
|
||||
<DialogClose asChild>
|
||||
<Button>확인</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { AlertDialog };
|
||||
@@ -0,0 +1,48 @@
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
Button,
|
||||
type ButtonProps,
|
||||
} from "@common/components";
|
||||
|
||||
type ConfirmDialogProps = {
|
||||
isOpen?: boolean;
|
||||
confirmButtonProps?: ButtonProps & { label?: string };
|
||||
cancelButtonProps?: ButtonProps & { label?: string };
|
||||
};
|
||||
|
||||
function ConfirmDialog({
|
||||
isOpen,
|
||||
confirmButtonProps,
|
||||
cancelButtonProps,
|
||||
}: ConfirmDialogProps) {
|
||||
const { message, isOpen: isGlobalOpen, onConfirm, onCancel } = useConfirm();
|
||||
return (
|
||||
<Dialog open={isOpen ?? isGlobalOpen} onOpenChange={onCancel}>
|
||||
<DialogContent aria-describedby={undefined}>
|
||||
<div className="py-10">
|
||||
<p className="whitespace-pre-line text-center text-[1.4rem] text-foreground">
|
||||
{message}
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter className="!justify-center">
|
||||
<DialogClose asChild>
|
||||
<Button onClick={onConfirm} {...confirmButtonProps}>
|
||||
{confirmButtonProps?.label ?? "확인"}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button onClick={onCancel} color="white" {...cancelButtonProps}>
|
||||
{cancelButtonProps?.label ?? "취소"}
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { ConfirmDialog };
|
||||
@@ -0,0 +1,13 @@
|
||||
//import { useMenuStore } from "../lib/use-menu-store";
|
||||
|
||||
function MapLayout({ children }: { children: React.ReactNode }) {
|
||||
//const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
|
||||
|
||||
return (
|
||||
<div className="h-auto min-h-full bg-container pb-2">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { MapLayout };
|
||||
@@ -0,0 +1,22 @@
|
||||
import { PageTitle } from "@common/components";
|
||||
import { useMenuStore } from "../lib/use-menu-store";
|
||||
|
||||
function PageLayout({ children }: { children: React.ReactNode }) {
|
||||
const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
|
||||
|
||||
const menuPaths = currentMenuInfo?.menuPath
|
||||
?.split(">")
|
||||
.map((path) => path.trim());
|
||||
|
||||
return (
|
||||
<div className="h-auto min-h-full bg-container pb-2">
|
||||
<PageTitle
|
||||
title={currentMenuInfo?.menuKornNm ?? ""}
|
||||
menuPaths={menuPaths ?? []}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { PageLayout };
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogClose,
|
||||
Input,
|
||||
} from "@common/components";
|
||||
import { usePrompt } from "@/src/shared/lib/use-prompt";
|
||||
|
||||
function PromptDialog() {
|
||||
const { message, isOpen, onSubmit, onCancel } = usePrompt();
|
||||
const [value, setValue] = useState("");
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onCancel}>
|
||||
<DialogContent aria-describedby={undefined}>
|
||||
<div className="flex flex-col gap-6 pb-4 pt-8">
|
||||
<p className="whitespace-pre-line text-center text-[1.4rem] text-foreground">
|
||||
{message}
|
||||
</p>
|
||||
<Input
|
||||
type="text"
|
||||
value={value}
|
||||
className="!text-[1.3rem]"
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<DialogFooter className="!justify-center">
|
||||
<DialogClose asChild>
|
||||
<Button onClick={() => onSubmit(value)}>확인</Button>
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button onClick={onCancel} color="white">
|
||||
취소
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { PromptDialog };
|
||||
@@ -9,4 +9,11 @@ export default {
|
||||
"../../packages/common/business_components/src/ui/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"../../packages/common/components/src/ui/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
boxShadow: {
|
||||
blur: "0 0 10px -1px rgba(0, 0, 0, 0.06)",
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Config;
|
||||
@@ -28,12 +28,8 @@
|
||||
"@/*": ["./*"],
|
||||
"@common/components/*": ["../../packages/common/components/src/*"],
|
||||
"@common/components": ["../../packages/common/components/src/index"],
|
||||
"@common/business_components/*": [
|
||||
"../../packages/common/business_components/src/*"
|
||||
],
|
||||
"@common/business_components": [
|
||||
"../../packages/common/business_components/src/index"
|
||||
],
|
||||
"@common/business_components/*": ["../../packages/common/business_components/src/*"],
|
||||
"@common/business_components": ["../../packages/common/business_components/src/index"],
|
||||
"@common/network": ["../../packages/common/network"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
NEXT_PUBLIC_SITE_ID = SN
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
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_PORT = 11302
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11302
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083
|
||||
NEXT_PUBLIC_PROXY_HOST = https://devlion.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.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_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/dc-service
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
NEXT_PUBLIC_SITE_ID = SN
|
||||
NEXT_PUBLIC_PORT = 11302
|
||||
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_PROXY_HOST = http://con.mind-one.co.kr:11302
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://con.mind-one.co.kr:11083
|
||||
NEXT_PUBLIC_DC_URL = https://apigateway.apps.rsonesoft.com
|
||||
@@ -1,14 +1,14 @@
|
||||
NEXT_PUBLIC_SITE_ID = SN
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
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_PORT = 3000
|
||||
NEXT_PUBLIC_PROXY_HOST = https://lion.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.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_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||
NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/dc-service
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
interface SortConfig {
|
||||
empty: boolean;
|
||||
sorted: boolean;
|
||||
unsorted: boolean;
|
||||
}
|
||||
|
||||
interface PageableConfig {
|
||||
sort: SortConfig;
|
||||
offset: number;
|
||||
pageNumber: number;
|
||||
pageSize: number;
|
||||
paged: boolean;
|
||||
unpaged: boolean;
|
||||
}
|
||||
|
||||
export interface PageableList<T> {
|
||||
content: T[];
|
||||
pageable: PageableConfig;
|
||||
sort: SortConfig;
|
||||
totalPages: number;
|
||||
totalElements: number;
|
||||
number: number;
|
||||
numberOfElements: number;
|
||||
size: number;
|
||||
first: boolean;
|
||||
last: boolean;
|
||||
empty: boolean;
|
||||
}
|
||||
|
||||
export interface PageableParams {
|
||||
page?: number;
|
||||
size?: number;
|
||||
sort?: string;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./api-interface";
|
||||
+14
-25
@@ -1,31 +1,20 @@
|
||||
# NEXT_PUBLIC_SITE_ID = WR
|
||||
# NEXT_PUBLIC_PORT = 11301
|
||||
# NEXT_PUBLIC_PROXY_HOST = http://localhost:3001
|
||||
# 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_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_WR_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_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
# 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_PORT = 3000
|
||||
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_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||
NEXT_PUBLIC_PROXY_HOST = https://devtiger.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.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_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||
+9
-17
@@ -1,29 +1,21 @@
|
||||
NEXT_PUBLIC_SITE_ID = WR
|
||||
NEXT_PUBLIC_PORT = 3001
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3001
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_SERVER_API_URL=http://dev.onasys.co.kr:8000
|
||||
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_PROXY_HOST = http://localhost:3001
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
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_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
# 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_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
|
||||
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PUBLIC_FINDADRESS_URL = https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
|
||||
|
||||
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 # 박이정
|
||||
|
||||
REACT_EDITOR = atom
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
NEXT_PUBLIC_SITE_ID = WR
|
||||
NEXT_PUBLIC_PORT = 11301
|
||||
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_PROXY_HOST = http://con.mind-one.co.kr:11301
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
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_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM=
|
||||
+6
-11
@@ -1,5 +1,10 @@
|
||||
# 공단 운영
|
||||
NEXT_PUBLIC_SITE_ID = WR
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
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_PROXY_HOST = https://tiger.hasuon.or.kr
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
@@ -7,19 +12,9 @@ NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:80
|
||||
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_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service/api
|
||||
NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NEXT_PRIVATE_WR_ADDRESS_KEY = U01TXOFVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA=
|
||||
+12
-47
@@ -1,49 +1,14 @@
|
||||
"use client";
|
||||
import "./globals.css";
|
||||
import i18next from "i18next";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import koBase from "../public/locales/ko/message.json";
|
||||
import { StoreProvider } from "./store";
|
||||
import { LoginApi } from "@/src/entities/auth";
|
||||
import { useEffect } from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { App } from "../src/app";
|
||||
|
||||
const i18n = i18next.createInstance();
|
||||
i18n.init({
|
||||
lng: "ko",
|
||||
fallbackLng: "ko",
|
||||
resources: {
|
||||
ko: {
|
||||
base: koBase,
|
||||
},
|
||||
},
|
||||
});
|
||||
export const metadata: Metadata = {
|
||||
title: "디지털 하수관로",
|
||||
description: "디지털 하수관로 시스템입니다",
|
||||
keywords: ["하수관로"],
|
||||
openGraph: {
|
||||
title: "디지털 하수관로",
|
||||
description: "디지털 하수관로 시스템입니다"
|
||||
}
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
|
||||
useEffect(() => {
|
||||
// 렌더 중이 아니라 클라이언트에서만 실행 + 에러 캐치
|
||||
void LoginApi.login({ userId: "CLEANWATER", password: "gktneh12!@" })
|
||||
.finally(()=>{
|
||||
// getCommonUpCd()
|
||||
} )
|
||||
.catch((e) => {
|
||||
console.error("Auto login failed:", e);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<html lang="ko">
|
||||
<body>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<StoreProvider>
|
||||
{children}
|
||||
</StoreProvider>
|
||||
</I18nextProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
export default App;
|
||||
+24
-159
@@ -1,166 +1,31 @@
|
||||
"use client";
|
||||
import { Button } from "@common/components/ui";
|
||||
|
||||
import { LoginApi } from "../src/entities/auth";
|
||||
import { useEffect } from "react";
|
||||
import { getCookie } from "../src/shared/lib";
|
||||
import { AUTH_USER_ID } from "../src/shared/config";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-8 p-6">
|
||||
{/* 시설 등록 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">시설 등록</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/pbnstReg')}}>
|
||||
하수재이용 시설 등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/rainReg')}}>
|
||||
빗물재이용 시설 등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/rswtReg')}}>
|
||||
중수도시설 등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltReg/htdflReg')}}>
|
||||
온배수시설 등록
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
const userId = "CLEANWATER";
|
||||
const password = "gktneh12!@";
|
||||
const checkToken = getCookie(AUTH_USER_ID);
|
||||
|
||||
{/* 지도점검 결과 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">지도점검 결과</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncReg/pbnstPrfmncReg')}}>
|
||||
하수재이용실적 등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn')}}>
|
||||
하수재이용실적 현황
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncReg/chkgdnReg')}}>
|
||||
지도점검 결과 등록
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/prfmncSttn/chkgdnSttn')}}>
|
||||
지도점검 결과 현황
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 시설 현황 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">시설 현황</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/pbnstSttn')}}>
|
||||
하수재이용시설현황
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/rainSttn')}}>
|
||||
빗물재이용시설현황
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/rswtSttn')}}>
|
||||
중수도이용시설현황
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/ruseFcltMng/fcltSttn/htdflSttn')}}>
|
||||
온배수현황
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 수요처 관리 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">수요처 관리</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMng/coSttn')}}>
|
||||
수요처 관리
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMng/ldrmntCoSttn')}}>
|
||||
잠재수요처 관리
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMng/drgtSttn')}}>
|
||||
우리동네가뭄
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 수요처 매칭 */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">수요처 매칭</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMat/coMatRvw')}}>
|
||||
수요처 매칭 검토
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMat/ruseRvwHstry')}}>
|
||||
재이용검토이력
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/matMng/coMat/ruseCaseMng')}}>
|
||||
물재이용사례 관리
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 통계*/}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">통계 매칭</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/stats/rainStat')}}>
|
||||
빗물재이용시설통계
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/stats/rswtStat')}}>
|
||||
중수도시설통계
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 시설 현황(지도) */}
|
||||
<div>
|
||||
<h2 className="text-xl font-semibold mb-4">시설 현황(지도)</h2>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
<div className="col-span-2">
|
||||
<Button className="w-full" onClick={() => {router.push('/map/mainMap')}}>
|
||||
시설 현황(지도)
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!checkToken) {
|
||||
async function doLogin() {
|
||||
try {
|
||||
await LoginApi.login({ userId, password });
|
||||
window.location.href = "/map/mainMap";
|
||||
} catch (err) {
|
||||
console.error("로그인 실패", err);
|
||||
}
|
||||
}
|
||||
doLogin();
|
||||
} else {
|
||||
router.push("/map/mainMap");
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
@@ -39,8 +39,7 @@ export const authActionsExport: (set: any, get: any) => authActions = (
|
||||
getMenuAuthrts: async (menuCd: number) => {
|
||||
try {
|
||||
const response = await getFetchWithCount(
|
||||
fetchType.LOCAL,
|
||||
|
||||
fetchType.CS,
|
||||
`/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}`
|
||||
);
|
||||
set((state: authState) => {
|
||||
|
||||
@@ -42,7 +42,7 @@ export const getCommonUpCd = async (set: any) => {
|
||||
try {
|
||||
let lwCdCode: Record<string, SelectDataType[]> = {};
|
||||
const upCd = await getFetchWithCount(
|
||||
fetchType.LOCAL,
|
||||
fetchType.CS,
|
||||
"/cms-service/api/v1/up-codes?keyword=&upCd=&cdSe=&useYn=Y&siteId=WR&page=0&size=100&sort=string"
|
||||
);
|
||||
|
||||
@@ -56,7 +56,7 @@ export const getCommonUpCd = async (set: any) => {
|
||||
if (!mapping) return;
|
||||
|
||||
const lwCd = await getFetchWithCount(
|
||||
fetchType.LOCAL,
|
||||
fetchType.CS,
|
||||
`/cms-service/api/v1/lwpo-cds/up-cd/${cd}`
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config";
|
||||
import { DEV_URL_FO, DEV_URL_WR, LOCAL_URL } from "../config/";
|
||||
import { getCookie } from "@common/components/lib/cookie";
|
||||
|
||||
const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST;
|
||||
export const fetchType = {
|
||||
COMMON: "common",
|
||||
WR: "wr",
|
||||
@@ -20,15 +21,15 @@ export const getFetch = async (type: string, url: string) => {
|
||||
: type === fetchType.CS
|
||||
? ""
|
||||
: DEV_URL_WR;
|
||||
|
||||
const apiUrl = PROXY_HOST + div + url;
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
const response = await fetch(div + url, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
AUTH_USER_ID: tokenId ?? "",
|
||||
CLAIM_NAME: Authorization ?? "",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -71,15 +72,16 @@ export const getFetchWithCount = async (type: string, url: string) => {
|
||||
: type === fetchType.CS
|
||||
? ""
|
||||
: DEV_URL_WR;
|
||||
const apiUrl = PROXY_HOST + div + url;
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
const response = await fetch(div + url, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
AUTH_USER_ID: tokenId ?? "",
|
||||
CLAIM_NAME: Authorization ?? "",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -116,16 +118,16 @@ export const getFetchWithCount = async (type: string, url: string) => {
|
||||
|
||||
export const postFectch = async (url: string, params: any, type?: string) => {
|
||||
const div = type === fetchType.COMMON ? DEV_URL_FO : DEV_URL_WR;
|
||||
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
const response = await fetch(div + url, {
|
||||
const apiUrl = PROXY_HOST + div + url;
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
AUTH_USER_ID: tokenId ?? "",
|
||||
CLAIM_NAME: Authorization ?? "",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -144,16 +146,16 @@ export const putFectch = async (url: string, params: any, type?: string) => {
|
||||
: type === fetchType.CS
|
||||
? ""
|
||||
: DEV_URL_WR;
|
||||
|
||||
const apiUrl = PROXY_HOST + div + url;
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
const response = await fetch(div + url, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
AUTH_USER_ID: tokenId ?? "",
|
||||
CLAIM_NAME: Authorization ?? "",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: JSON.stringify(params),
|
||||
});
|
||||
@@ -176,15 +178,16 @@ export const deleteFectch = async (
|
||||
: type === fetchType.CS
|
||||
? ""
|
||||
: DEV_URL_WR;
|
||||
const apiUrl = PROXY_HOST + div + url;
|
||||
const tokenId = getCookie(AUTH_USER_ID);
|
||||
const Authorization = getCookie(CLAIM_NAME);
|
||||
|
||||
const response = await fetch(div + url, {
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
AUTH_USER_ID: tokenId ?? "",
|
||||
CLAIM_NAME: Authorization ?? "",
|
||||
[AUTH_USER_ID]: tokenId ?? "",
|
||||
[CLAIM_NAME]: Authorization ?? "",
|
||||
},
|
||||
body: params ? JSON.stringify(params) : undefined,
|
||||
});
|
||||
|
||||
@@ -2,9 +2,9 @@ import {
|
||||
PROXY_HOST,
|
||||
SERVER_API_CS_TUS_URL,
|
||||
SERVER_API_CS_URL,
|
||||
SERVER_API_FO_URL,
|
||||
SERVER_API_WR_URL,
|
||||
SERVER_SEWERAGE_API_URL,
|
||||
//SERVER_API_FO_URL,
|
||||
//SERVER_API_WR_URL,
|
||||
//SERVER_SEWERAGE_API_URL,
|
||||
} from "@/src/shared/config";
|
||||
|
||||
//const DEV_PORT = "11080";
|
||||
@@ -18,9 +18,10 @@ import {
|
||||
//export const CS_TUS_UPLOAD= "/attach-service/api/v1/tus/upload";
|
||||
|
||||
export const LOCAL_URL_WR = PROXY_HOST + `/api/v1`;
|
||||
export const DEV_URL_WR = SERVER_API_WR_URL;
|
||||
export const DEV_URL_FO = SERVER_API_FO_URL;
|
||||
export const DEV_URL_SN = SERVER_SEWERAGE_API_URL;
|
||||
export const DEV_URL_WR = '/reuse-service';
|
||||
export const DEV_URL_FO = '/operation-service';
|
||||
//export const DEV_URL_FO = SERVER_API_FO_URL;
|
||||
export const DEV_URL_SN = '/sewerage-service/api';
|
||||
export const LOCAL_URL = PROXY_HOST;
|
||||
export const CS_URL = SERVER_API_CS_URL;
|
||||
export const CS_TUS_UPLOAD = SERVER_API_CS_TUS_URL;
|
||||
|
||||
@@ -18,6 +18,9 @@ export function middleware(req: NextRequest) {
|
||||
console.log(`[Middleware] Request URL: ${req.url}`);
|
||||
const searchParams = req.nextUrl.searchParams;
|
||||
|
||||
// if (pathname === "/")
|
||||
// return NextResponse.redirect(new URL("/map/mainMap", req.url));
|
||||
|
||||
if (pathname.startsWith("/server")) {
|
||||
const decodedParams = Array.from(searchParams.entries())
|
||||
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
||||
@@ -29,10 +32,6 @@ export function middleware(req: NextRequest) {
|
||||
return NextResponse.rewrite(new URL(newUrl));
|
||||
}
|
||||
|
||||
if (pathname === "/") {
|
||||
return NextResponse.redirect(new URL("/map/mainMap", req.url));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/cms-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const Authorization = req.cookies.get(CLAIM_NAME)?.value;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import "./styles/base.css";
|
||||
import "./styles/components.css";
|
||||
import "@fontsource-variable/noto-sans-kr";
|
||||
|
||||
import { AppProvider } from "./providers";
|
||||
import { cookies } from "next/headers";
|
||||
|
||||
const App = async ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||
const theme = (await cookies()).get("site-theme-key");
|
||||
|
||||
return (
|
||||
<html lang="ko" data-theme={theme?.value}>
|
||||
<body>
|
||||
<AppProvider>{children}</AppProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
};
|
||||
|
||||
export { App };
|
||||
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import { DndProvider } from "react-dnd";
|
||||
import { MultiBackend, getBackendOptions } from "@minoru/react-dnd-treeview";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
||||
import { ConfirmProvider } from "@frontend-opensource/use-react-hooks";
|
||||
import { AuthWrapper } from "../../entities/auth";
|
||||
import { MenuWrapper } from "../../entities/menu";
|
||||
import { PromptProvider, StoreProvider } from "../../shared/providers";
|
||||
import { AlertDialog } from "../../shared/ui/alert-dialog";
|
||||
import { PromptDialog } from "../../shared/ui/prompt-dialog";
|
||||
import { ConfirmDialog } from "../../shared/ui/confirm-dialog";
|
||||
// import "./globals.css";
|
||||
import i18next from "i18next";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import koBase from "@/public/locales/ko/message.json";
|
||||
import { StoreProvider as WrStoreProvider } from "@/app/store";
|
||||
//import { LoginApi } from "@/src/entities/auth";
|
||||
//import { useEffect } from "react";
|
||||
|
||||
|
||||
const i18n = i18next.createInstance();
|
||||
i18n.init({
|
||||
lng: "ko",
|
||||
fallbackLng: "ko",
|
||||
resources: {
|
||||
ko: {
|
||||
base: koBase,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
|
||||
const queryClient = new QueryClient();
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<StoreProvider>
|
||||
<PromptProvider>
|
||||
<ConfirmProvider>
|
||||
<AuthWrapper>
|
||||
<MenuWrapper>
|
||||
<DndProvider
|
||||
backend={MultiBackend}
|
||||
options={getBackendOptions()}
|
||||
>
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<WrStoreProvider>
|
||||
{children}
|
||||
</WrStoreProvider>
|
||||
</I18nextProvider>
|
||||
</DndProvider>
|
||||
</MenuWrapper>
|
||||
<AlertDialog />
|
||||
<PromptDialog />
|
||||
<ConfirmDialog />
|
||||
</AuthWrapper>
|
||||
</ConfirmProvider>
|
||||
</PromptProvider>
|
||||
</StoreProvider>
|
||||
<div className="text-2xl">
|
||||
<ReactQueryDevtools initialIsOpen={false} />
|
||||
</div>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export { AppProvider };
|
||||
@@ -0,0 +1 @@
|
||||
export { AppProvider } from "./app-provider";
|
||||
@@ -0,0 +1,12 @@
|
||||
@import "@common/assets/styles/base.css";
|
||||
@import "@common/assets/styles/diveicon.css";
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
font-family: "Noto Sans KR", sans-serif;
|
||||
font-size: 10px;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.card {
|
||||
@apply relative m-[0.6rem] box-border min-h-[2rem] rounded-[0.5rem] p-[0.6rem] text-[1.3rem] text-[#666];
|
||||
}
|
||||
.card-border {
|
||||
@apply border border-[#dbe2e6] bg-[rgba(255,255,255,0.6)] shadow-blur;
|
||||
}
|
||||
.card-header {
|
||||
@apply -mx-[0.6rem] flex items-center justify-between border-b border-b-[#e1e7eb] p-4;
|
||||
}
|
||||
.card-footer {
|
||||
@apply -mx-[0.6rem] mt-2 flex items-center justify-center border-t border-t-[#e1e7eb] py-4;
|
||||
}
|
||||
|
||||
[data-theme="black"] {
|
||||
.card-border {
|
||||
@apply border-[#3f4042] bg-[rgba(255,255,255,0.065)];
|
||||
}
|
||||
.card-header {
|
||||
@apply border-b-[#3f4042];
|
||||
}
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
@apply w-[200px] h-[40px] px-3 py-2 border border-[#d1d5db] rounded-md bg-white text-sm cursor-pointer;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export { LoginApi } from "./api/login-service";
|
||||
//export { AuthWrapper } from "./lib/auth-wrapper";
|
||||
export { AuthWrapper } from "./lib/auth-wrapper";
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { client } from "../../../shared/api";
|
||||
import type { IUserInfo } from "../../../shared/store";
|
||||
import { useUserStore } from "../../../shared/lib";
|
||||
import { ENDPOINT, AUTH_USER_ID } from "../../../shared/config";
|
||||
import { getCookie } from "../../../shared/lib";
|
||||
import { LoginApi } from "../api/login-service";
|
||||
|
||||
|
||||
//const LOGIN_PAGE_URL = process.env.NEXT_PUBLIC_DEV_CM_LOGIN_PAGE_URL || "http://localhost:3000/auth/login"; // 로그인 페이지 URL
|
||||
|
||||
const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
const router = useRouter();
|
||||
const { userInfo, setUserInfo } = useUserStore((state) => state);
|
||||
|
||||
async function authCheck() {
|
||||
const userId = getCookie(AUTH_USER_ID);
|
||||
if (!userId) {
|
||||
throw new Error("Missing required authentication tokens");
|
||||
}
|
||||
|
||||
const response = await client.get<IUserInfo>(
|
||||
`${ENDPOINT.USER_SERVICE.USERS}/info/me`,
|
||||
);
|
||||
if (!userInfo) setUserInfo(response.data);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
await authCheck();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
try {
|
||||
await LoginApi.silentRefresh();
|
||||
await authCheck();
|
||||
} catch (refreshError) {
|
||||
console.error("Token refresh failed:", refreshError);
|
||||
setUserInfo(null);
|
||||
router.push("/"); // 개발시 홈으로 돌아가기
|
||||
|
||||
// window.location.href = LOGIN_PAGE_URL; // 세션 만료 시 CM 로그인 페이지로 리다이렉트
|
||||
// router.push는 같은 Next.js 앱 내에서만 작동하므로 다른 앱으로 이동할 때는 window.location.href 사용
|
||||
}
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
export { AuthWrapper };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user