Merge branch 'main' into feature/0612_sentry_fr_merge
# Conflicts: # apps/as/src/pages/instl-sncn/ui/cnfm-view-page.tsx # pnpm-lock.yaml
This commit is contained in:
@@ -29,4 +29,5 @@ settings.json
|
||||
.agent
|
||||
|
||||
apps/**/tsconfig.tsbuildinfo
|
||||
packages/**/tsconfig.tsbuildinfo
|
||||
logs/
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { AcntgCodeManagementPage as default } from "@/pages/acntg-code-management";
|
||||
@@ -1 +0,0 @@
|
||||
export { CnstpCodeManagementPage as default } from "@/pages/cnstp-code-management";
|
||||
@@ -0,0 +1 @@
|
||||
export { CodeManagementPage as default } from "@/pages/code-management";
|
||||
@@ -0,0 +1,10 @@
|
||||
import { FnrsEditPage } from "@/pages/fnrs-cnsltn";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
return <FnrsEditPage id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { FnrsViewPage } from "@/pages/fnrs-cnsltn";
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: PageProps) {
|
||||
const { id } = await params;
|
||||
return <FnrsViewPage id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { FnrsListPage } from "@/pages/fnrs-cnsltn";
|
||||
|
||||
export default FnrsListPage;
|
||||
@@ -0,0 +1,12 @@
|
||||
import { FnrsRegistPage } from "@/pages/fnrs-cnsltn";
|
||||
import { parseTaskSeComCdFromApi } from "@/pages/fnrs-cnsltn/lib/fnrs-cnsltn-regist-schema";
|
||||
|
||||
interface PageProps {
|
||||
searchParams: Promise<{ taskSeComCd?: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ searchParams }: PageProps) {
|
||||
const params = await searchParams;
|
||||
const taskSeComCd = parseTaskSeComCdFromApi(params.taskSeComCd);
|
||||
return <FnrsRegistPage taskSeComCd={taskSeComCd.length ? taskSeComCd : undefined} />;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { InstlEditPage } from "@/pages/instl-sncn";
|
||||
|
||||
interface InstlSncnEditRouteProps {
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ params }: InstlSncnEditRouteProps) {
|
||||
const { id } = await params;
|
||||
return <InstlEditPage id={id} />;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { CnfmModifyPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default async function InstlSncnModifyPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const { id } = await params;
|
||||
return <CnfmModifyPage id={id} />;
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
import { CnfmViewPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default async function InstlSncnViewPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ id: string }>;
|
||||
}) {
|
||||
const { id } = await params;
|
||||
return <CnfmViewPage id={id} />;
|
||||
}
|
||||
import { InstlViewPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default async function InstlSncnDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
return <InstlViewPage id={id} />;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { CnfmListPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default CnfmListPage;
|
||||
import { InstlListPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default InstlListPage;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { InstlRegistPage } from "@/pages/instl-sncn";
|
||||
|
||||
interface InstlSncnRegistRouteProps {
|
||||
searchParams: Promise<{ taskSeComCd?: string }>;
|
||||
}
|
||||
|
||||
export default async function Page({ searchParams }: InstlSncnRegistRouteProps) {
|
||||
const params = await searchParams;
|
||||
const taskSeComCd = params.taskSeComCd
|
||||
? params.taskSeComCd.split(",").filter(Boolean)
|
||||
: undefined;
|
||||
|
||||
return <InstlRegistPage taskSeComCd={taskSeComCd} />;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { CnfmRegistPage } from "@/pages/instl-sncn";
|
||||
|
||||
export default function Page(props: any) {
|
||||
return <CnfmRegistPage {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { cookies } from "next/headers";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
AUTH_USER_ID,
|
||||
CLAIM_NAME,
|
||||
REFRESH_TOKEN,
|
||||
} from "@/shared/config";
|
||||
|
||||
export async function POST() {
|
||||
const cookieStore = await cookies();
|
||||
cookieStore.delete(ACCESS_TOKEN);
|
||||
cookieStore.delete(REFRESH_TOKEN);
|
||||
cookieStore.delete(AUTH_USER_ID);
|
||||
cookieStore.delete(CLAIM_NAME);
|
||||
return NextResponse.json({ message: "Logged out" }, { status: 200 });
|
||||
}
|
||||
@@ -31,6 +31,7 @@
|
||||
"@tanstack/react-query-devtools": "^5.75.5",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"axios": "^1.8.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"js-cookie": "^3.0.5",
|
||||
"lucide-react": "^0.474.0",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { useMenuStore } from "@/shared/lib";
|
||||
import { PageLayout } from "@/shared/ui/page-layout";
|
||||
import { authService } from "@/entities/auth";
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
import { BaseLayout } from "@common/components";
|
||||
|
||||
@@ -32,6 +33,7 @@ export function BaseLayoutWrapper({ children }: BaseLayoutWrapperProps) {
|
||||
return (
|
||||
<BaseLayout
|
||||
queryClient={queryClient}
|
||||
authService={authService}
|
||||
currentPath={pathname ?? ""}
|
||||
handleMenuClick={handleMenuClick}
|
||||
>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
type AcntgSeMngTreeDto,
|
||||
} from "../model/acntg-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_COMMON_SERVICE.ACNTG_SE;
|
||||
const BASE = ENDPOINT.AS_SERVICE.ACNTG_SE;
|
||||
|
||||
async function findAll(params: AcntgSeMngSearchDto): Promise<PageResponse<AcntgSeMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<AcntgSeMngResponseDto>>(BASE, { params });
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
|
||||
import { acntgSeMngService } from "./acntg-service";
|
||||
|
||||
const acntgSeMngBaseKey = [ENDPOINT.AS_COMMON_SERVICE.ACNTG_SE] as const;
|
||||
const acntgSeMngBaseKey = [ENDPOINT.AS_SERVICE.ACNTG_SE] as const;
|
||||
|
||||
export function useAcntgSeMngPage(params: AcntgSeMngSearchDto) {
|
||||
return useQuery({
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface AcntgSeMngResponseDto {
|
||||
cdLvNo: number;
|
||||
useYn: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
mdfr?: string;
|
||||
@@ -39,6 +40,7 @@ export interface AcntgSeMngSaveRequestDto {
|
||||
cdLvNo: number;
|
||||
useYn?: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
}
|
||||
|
||||
/** PUT /{acntgSeCd} 요청 (mode는 서비스에서 U로 설정) */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
CmCdAdstDstrComboResponseDto,
|
||||
CmCdAdstDstrService,
|
||||
} from "../model/adstdstr-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.ADSTDSTR;
|
||||
|
||||
async function findSidoCombo(): Promise<CmCdAdstDstrComboResponseDto[]> {
|
||||
const response = await client.get<CmCdAdstDstrComboResponseDto[]>(`${BASE}/sido`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findGugunCombo(sido: string): Promise<CmCdAdstDstrComboResponseDto[]> {
|
||||
const response = await client.get<CmCdAdstDstrComboResponseDto[]>(`${BASE}/gugun`, {
|
||||
params: { sido },
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const cmCdAdstDstrService = {
|
||||
findSidoCombo,
|
||||
findGugunCombo,
|
||||
} satisfies CmCdAdstDstrService;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import { cmCdAdstDstrService } from "./adstdstr-service";
|
||||
|
||||
const cmCdAdstDstrBaseKey = [ENDPOINT.AS_SERVICE.ADSTDSTR] as const;
|
||||
|
||||
export function useFindSidoCombo() {
|
||||
return useQuery({
|
||||
queryKey: [...cmCdAdstDstrBaseKey, "findSidoCombo"],
|
||||
queryFn: () => cmCdAdstDstrService.findSidoCombo(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFindGugunCombo(sido: string) {
|
||||
return useQuery({
|
||||
queryKey: [...cmCdAdstDstrBaseKey, "findGugunCombo", sido],
|
||||
queryFn: () => cmCdAdstDstrService.findGugunCombo(sido),
|
||||
enabled: Boolean(sido),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export { cmCdAdstDstrService } from "./api/adstdstr-service";
|
||||
export { useFindGugunCombo, useFindSidoCombo } from "./api/queries";
|
||||
export type {
|
||||
CmCdAdstDstrComboResponseDto,
|
||||
CmCdAdstDstrService,
|
||||
} from "./model/adstdstr-interface";
|
||||
@@ -0,0 +1,11 @@
|
||||
/** 백엔드 CmCdAdstDstrComboResponseDto */
|
||||
export interface CmCdAdstDstrComboResponseDto {
|
||||
adstDstrCd: string;
|
||||
adstDstrNm: string;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface CmCdAdstDstrService {
|
||||
findSidoCombo(): Promise<CmCdAdstDstrComboResponseDto[]>;
|
||||
findGugunCombo(sido: string): Promise<CmCdAdstDstrComboResponseDto[]>;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type { AttachService } from "../model/attach-interface";
|
||||
|
||||
async function deleteAttachmentByAtflId(atflId: string): Promise<void> {
|
||||
if (!atflId) return;
|
||||
await client.delete(`${ENDPOINT.ATTACH_SERVICE.ATTACHMENTS}/${atflId}`);
|
||||
}
|
||||
|
||||
async function downloadAttachmentByAtflCd(atflCd: string): Promise<AxiosResponse> {
|
||||
return await client.get<Blob>(`${ENDPOINT.ATTACH_SERVICE.DOWNLOAD}/${atflCd}`, {
|
||||
responseType: "blob",
|
||||
});
|
||||
}
|
||||
|
||||
export const attachService = {
|
||||
deleteAttachmentByAtflId,
|
||||
downloadAttachmentByAtflCd,
|
||||
} satisfies AttachService;
|
||||
@@ -0,0 +1,39 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import { parseFilenameFromResponse } from "../lib/parse-filename-from-response";
|
||||
|
||||
import { attachService } from "./attach-service";
|
||||
|
||||
const attachBaseKey = [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS] as const;
|
||||
const attachDownloadBaseKey = [ENDPOINT.ATTACH_SERVICE.DOWNLOAD] as const;
|
||||
|
||||
export function useDeleteAttachment() {
|
||||
return useMutation({
|
||||
mutationKey: [...attachBaseKey, "delete"],
|
||||
mutationFn: (atflId: string) => attachService.deleteAttachmentByAtflId(atflId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDownloadAttachmentByAtflCd() {
|
||||
return useMutation({
|
||||
mutationKey: [...attachDownloadBaseKey, "download"],
|
||||
mutationFn: async (atflCd: string) => {
|
||||
const response = await attachService.downloadAttachmentByAtflCd(atflCd);
|
||||
const filename = parseFilenameFromResponse(response);
|
||||
|
||||
const blob = new Blob([response.data]);
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(link);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export { attachService } from "./api/attach-service";
|
||||
export { useDeleteAttachment, useDownloadAttachmentByAtflCd } from "./api/queries";
|
||||
export type { Attach, AttachService } from "./model/attach-interface";
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { AxiosResponse } from "axios";
|
||||
|
||||
export function parseFilenameFromResponse(response: AxiosResponse): string {
|
||||
const contentDisposition = response.headers["content-disposition"];
|
||||
if (!contentDisposition) return "download";
|
||||
|
||||
const filenameMatch = contentDisposition.match(/filename\*=UTF-8''([^;]+)/);
|
||||
if (!filenameMatch?.[1]) return "download";
|
||||
|
||||
return decodeURIComponent(filenameMatch[1]);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AxiosResponse } from "axios";
|
||||
|
||||
/** 백엔드 AtflMngResponseDto */
|
||||
export interface Attach {
|
||||
atflCd: string;
|
||||
atflSn: number;
|
||||
atflId: string;
|
||||
physFileNm: string;
|
||||
orgnlFileNm: string;
|
||||
atflFlieSz: number;
|
||||
atflType: string;
|
||||
dwnldNocs: number;
|
||||
delYn: string;
|
||||
lnkgDmnId: string;
|
||||
lnkgDmnNm: string;
|
||||
regDt: string;
|
||||
}
|
||||
|
||||
export interface AttachService {
|
||||
deleteAttachmentByAtflId: (atflId: string) => Promise<void>;
|
||||
downloadAttachmentByAtflCd: (atflCd: string) => Promise<AxiosResponse>;
|
||||
}
|
||||
@@ -1,8 +1,19 @@
|
||||
import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config";
|
||||
import { getRemainingMs } from "@common/auth";
|
||||
import {
|
||||
ACCESS_TOKEN,
|
||||
AUTH_LOGIN_REDIRECT_URL,
|
||||
AUTH_USER_ID,
|
||||
CLAIM_NAME,
|
||||
ENDPOINT,
|
||||
} from "@/shared/config";
|
||||
import { type AuthPayload, type AuthService, getRemainingMs } from "@common/auth";
|
||||
|
||||
import { clearAuthToken, setToken, setupRefreshTimer } from "../lib/token-manager";
|
||||
import type { AuthPayload, AuthService } from "../model/auth-interface";
|
||||
import {
|
||||
clearAuthToken,
|
||||
clearRefreshTimer,
|
||||
removeToken,
|
||||
setToken,
|
||||
setupRefreshTimer,
|
||||
} from "../lib/token-manager";
|
||||
|
||||
async function login(payload: AuthPayload): Promise<void> {
|
||||
const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, {
|
||||
@@ -66,14 +77,28 @@ async function silentRefresh(): Promise<void> {
|
||||
}
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
try {
|
||||
await fetch("/api/auth/logout", {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Logout API failed:", error);
|
||||
}
|
||||
|
||||
clearAuthToken();
|
||||
removeToken("site-id-key");
|
||||
removeToken("site-theme-key");
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(new CustomEvent("auth:logout"));
|
||||
window.location.href = AUTH_LOGIN_REDIRECT_URL;
|
||||
}
|
||||
}
|
||||
|
||||
export const authService: AuthService = {
|
||||
login,
|
||||
silentRefresh,
|
||||
declineSessionExtend: clearRefreshTimer,
|
||||
logout,
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ export const {
|
||||
setToken,
|
||||
removeToken,
|
||||
setupRefreshTimer,
|
||||
clearRefreshTimer,
|
||||
clearAuthToken,
|
||||
getAccessToken,
|
||||
getRefreshToken,
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
export interface AuthPayload {
|
||||
userId: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export interface AuthService {
|
||||
login(payload: AuthPayload): Promise<void>;
|
||||
silentRefresh(): Promise<void>;
|
||||
logout(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import { buildBnsSearchDto } from "../lib/build-bns-search-dto";
|
||||
import type {
|
||||
BnsDetailResponseWrapperDto,
|
||||
BnsIfService,
|
||||
BnsListResponseWrapperDto,
|
||||
BnsSearchDto,
|
||||
EmCodeListResponseDto,
|
||||
} from "../model/bns-if-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.BNS_IF;
|
||||
|
||||
async function getBnsList(params: BnsSearchDto): Promise<BnsListResponseWrapperDto> {
|
||||
const response = await client.get<BnsListResponseWrapperDto>(`${BASE}/bns`, {
|
||||
params: buildBnsSearchDto(params),
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getBnsDetail(
|
||||
gbn: string,
|
||||
yr: string,
|
||||
seqNo: string,
|
||||
): Promise<BnsDetailResponseWrapperDto> {
|
||||
const response = await client.get<BnsDetailResponseWrapperDto>(`${BASE}/bns/detail`, {
|
||||
params: { gbn, yr, seqNo },
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getEmCodeList(
|
||||
dpmCd?: string,
|
||||
lclgvrnmntCd?: string,
|
||||
): Promise<EmCodeListResponseDto> {
|
||||
const response = await client.get<EmCodeListResponseDto>(`${BASE}/emCodeList`, {
|
||||
params: { dpmCd, lclgvrnmntCd },
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const bnsIfService = {
|
||||
getBnsList,
|
||||
getBnsDetail,
|
||||
getEmCodeList,
|
||||
} satisfies BnsIfService;
|
||||
@@ -0,0 +1,33 @@
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type { BnsSearchDto } from "../model/bns-if-interface";
|
||||
|
||||
import { bnsIfService } from "./bns-if-service";
|
||||
|
||||
const bnsIfBaseKey = [ENDPOINT.AS_SERVICE.BNS_IF] as const;
|
||||
|
||||
export function useBnsList(params: BnsSearchDto, options?: { enabled?: boolean }) {
|
||||
return useQuery({
|
||||
queryKey: [...bnsIfBaseKey, "getBnsList", params],
|
||||
queryFn: () => bnsIfService.getBnsList(params),
|
||||
enabled: options?.enabled,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
}
|
||||
|
||||
export function useBnsDetail(gbn: string, yr: string, seqNo: string) {
|
||||
return useQuery({
|
||||
queryKey: [...bnsIfBaseKey, "getBnsDetail", gbn, yr, seqNo],
|
||||
queryFn: () => bnsIfService.getBnsDetail(gbn, yr, seqNo),
|
||||
enabled: Boolean(gbn && yr && seqNo),
|
||||
});
|
||||
}
|
||||
|
||||
export function useEmCodeList(dpmCd?: string, lclgvrnmntCd?: string) {
|
||||
return useQuery({
|
||||
queryKey: [...bnsIfBaseKey, "getEmCodeList", dpmCd, lclgvrnmntCd],
|
||||
queryFn: () => bnsIfService.getEmCodeList(dpmCd, lclgvrnmntCd),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export { bnsIfService } from "./api/bns-if-service";
|
||||
export { useBnsDetail, useBnsList, useEmCodeList } from "./api/queries";
|
||||
export { buildBnsSearchDto } from "./lib/build-bns-search-dto";
|
||||
export type {
|
||||
BnsDetailResponseDto,
|
||||
BnsDetailResponseWrapperDto,
|
||||
BnsIfService,
|
||||
BnsListResponseDto,
|
||||
BnsListResponseWrapperDto,
|
||||
BnsSearchDto,
|
||||
EmCodeItemDto,
|
||||
EmCodeListResponseDto,
|
||||
} from "./model/bns-if-interface";
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { BnsSearchDto } from "../model/bns-if-interface";
|
||||
|
||||
export function buildBnsSearchDto(input: {
|
||||
cPage: number;
|
||||
searchStartYear?: string;
|
||||
searchSido?: string;
|
||||
searchGugun?: string;
|
||||
}): BnsSearchDto {
|
||||
const dto: BnsSearchDto = { cPage: input.cPage };
|
||||
|
||||
const year = input.searchStartYear?.trim();
|
||||
const sido = input.searchSido?.trim();
|
||||
const gugun = input.searchGugun?.trim();
|
||||
|
||||
if (year) dto.searchStartYear = year;
|
||||
if (sido) dto.searchSido = sido;
|
||||
if (gugun) dto.searchGugun = gugun;
|
||||
|
||||
return dto;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/** 백엔드 BnsSearchDto */
|
||||
export interface BnsSearchDto {
|
||||
searchStartYear?: string;
|
||||
cPage: number;
|
||||
searchSido?: string;
|
||||
searchGugun?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 BnsListResponseWrapperDto */
|
||||
export interface BnsListResponseWrapperDto {
|
||||
list: BnsListResponseDto[];
|
||||
cnt: number;
|
||||
}
|
||||
|
||||
/** 백엔드 BnsListResponseDto */
|
||||
export interface BnsListResponseDto {
|
||||
cPage: number;
|
||||
gbn: number;
|
||||
chk: number;
|
||||
seqNo: string;
|
||||
rgnNm: string;
|
||||
sido: string;
|
||||
gugun: string;
|
||||
bzCd: string;
|
||||
bzNm: string;
|
||||
yr: string;
|
||||
strtYr: string;
|
||||
edYr: string;
|
||||
totBzamt: string;
|
||||
}
|
||||
|
||||
/** 백엔드 BnsDetailResponseWrapperDto */
|
||||
export interface BnsDetailResponseWrapperDto {
|
||||
saup: unknown;
|
||||
detail: BnsDetailResponseDto;
|
||||
}
|
||||
|
||||
/** 백엔드 BnsDetailResponseDto */
|
||||
export interface BnsDetailResponseDto {
|
||||
cPage: number;
|
||||
seqNo: string;
|
||||
confBzCd: string;
|
||||
chngDgr: string;
|
||||
yr: string;
|
||||
sido: string;
|
||||
gugun: string;
|
||||
rvbsnNm: string;
|
||||
lgbsnNm: string;
|
||||
mdmbsnNm: string;
|
||||
lclgvrnmntNm: string;
|
||||
accKdNm: string;
|
||||
bzKdNm: string;
|
||||
untBzNm: string;
|
||||
dtlBzNm: string;
|
||||
mevBzNm: string;
|
||||
ddtlBzNm: string;
|
||||
strtYr: string;
|
||||
edYr: string;
|
||||
aidRt: string;
|
||||
bzStatus: string;
|
||||
bzCd: string;
|
||||
bzNm: string;
|
||||
bzLoc: string;
|
||||
efflWqltStndrdNm: string;
|
||||
goalYear: string;
|
||||
bssProm: string;
|
||||
uppPlan: string;
|
||||
techInsp: string;
|
||||
bssCont: string;
|
||||
rmk: string;
|
||||
inflBscFctNm: string;
|
||||
inflBscFctCd: string;
|
||||
trtMtd: string;
|
||||
swrExtns: string;
|
||||
swrChng: string;
|
||||
swrRnvt: string;
|
||||
totFamNum: string;
|
||||
pumpCnt: string;
|
||||
rvWbsn: string;
|
||||
rvText: string;
|
||||
imprvExpctFlw: string;
|
||||
imprvExpctWqlt: string;
|
||||
trtplntPlEfflWqlt: string;
|
||||
wqltAvg: string;
|
||||
wqltHgh: string;
|
||||
wqltLow: string;
|
||||
stdCost: string;
|
||||
bzEtc: string;
|
||||
bscFctCd: string;
|
||||
bscFctNm: string;
|
||||
fctCpc: string;
|
||||
fctCpcAfr: string;
|
||||
trtDstrArea: string;
|
||||
trtDstrAreaAfr: string;
|
||||
swrTrtPopl: string;
|
||||
swrTrtPoplAfr: string;
|
||||
adstPopl: string;
|
||||
}
|
||||
|
||||
/** 백엔드 EmCodeItemDto */
|
||||
export interface EmCodeItemDto {
|
||||
code: string;
|
||||
data: string;
|
||||
upCode: string;
|
||||
}
|
||||
|
||||
/** 백엔드 EmCodeListResponseDto */
|
||||
export interface EmCodeListResponseDto {
|
||||
rvbsnList: EmCodeItemDto[];
|
||||
lgbsnList: EmCodeItemDto[];
|
||||
mdmbsnList: EmCodeItemDto[];
|
||||
accList: EmCodeItemDto[];
|
||||
bzList: EmCodeItemDto[];
|
||||
untList: EmCodeItemDto[];
|
||||
dtlList: EmCodeItemDto[];
|
||||
mevList: EmCodeItemDto[];
|
||||
ddtlList: EmCodeItemDto[];
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface BnsIfService {
|
||||
getBnsList(params: BnsSearchDto): Promise<BnsListResponseWrapperDto>;
|
||||
getBnsDetail(gbn: string, yr: string, seqNo: string): Promise<BnsDetailResponseWrapperDto>;
|
||||
getEmCodeList(dpmCd?: string, lclgvrnmntCd?: string): Promise<EmCodeListResponseDto>;
|
||||
}
|
||||
@@ -11,33 +11,25 @@ import type {
|
||||
CklstArtclUpdateRequest,
|
||||
} from "../model/cklst-artcl-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.CKLST_ARTCL;
|
||||
|
||||
async function findPage(params: CklstArtclSearchParams): Promise<PageResponse<CklstArtcl>> {
|
||||
const response = await client.get<PageResponse<CklstArtcl>>(
|
||||
ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL,
|
||||
{ params },
|
||||
);
|
||||
const response = await client.get<PageResponse<CklstArtcl>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findByCklstSn(cklstSn: number): Promise<CklstArtcl[]> {
|
||||
const response = await client.get<CklstArtcl[]>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL}/by-cklst/${cklstSn}`,
|
||||
);
|
||||
const response = await client.get<CklstArtcl[]>(`${BASE}/by-cklst/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(cklstArtclSn: number, cklstSn: number): Promise<CklstArtcl> {
|
||||
const response = await client.get<CklstArtcl>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL}/${cklstArtclSn}/${cklstSn}`,
|
||||
);
|
||||
const response = await client.get<CklstArtcl>(`${BASE}/${cklstArtclSn}/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function create(body: CklstArtclSaveRequest): Promise<CklstArtclId> {
|
||||
const response = await client.post<CklstArtclId>(
|
||||
ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL,
|
||||
body,
|
||||
);
|
||||
const response = await client.post<CklstArtclId>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -46,17 +38,12 @@ async function update(
|
||||
cklstSn: number,
|
||||
body: CklstArtclUpdateRequest,
|
||||
): Promise<CklstArtclId> {
|
||||
const response = await client.put<CklstArtclId>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL}/${cklstArtclSn}/${cklstSn}`,
|
||||
body,
|
||||
);
|
||||
const response = await client.put<CklstArtclId>(`${BASE}/${cklstArtclSn}/${cklstSn}`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function remove(cklstArtclSn: number, cklstSn: number): Promise<CklstArtclId> {
|
||||
const response = await client.delete<CklstArtclId>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL}/${cklstArtclSn}/${cklstSn}`,
|
||||
);
|
||||
const response = await client.delete<CklstArtclId>(`${BASE}/${cklstArtclSn}/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,40 +11,39 @@ import type {
|
||||
CklstTreeSearchParams,
|
||||
} from "../model/cklst-mng-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.CKLST;
|
||||
|
||||
async function findPage(params: CklstMngSearchParams): Promise<PageResponse<CklstMng>> {
|
||||
const response = await client.get<PageResponse<CklstMng>>(ENDPOINT.AS_CHECKLIST_SERVICE.CKLST, {
|
||||
const response = await client.get<PageResponse<CklstMng>>(BASE, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(cklstSn: number): Promise<CklstMng> {
|
||||
const response = await client.get<CklstMng>(`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST}/${cklstSn}`);
|
||||
const response = await client.get<CklstMng>(`${BASE}/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findTree(params: CklstTreeSearchParams): Promise<CklstTreeNode[]> {
|
||||
const response = await client.get<CklstTreeNode[]>(`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST}/list/tree`, {
|
||||
const response = await client.get<CklstTreeNode[]>(`${BASE}/list/tree`, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function create(body: CklstMngSaveRequest): Promise<number> {
|
||||
const response = await client.post<number>(ENDPOINT.AS_CHECKLIST_SERVICE.CKLST, body);
|
||||
const response = await client.post<number>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(cklstSn: number, body: CklstMngSaveRequest): Promise<number> {
|
||||
const response = await client.put<number>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST}/${cklstSn}`,
|
||||
body,
|
||||
);
|
||||
const response = await client.put<number>(`${BASE}/${cklstSn}`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function remove(cklstSn: number): Promise<number> {
|
||||
const response = await client.delete<number>(`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST}/${cklstSn}`);
|
||||
const response = await client.delete<number>(`${BASE}/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,18 +11,15 @@ import type {
|
||||
CklstRvwUpdateRequest,
|
||||
} from "../model/cklst-rvw-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.CKLST_RVW;
|
||||
|
||||
async function findPage(params: CklstRvwSearchParams): Promise<PageResponse<CklstRvw>> {
|
||||
const response = await client.get<PageResponse<CklstRvw>>(
|
||||
ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW,
|
||||
{ params },
|
||||
);
|
||||
const response = await client.get<PageResponse<CklstRvw>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findByArtcl(cklstSn: number, cklstArtclSn: number): Promise<CklstRvw[]> {
|
||||
const response = await client.get<CklstRvw[]>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW}/by-artcl/${cklstSn}/${cklstArtclSn}`,
|
||||
);
|
||||
const response = await client.get<CklstRvw[]>(`${BASE}/by-artcl/${cklstSn}/${cklstArtclSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -31,17 +28,12 @@ async function findById(
|
||||
cklstArtclSn: number,
|
||||
cklstSn: number,
|
||||
): Promise<CklstRvw> {
|
||||
const response = await client.get<CklstRvw>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`,
|
||||
);
|
||||
const response = await client.get<CklstRvw>(`${BASE}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function create(body: CklstRvwSaveRequest): Promise<CklstRvwId> {
|
||||
const response = await client.post<CklstRvwId>(
|
||||
ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW,
|
||||
body,
|
||||
);
|
||||
const response = await client.post<CklstRvwId>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -52,7 +44,7 @@ async function update(
|
||||
body: CklstRvwUpdateRequest,
|
||||
): Promise<CklstRvwId> {
|
||||
const response = await client.put<CklstRvwId>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`,
|
||||
`${BASE}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
@@ -64,7 +56,7 @@ async function remove(
|
||||
cklstSn: number,
|
||||
): Promise<CklstRvwId> {
|
||||
const response = await client.delete<CklstRvwId>(
|
||||
`${ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`,
|
||||
`${BASE}/${cklstRvwSn}/${cklstArtclSn}/${cklstSn}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ import { cklstArtclService } from "./cklst-artcl-service";
|
||||
import { cklstMngService } from "./cklst-mng-service";
|
||||
import { cklstRvwService } from "./cklst-rvw-service";
|
||||
|
||||
const cklstMngBaseKey = [ENDPOINT.AS_CHECKLIST_SERVICE.CKLST] as const;
|
||||
const cklstArtclBaseKey = [ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_ARTCL] as const;
|
||||
const cklstRvwBaseKey = [ENDPOINT.AS_CHECKLIST_SERVICE.CKLST_RVW] as const;
|
||||
const cklstMngBaseKey = [ENDPOINT.AS_SERVICE.CKLST] as const;
|
||||
const cklstArtclBaseKey = [ENDPOINT.AS_SERVICE.CKLST_ARTCL] as const;
|
||||
const cklstRvwBaseKey = [ENDPOINT.AS_SERVICE.CKLST_RVW] as const;
|
||||
|
||||
export function useCklstMngPage(params: CklstMngSearchParams) {
|
||||
return useQuery({
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
type CnstpSeMngTreeDto,
|
||||
} from "../model/cnstp-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_COMMON_SERVICE.CNSTP_SE;
|
||||
const BASE = ENDPOINT.AS_SERVICE.CNSTP_SE;
|
||||
|
||||
async function findAll(params: CnstpSeMngSearchDto): Promise<PageResponse<CnstpSeMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<CnstpSeMngResponseDto>>(BASE, { params });
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
|
||||
import { cnstpSeMngService } from "./cnstp-service";
|
||||
|
||||
const cnstpSeMngBaseKey = [ENDPOINT.AS_COMMON_SERVICE.CNSTP_SE] as const;
|
||||
const cnstpSeMngBaseKey = [ENDPOINT.AS_SERVICE.CNSTP_SE] as const;
|
||||
|
||||
export function useCnstpSeMngPage(params: CnstpSeMngSearchDto) {
|
||||
return useQuery({
|
||||
|
||||
@@ -16,6 +16,7 @@ export interface CnstpSeMngResponseDto {
|
||||
cdLvNo: number;
|
||||
useYn: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
mdfr?: string;
|
||||
@@ -39,6 +40,7 @@ export interface CnstpSeMngSaveRequestDto {
|
||||
cdLvNo?: number;
|
||||
useYn?: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
}
|
||||
|
||||
/** PUT /{cnstpSeCd} 요청 (mode는 서비스에서 U로 설정) */
|
||||
|
||||
@@ -1,17 +1,67 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
ElaprDmndResponse,
|
||||
ElaprDmndSaveRequest,
|
||||
ElaprDmndResponseDto,
|
||||
ElaprDmndSaveRequestDto,
|
||||
ElaprDmndSearchDto,
|
||||
ElaprDmndService,
|
||||
ElaprDocPrgrsResponseDto,
|
||||
} from "../model/elapr-dmnd-interface";
|
||||
|
||||
async function create(body: ElaprDmndSaveRequest): Promise<ElaprDmndResponse> {
|
||||
const response = await client.post<ElaprDmndResponse>(ENDPOINT.AS_ELAPR_SERVICE.ELAPR_DMND, body);
|
||||
const BASE = ENDPOINT.AS_ELAPR_SERVICE.ELAPR_DMND;
|
||||
|
||||
async function findPage(params: ElaprDmndSearchDto): Promise<PageResponse<ElaprDmndResponseDto>> {
|
||||
const response = await client.get<PageResponse<ElaprDmndResponseDto>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const ElaprDmndApi: ElaprDmndService = {
|
||||
create,
|
||||
};
|
||||
async function findById(elaprDmndId: string): Promise<ElaprDmndResponseDto> {
|
||||
const response = await client.get<ElaprDmndResponseDto>(`${BASE}/${elaprDmndId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function save(body: ElaprDmndSaveRequestDto): Promise<ElaprDmndResponseDto> {
|
||||
const response = await client.post<ElaprDmndResponseDto>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(
|
||||
elaprDmndId: string,
|
||||
body: ElaprDmndSaveRequestDto,
|
||||
): Promise<ElaprDmndResponseDto> {
|
||||
const response = await client.put<ElaprDmndResponseDto>(`${BASE}/${elaprDmndId}`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function updateStatus(
|
||||
elaprDmndId: string,
|
||||
atrzSttsComCd: string,
|
||||
): Promise<ElaprDmndResponseDto> {
|
||||
const response = await client.put<ElaprDmndResponseDto>(`${BASE}/${elaprDmndId}/status`, null, {
|
||||
params: { atrzSttsComCd },
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function remove(elaprDmndId: string): Promise<void> {
|
||||
await client.delete(`${BASE}/${elaprDmndId}`);
|
||||
}
|
||||
|
||||
async function findDocPrgrsByDocNo(elaprDocNo: string): Promise<ElaprDocPrgrsResponseDto[]> {
|
||||
const response = await client.get<ElaprDocPrgrsResponseDto[]>(
|
||||
`${BASE}/sanc/docprgrs/${elaprDocNo}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const elaprDmndService = {
|
||||
findPage,
|
||||
findById,
|
||||
save,
|
||||
update,
|
||||
updateStatus,
|
||||
remove,
|
||||
findDocPrgrsByDocNo,
|
||||
} satisfies ElaprDmndService;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { client } from "@/shared/api";
|
||||
|
||||
export async function processElaprPrgrs(elaprPrgrsId: string, atrzPrgrsSttsComCd = "ATRZ_STTS_003"): Promise<void> {
|
||||
await client.put(`/as-elapr-service/api/v1/elapr-prgrs-stts/${elaprPrgrsId}/process`, {
|
||||
atrzPrgrsSttsComCd,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
ElaprApproverChangeRequestDto,
|
||||
ElaprApproverInsertRequestDto,
|
||||
ElaprPrgrsSttsMyDocSearchParams,
|
||||
ElaprPrgrsSttsResponseDto,
|
||||
ElaprPrgrsSttsSaveRequestDto,
|
||||
ElaprPrgrsSttsService,
|
||||
ElaprProcessRequestDto,
|
||||
} from "../model/elapr-prgrs-stts-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_ELAPR_SERVICE.ELAPR_PRGRS_STTS;
|
||||
|
||||
async function findAllByDmndId(elaprDmndId?: string): Promise<ElaprPrgrsSttsResponseDto[]> {
|
||||
const response = await client.get<ElaprPrgrsSttsResponseDto[]>(BASE, {
|
||||
params: elaprDmndId ? { elaprDmndId } : undefined,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(elaprPrgrsId: string): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.get<ElaprPrgrsSttsResponseDto>(`${BASE}/${elaprPrgrsId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function save(body: ElaprPrgrsSttsSaveRequestDto): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.post<ElaprPrgrsSttsResponseDto>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function process(
|
||||
elaprPrgrsId: string,
|
||||
body: ElaprProcessRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.put<ElaprPrgrsSttsResponseDto>(
|
||||
`${BASE}/${elaprPrgrsId}/process`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function processByDocNoAndAprvrUuid(
|
||||
elaprDocNo: string,
|
||||
aprvrUuid: string,
|
||||
body: ElaprProcessRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.put<ElaprPrgrsSttsResponseDto>(
|
||||
`${BASE}/doc/${elaprDocNo}/aprvr/${aprvrUuid}/process`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function deletePrgrsStts(elaprPrgrsId: string): Promise<void> {
|
||||
await client.delete(`${BASE}/${elaprPrgrsId}`);
|
||||
}
|
||||
|
||||
async function updateApprover(
|
||||
elaprPrgrsId: string,
|
||||
body: ElaprApproverChangeRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.put<ElaprPrgrsSttsResponseDto>(
|
||||
`${BASE}/${elaprPrgrsId}/approver`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function insertApprover(
|
||||
elaprDmndId: string,
|
||||
body: ElaprApproverInsertRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto> {
|
||||
const response = await client.post<ElaprPrgrsSttsResponseDto>(
|
||||
`${BASE}/${elaprDmndId}/approver/insert`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findMyElaperDocList(
|
||||
params: ElaprPrgrsSttsMyDocSearchParams,
|
||||
): Promise<PageResponse<ElaprPrgrsSttsResponseDto>> {
|
||||
const response = await client.get<PageResponse<ElaprPrgrsSttsResponseDto>>(`${BASE}/my/doc`, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const elaprPrgrsSttsService = {
|
||||
findAllByDmndId,
|
||||
findById,
|
||||
save,
|
||||
process,
|
||||
processByDocNoAndAprvrUuid,
|
||||
delete: deletePrgrsStts,
|
||||
updateApprover,
|
||||
insertApprover,
|
||||
findMyElaperDocList,
|
||||
} satisfies ElaprPrgrsSttsService;
|
||||
@@ -8,6 +8,14 @@ import type {
|
||||
ElaprAplnSaveRequest,
|
||||
ElaprAplnUpdateRequest,
|
||||
} from "../model/elapr-apln-interface";
|
||||
import type { ElaprDmndSaveRequestDto, ElaprDmndSearchDto } from "../model/elapr-dmnd-interface";
|
||||
import type {
|
||||
ElaprApproverChangeRequestDto,
|
||||
ElaprApproverInsertRequestDto,
|
||||
ElaprPrgrsSttsMyDocSearchParams,
|
||||
ElaprPrgrsSttsSaveRequestDto,
|
||||
ElaprProcessRequestDto,
|
||||
} from "../model/elapr-prgrs-stts-interface";
|
||||
import type {
|
||||
ElaprTmpltSaveRequest,
|
||||
ElaprTmpltSearchParams,
|
||||
@@ -15,10 +23,19 @@ import type {
|
||||
} from "../model/elapr-tmplt-interface";
|
||||
|
||||
import { elaprAplnService } from "./elapr-apln-service";
|
||||
import { elaprDmndService } from "./elapr-dmnd-service";
|
||||
import { elaprPrgrsSttsService } from "./elapr-prgrs-stts-service";
|
||||
import { elaprTmpltService } from "./elapr-tmplt-service";
|
||||
|
||||
const elaprTmpltBaseKey = [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_TMPLT] as const;
|
||||
const elaprAplnBaseKey = [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_APLN] as const;
|
||||
const elaprDmndBaseKey = [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_DMND] as const;
|
||||
const elaprPrgrsSttsBaseKey = [ENDPOINT.AS_ELAPR_SERVICE.ELAPR_PRGRS_STTS] as const;
|
||||
|
||||
function invalidateElaprPrgrsSttsQueries(queryClient: ReturnType<typeof useQueryClient>) {
|
||||
queryClient.invalidateQueries({ queryKey: elaprPrgrsSttsBaseKey });
|
||||
queryClient.invalidateQueries({ queryKey: elaprDmndBaseKey });
|
||||
}
|
||||
|
||||
export function useElaprTmpltPage(params: ElaprTmpltSearchParams) {
|
||||
return useQuery({
|
||||
@@ -50,13 +67,8 @@ export function useUpdateElaprTmplt() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
elaprTmpltId,
|
||||
body,
|
||||
}: {
|
||||
elaprTmpltId: string;
|
||||
body: ElaprTmpltUpdateRequest;
|
||||
}) => elaprTmpltService.update(elaprTmpltId, body),
|
||||
mutationFn: ({ elaprTmpltId, body }: { elaprTmpltId: string; body: ElaprTmpltUpdateRequest }) =>
|
||||
elaprTmpltService.update(elaprTmpltId, body),
|
||||
onSuccess: (_data, { elaprTmpltId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprTmpltBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -99,12 +111,8 @@ export function useSaveMultiElaprApln() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
body,
|
||||
}: {
|
||||
body: ElaprAplnSaveRequest[];
|
||||
elaprTmpltId: string;
|
||||
}) => elaprAplnService.saveMulti(body),
|
||||
mutationFn: ({ body }: { body: ElaprAplnSaveRequest[]; elaprTmpltId: string }) =>
|
||||
elaprAplnService.saveMulti(body),
|
||||
onSuccess: (_data, { elaprTmpltId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprAplnBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -118,13 +126,8 @@ export function useUpdateElaprApln() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
aplnId,
|
||||
body,
|
||||
}: {
|
||||
aplnId: string;
|
||||
body: ElaprAplnUpdateRequest;
|
||||
}) => elaprAplnService.update(aplnId, body),
|
||||
mutationFn: ({ aplnId, body }: { aplnId: string; body: ElaprAplnUpdateRequest }) =>
|
||||
elaprAplnService.update(aplnId, body),
|
||||
onSuccess: (_data, { body, aplnId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprAplnBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -141,12 +144,8 @@ export function useDeleteElaprApln() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
aplnId,
|
||||
}: {
|
||||
aplnId: string;
|
||||
elaprTmpltId: string;
|
||||
}) => elaprAplnService.remove(aplnId),
|
||||
mutationFn: ({ aplnId }: { aplnId: string; elaprTmpltId: string }) =>
|
||||
elaprAplnService.remove(aplnId),
|
||||
onSuccess: (_data, { elaprTmpltId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprAplnBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -160,12 +159,8 @@ export function useReorderElaprApln() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
body,
|
||||
}: {
|
||||
body: ElaprAplnReorderRequest;
|
||||
elaprTmpltId: string;
|
||||
}) => elaprAplnService.reorder(body),
|
||||
mutationFn: ({ body }: { body: ElaprAplnReorderRequest; elaprTmpltId: string }) =>
|
||||
elaprAplnService.reorder(body),
|
||||
onSuccess: (_data, { elaprTmpltId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprAplnBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
@@ -174,3 +169,193 @@ export function useReorderElaprApln() {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprDmndPage(params: ElaprDmndSearchDto) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprDmndBaseKey, "findPage", params],
|
||||
queryFn: () => elaprDmndService.findPage(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprDmnd(elaprDmndId: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprDmndBaseKey, "findById", elaprDmndId],
|
||||
queryFn: () => elaprDmndService.findById(elaprDmndId!),
|
||||
enabled: Boolean(elaprDmndId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprDocPrgrsByDocNo(elaprDocNo: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprDmndBaseKey, "findDocPrgrsByDocNo", elaprDocNo],
|
||||
queryFn: () => elaprDmndService.findDocPrgrsByDocNo(elaprDocNo!),
|
||||
enabled: Boolean(elaprDocNo),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveElaprDmnd() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (body: ElaprDmndSaveRequestDto) => elaprDmndService.save(body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprDmndBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateElaprDmnd() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ elaprDmndId, body }: { elaprDmndId: string; body: ElaprDmndSaveRequestDto }) =>
|
||||
elaprDmndService.update(elaprDmndId, body),
|
||||
onSuccess: (_data, { elaprDmndId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprDmndBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...elaprDmndBaseKey, "findById", elaprDmndId],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateElaprDmndStatus() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ elaprDmndId, atrzSttsComCd }: { elaprDmndId: string; atrzSttsComCd: string }) =>
|
||||
elaprDmndService.updateStatus(elaprDmndId, atrzSttsComCd),
|
||||
onSuccess: (_data, { elaprDmndId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprDmndBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...elaprDmndBaseKey, "findById", elaprDmndId],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveElaprDmnd() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (elaprDmndId: string) => elaprDmndService.remove(elaprDmndId),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: elaprDmndBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprPrgrsSttsByDmndId(elaprDmndId: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprPrgrsSttsBaseKey, "findAllByDmndId", elaprDmndId],
|
||||
queryFn: () => elaprPrgrsSttsService.findAllByDmndId(elaprDmndId!),
|
||||
enabled: Boolean(elaprDmndId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprPrgrsStts(elaprPrgrsId: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprPrgrsSttsBaseKey, elaprPrgrsId],
|
||||
queryFn: () => elaprPrgrsSttsService.findById(elaprPrgrsId!),
|
||||
enabled: Boolean(elaprPrgrsId),
|
||||
});
|
||||
}
|
||||
|
||||
export function useElaprPrgrsSttsMyDocPage(params: ElaprPrgrsSttsMyDocSearchParams) {
|
||||
return useQuery({
|
||||
queryKey: [...elaprPrgrsSttsBaseKey, "findMyElaperDocList", params],
|
||||
queryFn: () => elaprPrgrsSttsService.findMyElaperDocList(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveElaprPrgrsStts() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (body: ElaprPrgrsSttsSaveRequestDto) => elaprPrgrsSttsService.save(body),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useProcessElaprPrgrsStts() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
elaprPrgrsId,
|
||||
body,
|
||||
}: {
|
||||
elaprPrgrsId: string;
|
||||
body: ElaprProcessRequestDto;
|
||||
}) => elaprPrgrsSttsService.process(elaprPrgrsId, body),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useProcessElaprPrgrsSttsByDoc() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
elaprDocNo,
|
||||
aprvrUuid,
|
||||
body,
|
||||
}: {
|
||||
elaprDocNo: string;
|
||||
aprvrUuid: string;
|
||||
body: ElaprProcessRequestDto;
|
||||
}) => elaprPrgrsSttsService.processByDocNoAndAprvrUuid(elaprDocNo, aprvrUuid, body),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteElaprPrgrsStts() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (elaprPrgrsId: string) => elaprPrgrsSttsService.delete(elaprPrgrsId),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateElaprPrgrsSttsApprover() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
elaprPrgrsId,
|
||||
body,
|
||||
}: {
|
||||
elaprPrgrsId: string;
|
||||
body: ElaprApproverChangeRequestDto;
|
||||
}) => elaprPrgrsSttsService.updateApprover(elaprPrgrsId, body),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInsertElaprPrgrsSttsApprover() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
elaprDmndId,
|
||||
body,
|
||||
}: {
|
||||
elaprDmndId: string;
|
||||
body: ElaprApproverInsertRequestDto;
|
||||
}) => elaprPrgrsSttsService.insertApprover(elaprDmndId, body),
|
||||
onSuccess: () => {
|
||||
invalidateElaprPrgrsSttsQueries(queryClient);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,33 +1,91 @@
|
||||
export { elaprAplnService } from "./api/elapr-apln-service";
|
||||
export { elaprTmpltService } from "./api/elapr-tmplt-service";
|
||||
export {
|
||||
useCreateElaprTmplt,
|
||||
useDeleteElaprApln,
|
||||
useDeleteElaprTmplt,
|
||||
useElaprApln,
|
||||
useElaprAplnPage,
|
||||
useElaprTmplt,
|
||||
useElaprTmpltPage,
|
||||
useReorderElaprApln,
|
||||
useSaveMultiElaprApln,
|
||||
useUpdateElaprApln,
|
||||
useUpdateElaprTmplt,
|
||||
} from "./api/queries";
|
||||
export {
|
||||
ELAPR_APLN_SAVE_MODE,
|
||||
type ElaprApln,
|
||||
type ElaprAplnListParams,
|
||||
type ElaprAplnReorderItem,
|
||||
type ElaprAplnReorderRequest,
|
||||
type ElaprAplnSaveMode,
|
||||
type ElaprAplnSaveRequest,
|
||||
type ElaprAplnService,
|
||||
type ElaprAplnUpdateRequest,
|
||||
} from "./model/elapr-apln-interface";
|
||||
export type {
|
||||
ElaprTmplt,
|
||||
ElaprTmpltSaveRequest,
|
||||
ElaprTmpltSearchParams,
|
||||
ElaprTmpltService,
|
||||
ElaprTmpltUpdateRequest,
|
||||
} from "./model/elapr-tmplt-interface";
|
||||
export { elaprAplnService } from "./api/elapr-apln-service";
|
||||
export { elaprDmndService } from "./api/elapr-dmnd-service";
|
||||
export { elaprPrgrsSttsService } from "./api/elapr-prgrs-stts-service";
|
||||
export { elaprTmpltService } from "./api/elapr-tmplt-service";
|
||||
export {
|
||||
useCreateElaprTmplt,
|
||||
useDeleteElaprApln,
|
||||
useDeleteElaprPrgrsStts,
|
||||
useDeleteElaprTmplt,
|
||||
useElaprApln,
|
||||
useElaprAplnPage,
|
||||
useElaprDmnd,
|
||||
useElaprDmndPage,
|
||||
useElaprDocPrgrsByDocNo,
|
||||
useElaprPrgrsStts,
|
||||
useElaprPrgrsSttsByDmndId,
|
||||
useElaprPrgrsSttsMyDocPage,
|
||||
useElaprTmplt,
|
||||
useElaprTmpltPage,
|
||||
useInsertElaprPrgrsSttsApprover,
|
||||
useProcessElaprPrgrsStts,
|
||||
useProcessElaprPrgrsSttsByDoc,
|
||||
useRemoveElaprDmnd,
|
||||
useReorderElaprApln,
|
||||
useSaveElaprDmnd,
|
||||
useSaveElaprPrgrsStts,
|
||||
useSaveMultiElaprApln,
|
||||
useUpdateElaprApln,
|
||||
useUpdateElaprDmnd,
|
||||
useUpdateElaprDmndStatus,
|
||||
useUpdateElaprPrgrsSttsApprover,
|
||||
useUpdateElaprTmplt,
|
||||
} from "./api/queries";
|
||||
export {
|
||||
assignAtrzSeqNumbers,
|
||||
buildSaveMultiPayload,
|
||||
hasUnsavedChanges,
|
||||
} from "./lib/apln-save-utils";
|
||||
export {
|
||||
getVisibleRows,
|
||||
validateAllRowsForSave,
|
||||
validateAplnRow,
|
||||
validateAtrzOrder,
|
||||
validateUniqueSeqNo,
|
||||
} from "./lib/apln-validators";
|
||||
export {
|
||||
buildElaprPrgrsSttsListFromApln,
|
||||
buildElaprPrgrsSttsListFromAplnRows,
|
||||
} from "./lib/dmnd-save-utils";
|
||||
export {
|
||||
findCurrentUserPendingApprovalRequest,
|
||||
hasCurrentUserPendingApprovalRequest,
|
||||
} from "./lib/approval-request-utils";
|
||||
export { createDraftRowKey, useAplnRows } from "./lib/use-apln-rows";
|
||||
export { useDndSensors } from "./lib/use-dnd-sensors";
|
||||
export {
|
||||
ELAPR_APLN_SAVE_MODE,
|
||||
type ElaprApln,
|
||||
type ElaprAplnListParams,
|
||||
type ElaprAplnReorderItem,
|
||||
type ElaprAplnReorderRequest,
|
||||
type ElaprAplnSaveMode,
|
||||
type ElaprAplnSaveRequest,
|
||||
type ElaprAplnService,
|
||||
type ElaprAplnUpdateRequest,
|
||||
} from "./model/elapr-apln-interface";
|
||||
export type { ElaprAplnRow } from "./model/elapr-apln-row-types";
|
||||
export type {
|
||||
ElaprDmndResponseDto,
|
||||
ElaprDmndSaveRequestDto,
|
||||
ElaprDmndSearchDto,
|
||||
ElaprDmndService,
|
||||
ElaprDocPrgrsResponseDto,
|
||||
} from "./model/elapr-dmnd-interface";
|
||||
export type {
|
||||
ElaprApproverChangeRequestDto,
|
||||
ElaprApproverInsertRequestDto,
|
||||
ElaprPrgrsSttsMyDocSearchParams,
|
||||
ElaprPrgrsSttsResponseDto,
|
||||
ElaprPrgrsSttsSaveRequestDto,
|
||||
ElaprPrgrsSttsSearchDto,
|
||||
ElaprPrgrsSttsService,
|
||||
ElaprProcessRequestDto,
|
||||
} from "./model/elapr-prgrs-stts-interface";
|
||||
export type {
|
||||
ElaprTmplt,
|
||||
ElaprTmpltSaveRequest,
|
||||
ElaprTmpltSearchParams,
|
||||
ElaprTmpltService,
|
||||
ElaprTmpltUpdateRequest,
|
||||
} from "./model/elapr-tmplt-interface";
|
||||
|
||||
+31
-4
@@ -1,8 +1,16 @@
|
||||
import { ELAPR_APLN_SAVE_MODE, type ElaprApln, type ElaprAplnSaveRequest } from "@/entities/elapr";
|
||||
import {
|
||||
ELAPR_APLN_SAVE_MODE,
|
||||
type ElaprApln,
|
||||
type ElaprAplnSaveRequest,
|
||||
} from "../model/elapr-apln-interface";
|
||||
import type { ElaprAplnRow } from "../model/elapr-apln-row-types";
|
||||
|
||||
import { assignAtrzSeqNumbers } from "./assign-atrz-seq";
|
||||
import { getVisibleRows } from "./validators";
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
import { getVisibleRows } from "./apln-validators";
|
||||
|
||||
/** 그리드 행 순서(1-based)로 결재순서번호 일괄 채번 */
|
||||
export function assignAtrzSeqNumbers(rows: ElaprAplnRow[]): ElaprAplnRow[] {
|
||||
return rows.map((row, index) => ({ ...row, atrzSeqNo: index + 1 }));
|
||||
}
|
||||
|
||||
function rowDiffersFromServer(row: ElaprAplnRow, serverRow: ElaprApln): boolean {
|
||||
return (
|
||||
@@ -22,6 +30,25 @@ function shouldIncludeUpdate(row: ElaprAplnRow, serverRows: ElaprApln[]): boolea
|
||||
return rowDiffersFromServer(row, serverRow);
|
||||
}
|
||||
|
||||
export function hasUnsavedChanges(rows: ElaprAplnRow[], serverRows: ElaprApln[]): boolean {
|
||||
if (rows.some((row) => row.isDeleted && row.aplnId)) return true;
|
||||
if (rows.some((row) => row.isNew && !row.isDeleted)) return true;
|
||||
|
||||
const visibleWithSeq = assignAtrzSeqNumbers(getVisibleRows(rows));
|
||||
|
||||
for (const row of visibleWithSeq) {
|
||||
if (row.isNew) continue;
|
||||
if (!row.aplnId) continue;
|
||||
if (row.isDirty) return true;
|
||||
|
||||
const serverRow = serverRows.find((s) => s.aplnId === row.aplnId);
|
||||
if (!serverRow) continue;
|
||||
if (rowDiffersFromServer(row, serverRow)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function buildSaveMultiPayload(
|
||||
rows: ElaprAplnRow[],
|
||||
elaprTmpltId: string,
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
import type { ElaprAplnRow } from "../model/elapr-apln-row-types";
|
||||
|
||||
const ATRZ_RECEIVER_CD = "ATRZ01";
|
||||
const ATRZ_FINAL_CD = "ATRZ99";
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { ElaprDocPrgrsResponseDto } from "../model/elapr-dmnd-interface";
|
||||
|
||||
export function hasCurrentUserPendingApprovalRequest(
|
||||
list: ElaprDocPrgrsResponseDto[],
|
||||
currentUserUuid: string | undefined,
|
||||
): boolean {
|
||||
if (!currentUserUuid) return false;
|
||||
|
||||
return list.some(
|
||||
(row) =>
|
||||
row.aprvrUuid === currentUserUuid && row.atrzPrgrsSttsComCd === "ATRZ_PRGRS_002",
|
||||
);
|
||||
}
|
||||
|
||||
export function findCurrentUserPendingApprovalRequest(
|
||||
list: ElaprDocPrgrsResponseDto[],
|
||||
currentUserUuid: string | undefined,
|
||||
): ElaprDocPrgrsResponseDto | undefined {
|
||||
if (!currentUserUuid) return undefined;
|
||||
|
||||
return list.find(
|
||||
(row) =>
|
||||
row.aprvrUuid === currentUserUuid && row.atrzPrgrsSttsComCd === "ATRZ_PRGRS_002",
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import type { ElaprApln } from "../model/elapr-apln-interface";
|
||||
import type { ElaprAplnRow } from "../model/elapr-apln-row-types";
|
||||
import type { ElaprPrgrsSttsSaveRequestDto } from "../model/elapr-prgrs-stts-interface";
|
||||
|
||||
import { getVisibleRows } from "./apln-validators";
|
||||
|
||||
function toPrgrsSttsItem(
|
||||
aprvrUuid: string | undefined,
|
||||
atrzSeComCd: string | undefined,
|
||||
atrzSeqNo: number | undefined,
|
||||
): ElaprPrgrsSttsSaveRequestDto {
|
||||
if (!aprvrUuid?.trim()) {
|
||||
throw new Error("결재자가 지정되지 않은 행이 있습니다.");
|
||||
}
|
||||
if (!atrzSeComCd?.trim()) {
|
||||
throw new Error("결재구분이 지정되지 않은 행이 있습니다.");
|
||||
}
|
||||
if (atrzSeqNo == null) {
|
||||
throw new Error("결재순서번호가 지정되지 않은 행이 있습니다.");
|
||||
}
|
||||
|
||||
return {
|
||||
aprvrUuid,
|
||||
atrzSeComCd,
|
||||
atrzSeqNo,
|
||||
};
|
||||
}
|
||||
|
||||
/** 서식 결재선 API 응답 → 기안 요청용 진행상태 목록 */
|
||||
export function buildElaprPrgrsSttsListFromApln(
|
||||
items: ElaprApln[],
|
||||
): ElaprPrgrsSttsSaveRequestDto[] {
|
||||
return items.map((item) => toPrgrsSttsItem(item.aprvrUuid, item.atrzSeComCd, item.atrzSeqNo));
|
||||
}
|
||||
|
||||
/** 결재선 그리드 행 → 기안 요청용 진행상태 목록 (서버가 seq 기준 상태 자동 결정) */
|
||||
export function buildElaprPrgrsSttsListFromAplnRows(
|
||||
rows: ElaprAplnRow[],
|
||||
): ElaprPrgrsSttsSaveRequestDto[] {
|
||||
return getVisibleRows(rows).map((row) =>
|
||||
toPrgrsSttsItem(row.aprvrUuid, row.atrzSeComCd, row.atrzSeqNo),
|
||||
);
|
||||
}
|
||||
+25
-20
@@ -4,12 +4,11 @@ import { useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { arrayMove } from "@dnd-kit/sortable";
|
||||
|
||||
import type { ElaprApln } from "@/entities/elapr";
|
||||
import type { ElaprApln } from "../model/elapr-apln-interface";
|
||||
import type { ElaprAplnRow } from "../model/elapr-apln-row-types";
|
||||
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
|
||||
import { assignAtrzSeqNumbers } from "./assign-atrz-seq";
|
||||
import { getVisibleRows } from "./validators";
|
||||
import { assignAtrzSeqNumbers } from "./apln-save-utils";
|
||||
import { getVisibleRows } from "./apln-validators";
|
||||
|
||||
function toRow(item: ElaprApln): ElaprAplnRow {
|
||||
return {
|
||||
@@ -101,7 +100,13 @@ export function useAplnRows({ elaprTmpltId, serverRows }: UseAplnRowsOptions) {
|
||||
return prev.map((row) => {
|
||||
if (row.rowKey !== editingRowKey) return row;
|
||||
if (row.snapshot) {
|
||||
const { snapshot: _s, isNew: _n, isDirty: _d, isDeleted: _del, ...restored } = row.snapshot;
|
||||
const {
|
||||
snapshot: _s,
|
||||
isNew: _n,
|
||||
isDirty: _d,
|
||||
isDeleted: _del,
|
||||
...restored
|
||||
} = row.snapshot;
|
||||
return { ...restored, isNew: false, isDirty: false, isDeleted: false };
|
||||
}
|
||||
return { ...row, isDirty: false };
|
||||
@@ -110,19 +115,16 @@ export function useAplnRows({ elaprTmpltId, serverRows }: UseAplnRowsOptions) {
|
||||
setEditingRowKey(null);
|
||||
}, [editingRowKey]);
|
||||
|
||||
const commitEdit = useCallback(
|
||||
(rowKey: string) => {
|
||||
setRows((prev) =>
|
||||
prev.map((row) => {
|
||||
if (row.rowKey !== rowKey) return row;
|
||||
const { snapshot: _s, ...committed } = row;
|
||||
return { ...committed, isDirty: true };
|
||||
}),
|
||||
);
|
||||
setEditingRowKey(null);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const commitEdit = useCallback((rowKey: string) => {
|
||||
setRows((prev) =>
|
||||
prev.map((row) => {
|
||||
if (row.rowKey !== rowKey) return row;
|
||||
const { snapshot: _s, ...committed } = row;
|
||||
return { ...committed, isDirty: true };
|
||||
}),
|
||||
);
|
||||
setEditingRowKey(null);
|
||||
}, []);
|
||||
|
||||
const markDeleted = useCallback((rowKey: string) => {
|
||||
setRows((prev) => {
|
||||
@@ -158,7 +160,10 @@ export function useAplnRows({ elaprTmpltId, serverRows }: UseAplnRowsOptions) {
|
||||
|
||||
const visibleKeys = new Set(reorderedVisible.map((r) => r.rowKey));
|
||||
const deletedRows = prev.filter((r) => r.isDeleted);
|
||||
const merged = [...reorderedVisible, ...deletedRows.filter((r) => !visibleKeys.has(r.rowKey))];
|
||||
const merged = [
|
||||
...reorderedVisible,
|
||||
...deletedRows.filter((r) => !visibleKeys.has(r.rowKey)),
|
||||
];
|
||||
|
||||
payloadRows = reorderedVisible;
|
||||
return merged;
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
|
||||
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import type { ElaprApln } from "@/entities/elapr";
|
||||
import type { ElaprApln } from "./elapr-apln-interface";
|
||||
|
||||
/** 그리드 로컬 행 — API 응답 + UI 보조 필드 */
|
||||
export interface ElaprAplnRow extends Omit<ElaprApln, "aplnId"> {
|
||||
@@ -1,22 +1,63 @@
|
||||
export interface ElaprDmndSaveRequest {
|
||||
import type { PageableParams, PageResponse } from "@/shared/model";
|
||||
|
||||
import type { ElaprPrgrsSttsSaveRequestDto } from "./elapr-prgrs-stts-interface";
|
||||
|
||||
/** 백엔드 ElaprDmndSaveRequestDto */
|
||||
export interface ElaprDmndSaveRequestDto {
|
||||
elaprDocNo: string;
|
||||
elaprTmpltId: string;
|
||||
dmndTtl: string;
|
||||
dmndCn: string;
|
||||
elaprPrgrsSttsList?: ElaprPrgrsSttsSaveRequestDto[];
|
||||
}
|
||||
|
||||
export interface ElaprDmndResponse {
|
||||
/** 백엔드 ElaprDmndResponseDto */
|
||||
export interface ElaprDmndResponseDto {
|
||||
elaprDmndId: string;
|
||||
rqstrUuid?: string;
|
||||
elaprDocNo: string;
|
||||
elaprTmpltId: string;
|
||||
dmndTtl: string;
|
||||
dmndCn: string;
|
||||
atrzSttsComCd: string;
|
||||
rqstrUuid: string;
|
||||
regDt: string;
|
||||
updtDt: string;
|
||||
atrzSttsComNm?: string;
|
||||
dmndDt?: string;
|
||||
updtDt?: string;
|
||||
}
|
||||
|
||||
export interface ElaprDmndService {
|
||||
create(body: ElaprDmndSaveRequest): Promise<ElaprDmndResponse>;
|
||||
/** 백엔드 ElaprDmndSearchDto + Pageable */
|
||||
export interface ElaprDmndSearchDto extends PageableParams {
|
||||
rqstrUuid?: string;
|
||||
atrzSttsComCd?: string;
|
||||
dmndTtl?: string;
|
||||
dmndDtStart?: string;
|
||||
dmndDtEnd?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprDocPrgrsResponseDto */
|
||||
export interface ElaprDocPrgrsResponseDto {
|
||||
elaprDocNo: string;
|
||||
elaprDmndId: string;
|
||||
elaprPrgrsId: string;
|
||||
atrzSeqNo?: number;
|
||||
atrzSeComCd: string;
|
||||
atrzSeComNm?: string;
|
||||
aprvrUuid?: string;
|
||||
aprvrUuidNm?: string;
|
||||
atrzPrgrsSttsComCd: string;
|
||||
atrzPrgrsSttsComNm?: string;
|
||||
rjctRsn?: string;
|
||||
atrzDt?: string;
|
||||
jbpsSeNm?: string;
|
||||
jbgdSeNm?: string;
|
||||
ognzNm?: string;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface ElaprDmndService {
|
||||
findPage(params: ElaprDmndSearchDto): Promise<PageResponse<ElaprDmndResponseDto>>;
|
||||
findById(elaprDmndId: string): Promise<ElaprDmndResponseDto>;
|
||||
save(body: ElaprDmndSaveRequestDto): Promise<ElaprDmndResponseDto>;
|
||||
update(elaprDmndId: string, body: ElaprDmndSaveRequestDto): Promise<ElaprDmndResponseDto>;
|
||||
updateStatus(elaprDmndId: string, atrzSttsComCd: string): Promise<ElaprDmndResponseDto>;
|
||||
remove(elaprDmndId: string): Promise<void>;
|
||||
findDocPrgrsByDocNo(elaprDocNo: string): Promise<ElaprDocPrgrsResponseDto[]>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import type { PageableParams, PageResponse } from "@/shared/model";
|
||||
|
||||
/** 백엔드 ElaprPrgrsSttsResponseDto */
|
||||
export interface ElaprPrgrsSttsResponseDto {
|
||||
elaprPrgrsId: string;
|
||||
elaprDmndId?: string;
|
||||
aprvrUuid?: string;
|
||||
atrzSeComCd?: string;
|
||||
atrzSeComNm?: string;
|
||||
atrzSeqNo?: number;
|
||||
atrzPrgrsSttsComCd?: string;
|
||||
atrzPrgrsSttsComNm?: string;
|
||||
rjctRsn?: string;
|
||||
atrzDt?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprPrgrsSttsSaveRequestDto */
|
||||
export interface ElaprPrgrsSttsSaveRequestDto {
|
||||
elaprDmndId?: string;
|
||||
aprvrUuid: string;
|
||||
atrzSeComCd: string;
|
||||
atrzSeqNo: number;
|
||||
atrzPrgrsSttsComCd?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprPrgrsSttsSearchDto */
|
||||
export interface ElaprPrgrsSttsSearchDto {
|
||||
atrzSeComCd: string;
|
||||
atrzSeqNo: number;
|
||||
atrzPrgrsSttsComCd?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprPrgrsSttsSearchDto + Pageable */
|
||||
export interface ElaprPrgrsSttsMyDocSearchParams extends ElaprPrgrsSttsSearchDto, PageableParams {}
|
||||
|
||||
/** 백엔드 ElaprProcessRequestDto */
|
||||
export interface ElaprProcessRequestDto {
|
||||
atrzPrgrsSttsComCd: string;
|
||||
rjctRsn?: string;
|
||||
finalApprovalYn?: boolean;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprApproverChangeRequestDto */
|
||||
export interface ElaprApproverChangeRequestDto {
|
||||
aprvrUuid: string;
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprApproverInsertRequestDto */
|
||||
export interface ElaprApproverInsertRequestDto {
|
||||
aprvrUuid: string;
|
||||
atrzSeComCd: string;
|
||||
atrzSeqNo: number;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface ElaprPrgrsSttsService {
|
||||
findAllByDmndId(elaprDmndId?: string): Promise<ElaprPrgrsSttsResponseDto[]>;
|
||||
findById(elaprPrgrsId: string): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
save(body: ElaprPrgrsSttsSaveRequestDto): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
process(elaprPrgrsId: string, body: ElaprProcessRequestDto): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
processByDocNoAndAprvrUuid(
|
||||
elaprDocNo: string,
|
||||
aprvrUuid: string,
|
||||
body: ElaprProcessRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
delete(elaprPrgrsId: string): Promise<void>;
|
||||
updateApprover(
|
||||
elaprPrgrsId: string,
|
||||
body: ElaprApproverChangeRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
insertApprover(
|
||||
elaprDmndId: string,
|
||||
body: ElaprApproverInsertRequestDto,
|
||||
): Promise<ElaprPrgrsSttsResponseDto>;
|
||||
findMyElaperDocList(
|
||||
params: ElaprPrgrsSttsMyDocSearchParams,
|
||||
): Promise<PageResponse<ElaprPrgrsSttsResponseDto>>;
|
||||
}
|
||||
@@ -11,50 +11,41 @@ import type {
|
||||
EsntlAtchFileMngTreeSearchParams,
|
||||
} from "../model/esntl-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.ESNTL_ATCH_FILE;
|
||||
|
||||
async function findTree(
|
||||
params: EsntlAtchFileMngTreeSearchParams,
|
||||
): Promise<EsntlAtchFileMngTreeResponseDto[]> {
|
||||
const response = await client.get<EsntlAtchFileMngTreeResponseDto[]>(
|
||||
`${ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE}/list/tree`,
|
||||
{ params },
|
||||
);
|
||||
const response = await client.get<EsntlAtchFileMngTreeResponseDto[]>(`${BASE}/list/tree`, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findPage(
|
||||
params: EsntlAtchFileMngSearchParams,
|
||||
): Promise<PageResponse<EsntlAtchFileMng>> {
|
||||
const response = await client.get<PageResponse<EsntlAtchFileMng>>(
|
||||
ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE,
|
||||
{ params },
|
||||
);
|
||||
const response = await client.get<PageResponse<EsntlAtchFileMng>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(esntlAtchFileId: string): Promise<EsntlAtchFileMng> {
|
||||
const response = await client.get<EsntlAtchFileMng>(
|
||||
`${ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE}/${esntlAtchFileId}`,
|
||||
);
|
||||
const response = await client.get<EsntlAtchFileMng>(`${BASE}/${esntlAtchFileId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function create(body: EsntlAtchFileMngSaveRequest): Promise<string> {
|
||||
const response = await client.post<string>(ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE, body);
|
||||
const response = await client.post<string>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(esntlAtchFileId: string, body: EsntlAtchFileMngSaveRequest): Promise<string> {
|
||||
const response = await client.put<string>(
|
||||
`${ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE}/${esntlAtchFileId}`,
|
||||
body,
|
||||
);
|
||||
const response = await client.put<string>(`${BASE}/${esntlAtchFileId}`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function remove(esntlAtchFileId: string): Promise<string> {
|
||||
const response = await client.delete<string>(
|
||||
`${ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE}/${esntlAtchFileId}`,
|
||||
);
|
||||
const response = await client.delete<string>(`${BASE}/${esntlAtchFileId}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
|
||||
import { esntlAtchFileMngService } from "./esntl-service";
|
||||
|
||||
const esntlAtchFileMngBaseKey = [ENDPOINT.AS_COMMON_SERVICE.ESNTL_ATCH_FILE] as const;
|
||||
const esntlAtchFileMngBaseKey = [ENDPOINT.AS_SERVICE.ESNTL_ATCH_FILE] as const;
|
||||
|
||||
export function useEsntlAtchFileMngTree(params: EsntlAtchFileMngTreeSearchParams) {
|
||||
return useQuery({
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
DocPrgrsUpdateRequestDto,
|
||||
FnrsCnsltnApplySaveRequestDto,
|
||||
FnrsCnsltnMngDetResponseDto,
|
||||
FnrsCnsltnMngResponseDto,
|
||||
FnrsCnsltnMngSaveRequestDto,
|
||||
FnrsCnsltnMngSearchDto,
|
||||
FnrsCnsltnMngService,
|
||||
FnrsCnsltnMngUpdateRequestDto,
|
||||
} from "../model/fnrs-cnsltn-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.FNRS_CNSLTN_MNG;
|
||||
|
||||
async function findPage(
|
||||
searchDto: FnrsCnsltnMngSearchDto,
|
||||
): Promise<PageResponse<FnrsCnsltnMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<FnrsCnsltnMngResponseDto>>(BASE, {
|
||||
params: searchDto,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(fnrsCnsltnDocNo: string): Promise<FnrsCnsltnMngDetResponseDto> {
|
||||
const response = await client.get<FnrsCnsltnMngDetResponseDto>(`${BASE}/${fnrsCnsltnDocNo}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function save(body: FnrsCnsltnMngSaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function saveApply(body: FnrsCnsltnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/save-apply`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(
|
||||
fnrsCnsltnDocNo: string,
|
||||
body: FnrsCnsltnMngUpdateRequestDto,
|
||||
): Promise<string> {
|
||||
const response = await client.put<string>(`${BASE}/${fnrsCnsltnDocNo}`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function techReview(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<void> {
|
||||
await client.post<void>(`${BASE}/${id}/tech-review`, body);
|
||||
}
|
||||
|
||||
async function submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/${id}/submitsupplement`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function submitReqSupplement(
|
||||
id: string,
|
||||
body: FnrsCnsltnApplySaveRequestDto,
|
||||
): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/${id}/reqsupplement`, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function remove(fnrsCnsltnDocNo: string): Promise<void> {
|
||||
await client.delete<void>(`${BASE}/${fnrsCnsltnDocNo}`);
|
||||
}
|
||||
|
||||
async function changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void> {
|
||||
await client.post<void>(`${BASE}/doc/updatePrgrsStts`, body);
|
||||
}
|
||||
|
||||
export const fnrsCnsltnMngService = {
|
||||
findPage,
|
||||
findById,
|
||||
save,
|
||||
saveApply,
|
||||
update,
|
||||
techReview,
|
||||
submitSupplement,
|
||||
submitReqSupplement,
|
||||
remove,
|
||||
changePrgrsSttsComCd,
|
||||
} satisfies FnrsCnsltnMngService;
|
||||
@@ -0,0 +1,131 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
DocPrgrsUpdateRequestDto,
|
||||
FnrsCnsltnApplySaveRequestDto,
|
||||
FnrsCnsltnMngSaveRequestDto,
|
||||
FnrsCnsltnMngSearchDto,
|
||||
FnrsCnsltnMngUpdateRequestDto,
|
||||
} from "../model/fnrs-cnsltn-interface";
|
||||
|
||||
import { fnrsCnsltnMngService } from "./fnrs-cnsltn-service";
|
||||
|
||||
export const fnrsCnsltnMngBaseKey = [ENDPOINT.AS_SERVICE.FNRS_CNSLTN_MNG] as const;
|
||||
|
||||
export function useFnrsCnsltnMngPage(params: FnrsCnsltnMngSearchDto) {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findPage", params],
|
||||
queryFn: () => fnrsCnsltnMngService.findPage(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFnrsCnsltnMng(fnrsCnsltnDocNo: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findById", fnrsCnsltnDocNo],
|
||||
queryFn: () => fnrsCnsltnMngService.findById(fnrsCnsltnDocNo!),
|
||||
enabled: Boolean(fnrsCnsltnDocNo),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: FnrsCnsltnMngSaveRequestDto) => fnrsCnsltnMngService.save(body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveApplyFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: FnrsCnsltnApplySaveRequestDto) => fnrsCnsltnMngService.saveApply(body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
fnrsCnsltnDocNo,
|
||||
body,
|
||||
}: {
|
||||
fnrsCnsltnDocNo: string;
|
||||
body: FnrsCnsltnMngUpdateRequestDto;
|
||||
}) => fnrsCnsltnMngService.update(fnrsCnsltnDocNo, body),
|
||||
onSuccess: (_data, { fnrsCnsltnDocNo }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findById", fnrsCnsltnDocNo],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useTechReviewFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: string; body: FnrsCnsltnApplySaveRequestDto }) =>
|
||||
fnrsCnsltnMngService.techReview(id, body),
|
||||
onSuccess: (_data, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findById", id],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitSupplementFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: string; body: FnrsCnsltnApplySaveRequestDto }) =>
|
||||
fnrsCnsltnMngService.submitSupplement(id, body),
|
||||
onSuccess: (_data, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findById", id],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useSubmitReqSupplementFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: string; body: FnrsCnsltnApplySaveRequestDto }) =>
|
||||
fnrsCnsltnMngService.submitReqSupplement(id, body),
|
||||
onSuccess: (_data, { id }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...fnrsCnsltnMngBaseKey, "findById", id],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFnrsCnsltnMng() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (fnrsCnsltnDocNo: string) => fnrsCnsltnMngService.remove(fnrsCnsltnDocNo),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useChangePrgrsSttsComCd() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (body: DocPrgrsUpdateRequestDto) => fnrsCnsltnMngService.changePrgrsSttsComCd(body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsCnsltnMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
export { fnrsCnsltnMngService } from "./api/fnrs-cnsltn-service";
|
||||
export { fnrsCnsltnMngBaseKey } from "./api/queries";
|
||||
export {
|
||||
useChangePrgrsSttsComCd,
|
||||
useDeleteFnrsCnsltnMng,
|
||||
useFnrsCnsltnMng,
|
||||
useFnrsCnsltnMngPage,
|
||||
useSaveApplyFnrsCnsltnMng,
|
||||
useSaveFnrsCnsltnMng,
|
||||
useSubmitReqSupplementFnrsCnsltnMng,
|
||||
useSubmitSupplementFnrsCnsltnMng,
|
||||
useTechReviewFnrsCnsltnMng,
|
||||
useUpdateFnrsCnsltnMng,
|
||||
} from "./api/queries";
|
||||
export type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstItem,
|
||||
BizCstTreeDto,
|
||||
BizSclTreeDto,
|
||||
BzScaleItem,
|
||||
CklstResultItem,
|
||||
CklstRvwRsltTreeDto,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
ElaprDocPrgrsClientResponseDto,
|
||||
EsntlAtchFileItem,
|
||||
EsntlAtchFileMngTreeResponseDto,
|
||||
FnrsCnsltnApplySaveRequestDto,
|
||||
FnrsCnsltnMngDetResponseDto,
|
||||
FnrsCnsltnMngResponseDto,
|
||||
FnrsCnsltnMngSaveRequestDto,
|
||||
FnrsCnsltnMngSearchDto,
|
||||
FnrsCnsltnMngService,
|
||||
FnrsCnsltnMngUpdateRequestDto,
|
||||
FnrsCnsltnRvwOpnnResponseDto,
|
||||
FnrsCnsltnRvwOpnnSaveRequestDto,
|
||||
FnrsCnsltnSplmntDmndItem,
|
||||
FnrsCnsltnSplmntDmndResponseDto,
|
||||
FnrsSharItem,
|
||||
} from "./model/fnrs-cnsltn-interface";
|
||||
@@ -0,0 +1,177 @@
|
||||
import type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstSaveBase,
|
||||
BzScaleSaveBase,
|
||||
CklstResultSaveBase,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
EsntlAtchFileSaveBase,
|
||||
PageableParams,
|
||||
PageResponse,
|
||||
PbadmsApplySaveHeader,
|
||||
PbadmsMngDetResponseBase,
|
||||
RvwOpnnResponseExtension,
|
||||
RvwOpnnSaveBase,
|
||||
SplmntDmndResponseExtension,
|
||||
SplmntDmndSaveBase,
|
||||
} from "@/shared/model";
|
||||
import type { FnrsSharBizCstTreeDto } from "@/entities/fnrs-shar-se";
|
||||
|
||||
export type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstTreeDto,
|
||||
BizSclTreeDto,
|
||||
CklstRvwRsltTreeDto,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
ElaprDocPrgrsClientResponseDto,
|
||||
EsntlAtchFileMngTreeResponseDto,
|
||||
} from "@/shared/model";
|
||||
|
||||
/** 백엔드 FnrsCnsltnMngSaveRequestDto */
|
||||
export interface FnrsCnsltnMngSaveRequestDto {
|
||||
fnrsCnsltnDocNo?: string;
|
||||
admdstCd?: string;
|
||||
envoSeComCd?: string;
|
||||
taskSeComCd?: string;
|
||||
cfmtnBgtBizNo?: string;
|
||||
bgtAplyYr?: string;
|
||||
bizNm?: string;
|
||||
bizBgngYr?: string;
|
||||
bizEndYr?: string;
|
||||
bizCn?: string;
|
||||
aplcntNm?: string;
|
||||
aplcntMblTelno?: string;
|
||||
aplcntOgdpNm?: string;
|
||||
aplcntEmlAddr?: string;
|
||||
dsgnrNm?: string;
|
||||
dsgnrTelno?: string;
|
||||
dsgnrOgdpNm?: string;
|
||||
dsgnrEmlAddr?: string;
|
||||
atchFileGroupId?: string;
|
||||
sharAtchFileGroupId?: string;
|
||||
prgrsSttsComCd?: string;
|
||||
rtlztnFcltSeComCd?: string;
|
||||
rtlztnUsgSeComCd?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsCnsltnMngUpdateRequestDto */
|
||||
export type FnrsCnsltnMngUpdateRequestDto = Omit<FnrsCnsltnMngSaveRequestDto, "fnrsCnsltnDocNo">;
|
||||
|
||||
/** 백엔드 FnrsCnsltnMngSearchDto + Pageable */
|
||||
export interface FnrsCnsltnMngSearchDto extends PageableParams {
|
||||
fnrsCnsltnDocNo?: string;
|
||||
admdstCd?: string;
|
||||
envoSeComCd?: string;
|
||||
taskSeComCd?: string;
|
||||
cfmtnBgtBizNo?: string;
|
||||
bgtAplyYr?: string;
|
||||
bizNm?: string;
|
||||
bizBgngYr?: string;
|
||||
bizEndYr?: string;
|
||||
bizCn?: string;
|
||||
aplcntNm?: string;
|
||||
aplcntMblTelno?: string;
|
||||
aplcntOgdpNm?: string;
|
||||
aplcntEmlAddr?: string;
|
||||
dsgnrNm?: string;
|
||||
dsgnrTelno?: string;
|
||||
dsgnrOgdpNm?: string;
|
||||
dsgnrEmlAddr?: string;
|
||||
atchFileGroupId?: string;
|
||||
sharAtchFileGroupId?: string;
|
||||
prgrsSttsComCd?: string;
|
||||
}
|
||||
|
||||
interface FnrsCnsltnMngResponseExtension {
|
||||
fnrsCnsltnDocNo: string;
|
||||
envoSeCdNm?: string;
|
||||
taskSeCdNm?: string;
|
||||
prgrsSttsCdNm?: string;
|
||||
regDt?: string;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
mdfcnDt?: string;
|
||||
sido?: string;
|
||||
gugun?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsCnsltnMngResponseDto */
|
||||
export type FnrsCnsltnMngResponseDto = Omit<FnrsCnsltnMngSaveRequestDto, "fnrsCnsltnDocNo"> &
|
||||
FnrsCnsltnMngResponseExtension;
|
||||
|
||||
export type BzScaleItem = BzScaleSaveBase;
|
||||
|
||||
export type BizCstItem = BizCstSaveBase;
|
||||
|
||||
export interface CklstResultItem extends CklstResultSaveBase {
|
||||
selected?: string;
|
||||
}
|
||||
|
||||
export type EsntlAtchFileItem = EsntlAtchFileSaveBase;
|
||||
|
||||
export type FnrsCnsltnSplmntDmndItem = SplmntDmndSaveBase;
|
||||
|
||||
/** 백엔드 FnrsCnsltnApplySaveRequestDto.FnrsSharItem */
|
||||
export interface FnrsSharItem {
|
||||
fnrsSharSeCd?: string;
|
||||
exstnAmt?: number;
|
||||
chgRqtAmt?: number;
|
||||
ajmtAmt?: number;
|
||||
rmrkCn?: string;
|
||||
envoRmrkCn?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsCnsltnRvwOpnnSaveRequestDto */
|
||||
export interface FnrsCnsltnRvwOpnnSaveRequestDto extends RvwOpnnSaveBase {
|
||||
fnrsCnsltnDocNo: string;
|
||||
rvwOpnnSn: number;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsCnsltnRvwOpnnResponseDto */
|
||||
export type FnrsCnsltnRvwOpnnResponseDto = Partial<
|
||||
Pick<FnrsCnsltnRvwOpnnSaveRequestDto, "fnrsCnsltnDocNo" | "rvwOpnnSn">
|
||||
> &
|
||||
Omit<FnrsCnsltnRvwOpnnSaveRequestDto, "fnrsCnsltnDocNo" | "rvwOpnnSn"> &
|
||||
RvwOpnnResponseExtension;
|
||||
|
||||
/** 백엔드 FnrsCnsltnSplmntDmndResponseDto */
|
||||
export type FnrsCnsltnSplmntDmndResponseDto = FnrsCnsltnSplmntDmndItem &
|
||||
SplmntDmndResponseExtension & {
|
||||
fnrsCnsltnDocNo?: string;
|
||||
};
|
||||
|
||||
/** 백엔드 FnrsCnsltnMngDetResponseDto */
|
||||
export type FnrsCnsltnMngDetResponseDto = PbadmsMngDetResponseBase<
|
||||
FnrsCnsltnMngResponseDto,
|
||||
FnrsCnsltnRvwOpnnResponseDto,
|
||||
FnrsCnsltnSplmntDmndResponseDto
|
||||
> & {
|
||||
fnrsSharList?: FnrsSharBizCstTreeDto[];
|
||||
sharAtchFileList?: AtflMngClientResponseDto[];
|
||||
};
|
||||
|
||||
/** 백엔드 FnrsCnsltnApplySaveRequestDto */
|
||||
export interface FnrsCnsltnApplySaveRequestDto extends PbadmsApplySaveHeader {
|
||||
fnrsCnsltnMng: FnrsCnsltnMngSaveRequestDto;
|
||||
bzScaleList?: BzScaleItem[];
|
||||
bizCstList?: BizCstItem[];
|
||||
cklstResultList?: CklstResultItem[];
|
||||
cklstResultMap?: Record<string, CklstResultItem[]>;
|
||||
esntlAtchFileList?: EsntlAtchFileItem[];
|
||||
rvwOpnnList?: FnrsCnsltnRvwOpnnSaveRequestDto[];
|
||||
splmntDmndList?: FnrsCnsltnSplmntDmndItem[];
|
||||
fnrsSharList?: FnrsSharItem[];
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface FnrsCnsltnMngService {
|
||||
findPage(searchDto: FnrsCnsltnMngSearchDto): Promise<PageResponse<FnrsCnsltnMngResponseDto>>;
|
||||
findById(fnrsCnsltnDocNo: string): Promise<FnrsCnsltnMngDetResponseDto>;
|
||||
save(body: FnrsCnsltnMngSaveRequestDto): Promise<string>;
|
||||
saveApply(body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
update(fnrsCnsltnDocNo: string, body: FnrsCnsltnMngUpdateRequestDto): Promise<string>;
|
||||
techReview(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<void>;
|
||||
submitSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
submitReqSupplement(id: string, body: FnrsCnsltnApplySaveRequestDto): Promise<string>;
|
||||
remove(fnrsCnsltnDocNo: string): Promise<void>;
|
||||
changePrgrsSttsComCd(body: DocPrgrsUpdateRequestDto): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import {
|
||||
FNRS_SHAR_SE_SAVE_MODE,
|
||||
type FnrsSharBizCstTreeDto,
|
||||
type FnrsSharSeMngResponseDto,
|
||||
type FnrsSharSeMngSaveRequestDto,
|
||||
type FnrsSharSeMngSearchDto,
|
||||
type FnrsSharSeMngService,
|
||||
type FnrsSharSeMngTreeDto,
|
||||
} from "../model/fnrs-shar-se-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.FNRS_SHAR_SE;
|
||||
|
||||
async function findAll(
|
||||
params: FnrsSharSeMngSearchDto,
|
||||
): Promise<PageResponse<FnrsSharSeMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<FnrsSharSeMngResponseDto>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findTree(): Promise<FnrsSharSeMngTreeDto[]> {
|
||||
const response = await client.get<FnrsSharSeMngTreeDto[]>(`${BASE}/list/tree`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findById(fnrsSharSeCd: string): Promise<FnrsSharSeMngResponseDto> {
|
||||
const response = await client.get<FnrsSharSeMngResponseDto>(`${BASE}/${fnrsSharSeCd}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function saveMulti(
|
||||
body: FnrsSharSeMngSaveRequestDto[],
|
||||
): Promise<FnrsSharSeMngResponseDto[]> {
|
||||
const response = await client.post<FnrsSharSeMngResponseDto[]>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(fnrsSharSeCd: string, body: FnrsSharSeMngSaveRequestDto): Promise<string> {
|
||||
const response = await client.put<string>(`${BASE}/${fnrsSharSeCd}`, {
|
||||
...body,
|
||||
fnrsSharSeCd,
|
||||
mode: FNRS_SHAR_SE_SAVE_MODE.UPDATE,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function deleteFnrsSharSeCd(fnrsSharSeCd: string): Promise<string> {
|
||||
const response = await client.delete<string>(`${BASE}/${fnrsSharSeCd}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findTreeFnrsShar(fnrsCnsltnDocNo: string): Promise<FnrsSharBizCstTreeDto[]> {
|
||||
const response = await client.get<FnrsSharBizCstTreeDto[]>(
|
||||
`${BASE}/list/tree/fnrs-shar/${fnrsCnsltnDocNo}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const fnrsSharSeMngService = {
|
||||
findAll,
|
||||
findTree,
|
||||
findById,
|
||||
saveMulti,
|
||||
update,
|
||||
delete: deleteFnrsSharSeCd,
|
||||
findTreeFnrsShar,
|
||||
} satisfies FnrsSharSeMngService;
|
||||
@@ -0,0 +1,85 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
FnrsSharSeMngSaveRequestDto,
|
||||
FnrsSharSeMngSearchDto,
|
||||
} from "../model/fnrs-shar-se-interface";
|
||||
|
||||
import { fnrsSharSeMngService } from "./fnrs-shar-se-service";
|
||||
|
||||
const fnrsSharSeMngBaseKey = [ENDPOINT.AS_SERVICE.FNRS_SHAR_SE] as const;
|
||||
|
||||
export function useFnrsSharSeMngPage(params: FnrsSharSeMngSearchDto) {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsSharSeMngBaseKey, "findAll", params],
|
||||
queryFn: () => fnrsSharSeMngService.findAll(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useFnrsSharSeMngTree() {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsSharSeMngBaseKey, "findTree"],
|
||||
queryFn: () => fnrsSharSeMngService.findTree(),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
}
|
||||
|
||||
export function useFnrsSharSeMng(fnrsSharSeCd: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsSharSeMngBaseKey, "findById", fnrsSharSeCd],
|
||||
queryFn: () => fnrsSharSeMngService.findById(fnrsSharSeCd!),
|
||||
enabled: Boolean(fnrsSharSeCd),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveMultiFnrsSharSeMng() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (body: FnrsSharSeMngSaveRequestDto[]) => fnrsSharSeMngService.saveMulti(body),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsSharSeMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateFnrsSharSeMng() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
fnrsSharSeCd,
|
||||
body,
|
||||
}: {
|
||||
fnrsSharSeCd: string;
|
||||
body: FnrsSharSeMngSaveRequestDto;
|
||||
}) => fnrsSharSeMngService.update(fnrsSharSeCd, body),
|
||||
onSuccess: (_data, { fnrsSharSeCd }) => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsSharSeMngBaseKey });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...fnrsSharSeMngBaseKey, "findById", fnrsSharSeCd],
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteFnrsSharSeMng() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (fnrsSharSeCd: string) => fnrsSharSeMngService.delete(fnrsSharSeCd),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: fnrsSharSeMngBaseKey });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useFnrsSharBizCstTree(fnrsCnsltnDocNo: string | null | undefined) {
|
||||
return useQuery({
|
||||
queryKey: [...fnrsSharSeMngBaseKey, "findTreeFnrsShar", fnrsCnsltnDocNo],
|
||||
queryFn: () => fnrsSharSeMngService.findTreeFnrsShar(fnrsCnsltnDocNo!),
|
||||
enabled: Boolean(fnrsCnsltnDocNo),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export { fnrsSharSeMngService } from "./api/fnrs-shar-se-service";
|
||||
export {
|
||||
useDeleteFnrsSharSeMng,
|
||||
useFnrsSharBizCstTree,
|
||||
useFnrsSharSeMng,
|
||||
useFnrsSharSeMngPage,
|
||||
useFnrsSharSeMngTree,
|
||||
useSaveMultiFnrsSharSeMng,
|
||||
useUpdateFnrsSharSeMng,
|
||||
} from "./api/queries";
|
||||
export type {
|
||||
FnrsSharBizCstTreeDto,
|
||||
FnrsSharSeMngResponseDto,
|
||||
FnrsSharSeMngSaveRequestDto,
|
||||
FnrsSharSeMngSearchDto,
|
||||
FnrsSharSeMngService,
|
||||
FnrsSharSeMngTreeDto,
|
||||
FnrsSharSeSaveMode,
|
||||
} from "./model/fnrs-shar-se-interface";
|
||||
export { FNRS_SHAR_SE_SAVE_MODE } from "./model/fnrs-shar-se-interface";
|
||||
@@ -0,0 +1,83 @@
|
||||
import type { PageableParams, PageResponse } from "@/shared/model";
|
||||
|
||||
export const FNRS_SHAR_SE_SAVE_MODE = {
|
||||
CREATE: "C",
|
||||
UPDATE: "U",
|
||||
DELETE: "D",
|
||||
} as const;
|
||||
|
||||
export type FnrsSharSeSaveMode = (typeof FNRS_SHAR_SE_SAVE_MODE)[keyof typeof FNRS_SHAR_SE_SAVE_MODE];
|
||||
|
||||
/** 백엔드 FnrsSharSeMngResponseDto */
|
||||
export interface FnrsSharSeMngResponseDto {
|
||||
fnrsSharSeCd: string;
|
||||
fnrsSharSeCdNm: string;
|
||||
upFnrsSharSeCd?: string;
|
||||
cdLvNo: number;
|
||||
useYn: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
mdfr?: string;
|
||||
regDt?: string;
|
||||
mdfcnDt?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsSharSeMngSearchDto + Pageable */
|
||||
export interface FnrsSharSeMngSearchDto extends PageableParams {
|
||||
fnrsSharSeCd?: string;
|
||||
fnrsSharSeCdNm?: string;
|
||||
useYn?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsSharSeMngSaveRequestDto */
|
||||
export interface FnrsSharSeMngSaveRequestDto {
|
||||
fnrsSharSeCd: string;
|
||||
mode: FnrsSharSeSaveMode;
|
||||
fnrsSharSeCdNm: string;
|
||||
upFnrsSharSeCd?: string;
|
||||
cdLvNo: number;
|
||||
useYn?: string;
|
||||
expln?: string;
|
||||
sortSeq?: number;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsSharSeMngTreeDto */
|
||||
export interface FnrsSharSeMngTreeDto {
|
||||
indFnrsSharSeCdNm: string;
|
||||
fnrsSharSeCd: string;
|
||||
fnrsSharSeCdNm: string;
|
||||
upFnrsSharSeCd?: string;
|
||||
useYn: string;
|
||||
treeLevel: number;
|
||||
treePath: string;
|
||||
sortPath: string;
|
||||
}
|
||||
|
||||
/** 백엔드 FnrsSharBizCstTreeDto */
|
||||
export interface FnrsSharBizCstTreeDto {
|
||||
indFnrsSharSeCdNm: string;
|
||||
fnrsSharSeCd: string;
|
||||
fnrsSharSeCdNm: string;
|
||||
useYn: string;
|
||||
treeLevel: number;
|
||||
treePath: string;
|
||||
sortPath: string;
|
||||
exstnAmt?: number;
|
||||
chgRqtAmt?: number;
|
||||
ajmtAmt?: number;
|
||||
rmrkCn?: string;
|
||||
envoRmrkCn?: string;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface FnrsSharSeMngService {
|
||||
findAll(params: FnrsSharSeMngSearchDto): Promise<PageResponse<FnrsSharSeMngResponseDto>>;
|
||||
findTree(): Promise<FnrsSharSeMngTreeDto[]>;
|
||||
findById(fnrsSharSeCd: string): Promise<FnrsSharSeMngResponseDto>;
|
||||
saveMulti(body: FnrsSharSeMngSaveRequestDto[]): Promise<FnrsSharSeMngResponseDto[]>;
|
||||
update(fnrsSharSeCd: string, body: FnrsSharSeMngSaveRequestDto): Promise<string>;
|
||||
delete(fnrsSharSeCd: string): Promise<string>;
|
||||
findTreeFnrsShar(fnrsCnsltnDocNo: string): Promise<FnrsSharBizCstTreeDto[]>;
|
||||
}
|
||||
@@ -1,41 +1,91 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
AdstZoneResponseDto,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
InstlSncnApplySaveRequestDto,
|
||||
InstlSncnMng,
|
||||
InstlSncnMngDetResponse,
|
||||
InstlSncnMngResponseDto,
|
||||
InstlSncnMngSaveRequestDto,
|
||||
InstlSncnMngSearchParams,
|
||||
InstlSncnMngService,
|
||||
SpringPage,
|
||||
InstlSncnMngDetResponse,
|
||||
InstlSncnMngUpdateRequestDto,
|
||||
} from "../model/instl-sncn-interface";
|
||||
|
||||
export class InstlSncnApiService implements InstlSncnMngService {
|
||||
public async getPage(params: InstlSncnMngSearchParams): Promise<SpringPage<InstlSncnMng>> {
|
||||
const response = await client.get<SpringPage<InstlSncnMng>>(ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
async function getSidoCombo(): Promise<AdstZoneResponseDto[]> {
|
||||
const response = await client.get<AdstZoneResponseDto[]>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sido`,
|
||||
);
|
||||
|
||||
public async getById(id: string): Promise<InstlSncnMngDetResponse> {
|
||||
const response = await client.get<InstlSncnMngDetResponse>(`${ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN}/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
public async saveApply(payload: InstlSncnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(
|
||||
`${ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN}/save-apply`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
public async techReview(payload: InstlSncnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(
|
||||
`${ENDPOINT.AS_INSTL_SERVICE.INSTL_SNCN}/tech-review`,
|
||||
payload,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const InstlSncnApi = new InstlSncnApiService();
|
||||
async function getSigunguCombo(sidoCd: string): Promise<AdstZoneResponseDto[]> {
|
||||
const response = await client.get<AdstZoneResponseDto[]>(
|
||||
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sigungu`,
|
||||
{ params: { sidoCd } },
|
||||
);
|
||||
|
||||
return response.data;
|
||||
}
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.INSTL_SNCN;
|
||||
|
||||
async function getPage(
|
||||
params: InstlSncnMngSearchParams,
|
||||
): Promise<PageResponse<InstlSncnMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<InstlSncnMngResponseDto>>(BASE, {
|
||||
params,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function getById(id: string): Promise<InstlSncnMngDetResponse> {
|
||||
const response = await client.get<InstlSncnMngDetResponse>(`${BASE}/${id}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function save(payload: InstlSncnMngSaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(BASE, payload);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function saveApply(payload: InstlSncnApplySaveRequestDto): Promise<string> {
|
||||
const response = await client.post<string>(`${BASE}/save-apply`, payload);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(id: string, payload: InstlSncnMngUpdateRequestDto): Promise<void> {
|
||||
await client.put(`${BASE}/${id}`, payload);
|
||||
}
|
||||
|
||||
async function techReview(id: string, payload: InstlSncnApplySaveRequestDto): Promise<void> {
|
||||
await client.post(`${BASE}/${id}/tech-review`, payload);
|
||||
}
|
||||
|
||||
async function remove(id: string): Promise<void> {
|
||||
await client.delete(`${BASE}/${id}`);
|
||||
}
|
||||
|
||||
async function changePrgrsSttsComCd(payload: DocPrgrsUpdateRequestDto): Promise<void> {
|
||||
await client.post(`${BASE}/doc/updatePrgrsStts`, payload);
|
||||
}
|
||||
|
||||
export const instlSncnService = {
|
||||
getPage,
|
||||
getById,
|
||||
save,
|
||||
saveApply,
|
||||
update,
|
||||
techReview,
|
||||
remove,
|
||||
changePrgrsSttsComCd,
|
||||
} satisfies InstlSncnMngService;
|
||||
|
||||
// TODO: st-anls-mst service 로 분리
|
||||
export const instlSncnRegionService = {
|
||||
getSidoCombo,
|
||||
getSigunguCombo,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
DocPrgrsUpdateRequestDto,
|
||||
InstlSncnApplySaveRequestDto,
|
||||
InstlSncnMngSaveRequestDto,
|
||||
InstlSncnMngSearchParams,
|
||||
InstlSncnMngUpdateRequestDto,
|
||||
} from "../model/instl-sncn-interface";
|
||||
|
||||
import { instlSncnRegionService, instlSncnService } from "./instl-sncn-service";
|
||||
|
||||
export const instlSncnKeys = {
|
||||
all: ["instl-sncn"] as const,
|
||||
list: () => [...instlSncnKeys.all, "list"] as const,
|
||||
detail: (id: string) => [...instlSncnKeys.all, "detail", id] as const,
|
||||
sidoCombo: () => [ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sido"] as const,
|
||||
sigunguCombo: (sidoCd: string) =>
|
||||
[ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST, "sigungu", sidoCd] as const,
|
||||
};
|
||||
|
||||
export function useInstlSncnList(params: InstlSncnMngSearchParams) {
|
||||
return useQuery({
|
||||
queryKey: [...instlSncnKeys.list(), params],
|
||||
queryFn: () => instlSncnService.getPage(params),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnDetail(id: string) {
|
||||
return useQuery({
|
||||
queryKey: instlSncnKeys.detail(id),
|
||||
queryFn: () => instlSncnService.getById(id),
|
||||
enabled: Boolean(id),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnSidoCombo() {
|
||||
return useQuery({
|
||||
queryKey: instlSncnKeys.sidoCombo(),
|
||||
queryFn: () => instlSncnRegionService.getSidoCombo(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnSigunguCombo(sidoCd: string) {
|
||||
return useQuery({
|
||||
queryKey: instlSncnKeys.sigunguCombo(sidoCd),
|
||||
queryFn: () => instlSncnRegionService.getSigunguCombo(sidoCd),
|
||||
enabled: Boolean(sidoCd),
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnSave() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (payload: InstlSncnMngSaveRequestDto) => instlSncnService.save(payload),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnSaveApply() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (payload: InstlSncnApplySaveRequestDto) => instlSncnService.saveApply(payload),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnUpdate() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, payload }: { id: string; payload: InstlSncnMngUpdateRequestDto }) =>
|
||||
instlSncnService.update(id, payload),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.detail(variables.id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnTechReview() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: ({ id, payload }: { id: string; payload: InstlSncnApplySaveRequestDto }) =>
|
||||
instlSncnService.techReview(id, payload),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.detail(variables.id) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnRemove() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => instlSncnService.remove(id),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useInstlSncnChangePrgrsStts() {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (payload: DocPrgrsUpdateRequestDto) =>
|
||||
instlSncnService.changePrgrsSttsComCd(payload),
|
||||
onSuccess: (_, variables) => {
|
||||
queryClient.invalidateQueries({ queryKey: instlSncnKeys.all });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: instlSncnKeys.detail(variables.docNo),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,12 +1,33 @@
|
||||
export { InstlSncnApi } from "./api/instl-sncn-service";
|
||||
export { instlSncnRegionService, instlSncnService } from "./api/instl-sncn-service";
|
||||
export {
|
||||
instlSncnKeys,
|
||||
useInstlSncnDetail,
|
||||
useInstlSncnList,
|
||||
useInstlSncnSidoCombo,
|
||||
useInstlSncnSigunguCombo,
|
||||
useInstlSncnTechReview,
|
||||
} from "./api/queries";
|
||||
export {
|
||||
type AdstZoneResponseDto,
|
||||
type AtflMngClientResponseDto,
|
||||
type BizCstTreeDto,
|
||||
type BizSclTreeDto,
|
||||
type CklstRvwRsltTreeDto,
|
||||
type DocPrgrsUpdateRequestDto,
|
||||
type ElaprDocPrgrsClientResponseDto,
|
||||
type EsntlAtchFileMngTreeResponseDto,
|
||||
INSTL_SNCN_REGISTER_SELECTION_KEY,
|
||||
type InstlSncnApplySaveRequestDto,
|
||||
type InstlSncnBizCstSaveRequestDto,
|
||||
type InstlSncnBzScaleSaveDto,
|
||||
type InstlSncnCklstResultSaveDto,
|
||||
type InstlSncnMng,
|
||||
type InstlSncnEsntlAtchFileSaveDto,
|
||||
type InstlSncnMngDetResponse,
|
||||
type InstlSncnMngResponseDto,
|
||||
type InstlSncnMngSaveRequestDto,
|
||||
type InstlSncnMngSearchParams,
|
||||
type SpringPage,
|
||||
type ElaprDocPrgrsClientResponseDto,
|
||||
type InstlSncnRvwOpnnResponseDto,
|
||||
type InstlSncnRvwOpnnSaveRequestDto,
|
||||
type InstlSncnSplmntDmndSaveDto,
|
||||
type ReviewStepCode,
|
||||
} from "./model/instl-sncn-interface";
|
||||
|
||||
@@ -1,78 +1,149 @@
|
||||
export const INSTL_SNCN_REGISTER_SELECTION_KEY = "instlSncnRegisterSelection";
|
||||
import type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstSaveBase,
|
||||
BizCstTreeDto,
|
||||
BizSclTreeDto,
|
||||
BzScaleSaveBase,
|
||||
CklstResultSaveBase,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
ElaprDocPrgrsClientResponseDto,
|
||||
EsntlAtchFileMngTreeResponseDto,
|
||||
EsntlAtchFileSaveBase,
|
||||
PageableParams,
|
||||
PageResponse,
|
||||
PbadmsApplySaveHeader,
|
||||
PbadmsMngDetResponseBase,
|
||||
RvwOpnnResponseExtension,
|
||||
RvwOpnnSaveBase,
|
||||
SplmntDmndSaveBase,
|
||||
} from "@/shared/model";
|
||||
|
||||
export interface SpringPage<T> {
|
||||
content: T[];
|
||||
totalElements: number;
|
||||
totalPages: number;
|
||||
number: number;
|
||||
size: number;
|
||||
first: boolean;
|
||||
last: boolean;
|
||||
numberOfElements: number;
|
||||
export type {
|
||||
AtflMngClientResponseDto,
|
||||
BizCstTreeDto,
|
||||
BizSclTreeDto,
|
||||
CklstRvwRsltTreeDto,
|
||||
DocPrgrsUpdateRequestDto,
|
||||
ElaprDocPrgrsClientResponseDto,
|
||||
EsntlAtchFileMngTreeResponseDto,
|
||||
} from "@/shared/model";
|
||||
|
||||
/** 행정구역(시도·시군구) 콤보 응답 DTO */
|
||||
export interface AdstZoneResponseDto {
|
||||
/** 행정구역 코드 */
|
||||
adstCd: string;
|
||||
/** 행정구역 명 */
|
||||
adstNm: string;
|
||||
}
|
||||
|
||||
export interface InstlSncnMngSearchParams {
|
||||
export const INSTL_SNCN_REGISTER_SELECTION_KEY = "instlSncnRegisterSelection";
|
||||
|
||||
export interface InstlSncnMngSearchParams extends PageableParams {
|
||||
bizNm?: string;
|
||||
taskSeComCd?: string;
|
||||
sidoComCd?: string;
|
||||
sggComCd?: string;
|
||||
page?: number;
|
||||
size?: number;
|
||||
sort?: string;
|
||||
}
|
||||
|
||||
export interface InstlSncnMng {
|
||||
gugun: string;
|
||||
sido: string;
|
||||
instlSncnDocNo: string;
|
||||
admdstCd: string;
|
||||
sidoNm?: string;
|
||||
sggNm?: string;
|
||||
chrgrNm?: string;
|
||||
instNm?: string;
|
||||
envoSeComCd: string;
|
||||
/** 설치인가관리 저장 요청 DTO */
|
||||
export interface InstlSncnMngSaveRequestDto {
|
||||
/** 설치인가문서번호 */
|
||||
instlSncnDocNo?: string;
|
||||
/** 행정구역코드 */
|
||||
admdstCd?: string;
|
||||
/** 환경청구분공통코드 */
|
||||
envoSeComCd?: string;
|
||||
/** 인가구분공통코드 */
|
||||
sncnSeComCd: string;
|
||||
envoSeCdNm: string;
|
||||
/** 업무구분공통코드 */
|
||||
taskSeComCd: string;
|
||||
taskSeCdNm: string;
|
||||
bgtBizCd: string;
|
||||
bgtAplyYr: string;
|
||||
/** 예산사업코드 */
|
||||
bgtBizCd?: string;
|
||||
/** 예산신청연도 */
|
||||
bgtAplyYr?: string;
|
||||
/** 사업명 */
|
||||
bizNm: string;
|
||||
/** 사업시작연도 */
|
||||
bizBgngYr: string;
|
||||
/** 사업종료연도 */
|
||||
bizEndYr: string;
|
||||
/** 사업내용 */
|
||||
bizCn: string;
|
||||
aplcntNm: string;
|
||||
aplcntMblTelno: string;
|
||||
aplcntOgdpNm: string;
|
||||
aplcntEmlAddr: string;
|
||||
/** 신청자명 */
|
||||
aplcntNm?: string;
|
||||
/** 신청자휴대전화번호 */
|
||||
aplcntMblTelno?: string;
|
||||
/** 신청자소속명 */
|
||||
aplcntOgdpNm?: string;
|
||||
/** 신청자이메일주소 */
|
||||
aplcntEmlAddr?: string;
|
||||
/** 설계자명 */
|
||||
dsgnrNm: string;
|
||||
/** 설계자전화번호 */
|
||||
dsgnrTelno: string;
|
||||
/** 설계자소속명 */
|
||||
dsgnrOgdpNm: string;
|
||||
/** 설계자이메일주소 */
|
||||
dsgnrEmlAddr: string;
|
||||
atchFileGroupId: string;
|
||||
prgrsSttsComCd: string;
|
||||
/** 첨부파일그룹아이디 */
|
||||
atchFileGroupId?: string;
|
||||
/** 진행상태공통코드 */
|
||||
prgrsSttsComCd?: string;
|
||||
/** 재이용시설구분공통코드 */
|
||||
rtlztnFcltSeComCd?: string;
|
||||
/** 재이용용도구분공통코드 */
|
||||
rtlztnUsgSeComCd?: string;
|
||||
}
|
||||
|
||||
/** 설치인가관리 수정 요청 DTO */
|
||||
export type InstlSncnMngUpdateRequestDto = InstlSncnMngSaveRequestDto;
|
||||
|
||||
/** 설치인가관리 응답 DTO */
|
||||
export interface InstlSncnMngResponseDto extends InstlSncnMngSaveRequestDto {
|
||||
/** 설치인가문서번호 (필수로 재정의) */
|
||||
instlSncnDocNo: string;
|
||||
/** 환경청구분공통코드명 */
|
||||
envoSeCdNm: string;
|
||||
/** 인가구분공통코드명 */
|
||||
sncnSeCdNm: string;
|
||||
/** 업무구분공통코드명 */
|
||||
taskSeCdNm: string;
|
||||
/** 진행상태공통코드명 */
|
||||
prgrsSttsCdNm: string;
|
||||
/** 재이용시설구분공통코드명 */
|
||||
rtlztnFcltSeComNm: string;
|
||||
/** 재이용용도구분공통코드명 */
|
||||
rtlztnUsgSeComNm: string;
|
||||
/** 등록일시 */
|
||||
regDt: string;
|
||||
mdfcnDt: string;
|
||||
/** 등록자 UUID */
|
||||
rgtr: string;
|
||||
/** 등록자명 */
|
||||
rgtrNm: string;
|
||||
/** 수정 일시 */
|
||||
mdfcnDt: string;
|
||||
/** 시도 */
|
||||
sido: string;
|
||||
/** 구군 */
|
||||
gugun: string;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 사업규모 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnBzScaleSaveDto {
|
||||
export interface InstlSncnBzScaleSaveDto extends BzScaleSaveBase {
|
||||
/** 공종구분코드 */
|
||||
cnstpSeCd: string;
|
||||
/** 기본계획(A) */
|
||||
/** 기본계획수량 */
|
||||
bscPl: number;
|
||||
/** 예산반영(B) */
|
||||
/** 예산반영수량 */
|
||||
budgetReflct: number;
|
||||
/** 금회설계시(C) */
|
||||
/** 금회설계수량 */
|
||||
thtiDsgn: number;
|
||||
/** 증감(C-B) */
|
||||
diff: number;
|
||||
/** 증감(C-B) — 클라이언트 계산용, API 미전송 */
|
||||
diff?: number;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 총사업비 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnBizCstSaveRequestDto {
|
||||
export interface InstlSncnBizCstSaveRequestDto extends BizCstSaveBase {
|
||||
/** 회계구분코드 */
|
||||
acntgSeCd: string;
|
||||
/** 기본계획금액 */
|
||||
@@ -84,66 +155,90 @@ export interface InstlSncnBizCstSaveRequestDto {
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 체크리스트 결과 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnCklstResultSaveDto {
|
||||
export interface InstlSncnCklstResultSaveDto extends CklstResultSaveBase {
|
||||
/** 점검표일련번호 */
|
||||
cklstSn: number;
|
||||
/** 점검표항목일련번호 */
|
||||
cklstArtclSn: number;
|
||||
/** 점검표검토일련번호 */
|
||||
cklstRvwSn: number;
|
||||
rvwStpComCd: string;
|
||||
/** 선택값(Y/N/null) */
|
||||
selected: "Y" | "N" | null;
|
||||
/** 담당자검토내용(기술검토의견) */
|
||||
picRvwCn?: string | null;
|
||||
}
|
||||
|
||||
/** 설치인가 신청 - 필수첨부파일 저장 DTO(배열 원소) */
|
||||
export interface InstlSncnEsntlAtchFileSaveDto {
|
||||
export interface InstlSncnEsntlAtchFileSaveDto extends EsntlAtchFileSaveBase {
|
||||
/** 필수첨부파일아이디 */
|
||||
esntlAtchFileId: string;
|
||||
/** 첨부파일그룹아이디 */
|
||||
atchFileGroupId: string;
|
||||
}
|
||||
|
||||
/** 설치인가검토의견 저장 요청 DTO */
|
||||
export interface InstlSncnRvwOpnnSaveRequestDto extends RvwOpnnSaveBase {
|
||||
/** 설치인가문서번호 */
|
||||
instlSncnDocNo: string;
|
||||
}
|
||||
|
||||
/** 설치인가 보완요구 저장 DTO(배열 원소) */
|
||||
export type InstlSncnSplmntDmndSaveDto = SplmntDmndSaveBase;
|
||||
|
||||
/** 설치인가 신청 - 통합 저장 요청 DTO */
|
||||
export interface InstlSncnApplySaveRequestDto {
|
||||
instlSncnMng: InstlSncnMng;
|
||||
export interface InstlSncnApplySaveRequestDto extends PbadmsApplySaveHeader {
|
||||
/** 설치인가관리 정보 */
|
||||
instlSncnMng: InstlSncnMngSaveRequestDto;
|
||||
/** 사업규모 목록 */
|
||||
bzScaleList: InstlSncnBzScaleSaveDto[];
|
||||
/** 총사업비 목록 */
|
||||
bizCstList: InstlSncnBizCstSaveRequestDto[];
|
||||
/** 체크리스트 검토결과 목록 */
|
||||
cklstResultList: InstlSncnCklstResultSaveDto[];
|
||||
/** 체크리스트 검토결과 맵 (rvwStpComCd -> 목록) */
|
||||
cklstResultMap?: Record<string, InstlSncnCklstResultSaveDto[]>;
|
||||
/** 필수첨부파일 목록 */
|
||||
esntlAtchFileList: InstlSncnEsntlAtchFileSaveDto[];
|
||||
/** 검토의견 목록 */
|
||||
rvwOpnnList?: InstlSncnRvwOpnnSaveRequestDto[];
|
||||
/** 보완요구 목록 */
|
||||
splmntDmndList?: InstlSncnSplmntDmndSaveDto[];
|
||||
}
|
||||
|
||||
export type ReviewStepCode = "LOCAL" | "ENV" | "KECO";
|
||||
|
||||
/** 설치인가검토의견 응답 DTO */
|
||||
export type InstlSncnRvwOpnnResponseDto = InstlSncnRvwOpnnSaveRequestDto &
|
||||
RvwOpnnResponseExtension & {
|
||||
instlSncnDocNo: string;
|
||||
rvwOpnnSn: number;
|
||||
opnrSeComCd: string;
|
||||
opnrNm: string;
|
||||
rvwRsltCn: string;
|
||||
rvwRsltAnmlyYn: string;
|
||||
atchFileGroupId: string | null;
|
||||
opnrSeCdNm: string | null;
|
||||
regDt: string;
|
||||
};
|
||||
|
||||
|
||||
/** 전자결재 진행상태 (클라이언트 응답용) */
|
||||
export interface ElaprDocPrgrsClientResponseDto {
|
||||
elaprDocNo?: string;
|
||||
elaprDmndId?: string;
|
||||
elaprPrgrsId?: string;
|
||||
atrzSeqNo?: number;
|
||||
atrzSeComCd?: string;
|
||||
atrzSeComNm?: string;
|
||||
aprvrUuid?: string;
|
||||
aprvrUuidNm?: string;
|
||||
atrzPrgrsSttsComCd?: string;
|
||||
atrzPrgrsSttsComNm?: string;
|
||||
rjctRsn?: string | null;
|
||||
atrzDt?: string | null;
|
||||
jbpsSeNm?: string;
|
||||
jbgdSeNm?: string;
|
||||
ognzNm?: string;
|
||||
}
|
||||
|
||||
// det 응답에 전자결재 진행상태 목록을 포함
|
||||
export interface InstlSncnMngDetResponse {
|
||||
detDto: InstlSncnMng;
|
||||
bizSclList: any[];
|
||||
bizCstList: any[];
|
||||
cklstResultList?: any[];
|
||||
esntlFileList: any[];
|
||||
etcFileList: any[];
|
||||
/** 설치인가관리 상세 응답 DTO */
|
||||
export interface InstlSncnMngDetResponse
|
||||
extends PbadmsMngDetResponseBase<InstlSncnMngResponseDto, InstlSncnRvwOpnnResponseDto> {
|
||||
detDto: InstlSncnMngResponseDto;
|
||||
bizSclList: BizSclTreeDto[];
|
||||
bizCstList: BizCstTreeDto[];
|
||||
esntlFileList: EsntlAtchFileMngTreeResponseDto[];
|
||||
etcFileList: AtflMngClientResponseDto[];
|
||||
elaprDocPrgrsList?: ElaprDocPrgrsClientResponseDto[] | null;
|
||||
}
|
||||
|
||||
export interface InstlSncnMngService {
|
||||
getPage: (params: InstlSncnMngSearchParams) => Promise<SpringPage<InstlSncnMng>>;
|
||||
getPage: (params: InstlSncnMngSearchParams) => Promise<PageResponse<InstlSncnMngResponseDto>>;
|
||||
getById: (id: string) => Promise<InstlSncnMngDetResponse>;
|
||||
save: (payload: InstlSncnMngSaveRequestDto) => Promise<string>;
|
||||
saveApply: (payload: InstlSncnApplySaveRequestDto) => Promise<string>;
|
||||
update: (id: string, payload: InstlSncnMngUpdateRequestDto) => Promise<void>;
|
||||
techReview: (id: string, payload: InstlSncnApplySaveRequestDto) => Promise<void>;
|
||||
remove: (id: string) => Promise<void>;
|
||||
changePrgrsSttsComCd: (payload: DocPrgrsUpdateRequestDto) => Promise<void>;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
RvwMnpwMngPageSearchDto,
|
||||
RvwMnpwMngSaveRequestDto,
|
||||
RvwMnpwMngSearchDto,
|
||||
RvwMnpwMngUpdateRequestDto,
|
||||
@@ -10,9 +11,12 @@ import type {
|
||||
|
||||
import { rvwMnpwMngService } from "./rvw-mnpw-service";
|
||||
|
||||
const rvwMnpwMngBaseKey = [ENDPOINT.AS_COMMON_SERVICE.RVW_MNPW] as const;
|
||||
const rvwMnpwMngBaseKey = [ENDPOINT.AS_SERVICE.RVW_MNPW] as const;
|
||||
|
||||
export function useRvwMnpwMngPage(params: RvwMnpwMngSearchDto, options?: { enabled?: boolean }) {
|
||||
export function useRvwMnpwMngPage(
|
||||
params: RvwMnpwMngPageSearchDto,
|
||||
options?: { enabled?: boolean },
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: [...rvwMnpwMngBaseKey, "findPage", params],
|
||||
queryFn: () => rvwMnpwMngService.findPage(params),
|
||||
@@ -20,6 +24,17 @@ export function useRvwMnpwMngPage(params: RvwMnpwMngSearchDto, options?: { enabl
|
||||
});
|
||||
}
|
||||
|
||||
export function useRvwMnpwMngLoginUserList(
|
||||
searchDto: RvwMnpwMngSearchDto,
|
||||
options?: { enabled?: boolean },
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: [...rvwMnpwMngBaseKey, "findRvwLoginUserList", searchDto],
|
||||
queryFn: () => rvwMnpwMngService.findRvwLoginUserList(searchDto),
|
||||
enabled: options?.enabled,
|
||||
});
|
||||
}
|
||||
|
||||
export function useRvwMnpwMng(
|
||||
atrzSeComCd: string | null | undefined,
|
||||
ognzSn: number | null | undefined,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
RvwMnpwMngPageSearchDto,
|
||||
RvwMnpwMngResponseDto,
|
||||
RvwMnpwMngSaveRequestDto,
|
||||
RvwMnpwMngSearchDto,
|
||||
@@ -10,14 +11,21 @@ import type {
|
||||
RvwMnpwMngUpdateRequestDto,
|
||||
} from "../model/rvw-mnpw-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_COMMON_SERVICE.RVW_MNPW;
|
||||
const BASE = ENDPOINT.AS_SERVICE.RVW_MNPW;
|
||||
|
||||
function pkPath(atrzSeComCd: string, ognzSn: number, userUuid: string): string {
|
||||
return `${BASE}/${atrzSeComCd}/${ognzSn}/${userUuid}`;
|
||||
async function findPage(
|
||||
params: RvwMnpwMngPageSearchDto,
|
||||
): Promise<PageResponse<RvwMnpwMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<RvwMnpwMngResponseDto>>(BASE, { params });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function findPage(params: RvwMnpwMngSearchDto): Promise<PageResponse<RvwMnpwMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<RvwMnpwMngResponseDto>>(BASE, { params });
|
||||
async function findRvwLoginUserList(
|
||||
searchDto: RvwMnpwMngSearchDto,
|
||||
): Promise<PageResponse<RvwMnpwMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<RvwMnpwMngResponseDto>>(`${BASE}/loginuser/me`, {
|
||||
params: searchDto,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -26,41 +34,48 @@ async function findById(
|
||||
ognzSn: number,
|
||||
userUuid: string,
|
||||
): Promise<RvwMnpwMngResponseDto> {
|
||||
const response = await client.get<RvwMnpwMngResponseDto>(pkPath(atrzSeComCd, ognzSn, userUuid));
|
||||
const response = await client.get<RvwMnpwMngResponseDto>(
|
||||
`${BASE}/${atrzSeComCd}/${ognzSn}/${userUuid}`,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function save(body: RvwMnpwMngSaveRequestDto): Promise<RvwMnpwMngResponseDto> {
|
||||
const response = await client.post<RvwMnpwMngResponseDto>(BASE, body);
|
||||
async function saveRvwMnpwMng(body: RvwMnpwMngSaveRequestDto): Promise<RvwMnpwMngResponseDto> {
|
||||
const { atrzSeComCd, ognzSn, userUuid } = body;
|
||||
const response = await client.post<RvwMnpwMngResponseDto>(
|
||||
`${BASE}/${atrzSeComCd}/${ognzSn}/${userUuid}`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function update(
|
||||
async function updateRvwMnpwMng(
|
||||
atrzSeComCd: string,
|
||||
ognzSn: number,
|
||||
userUuid: string,
|
||||
body: RvwMnpwMngUpdateRequestDto,
|
||||
): Promise<RvwMnpwMngResponseDto> {
|
||||
const response = await client.put<RvwMnpwMngResponseDto>(
|
||||
pkPath(atrzSeComCd, ognzSn, userUuid),
|
||||
`${BASE}/${atrzSeComCd}/${ognzSn}/${userUuid}`,
|
||||
body,
|
||||
);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
async function deleteRvwMnpw(
|
||||
async function deleteRvwMnpwMng(
|
||||
atrzSeComCd: string,
|
||||
ognzSn: number,
|
||||
userUuid: string,
|
||||
): Promise<string> {
|
||||
const response = await client.delete<string>(pkPath(atrzSeComCd, ognzSn, userUuid));
|
||||
const response = await client.delete<string>(`${BASE}/${atrzSeComCd}/${ognzSn}/${userUuid}`);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const rvwMnpwMngService = {
|
||||
findPage,
|
||||
findRvwLoginUserList,
|
||||
findById,
|
||||
save,
|
||||
update,
|
||||
delete: deleteRvwMnpw,
|
||||
save: saveRvwMnpwMng,
|
||||
update: updateRvwMnpwMng,
|
||||
delete: deleteRvwMnpwMng,
|
||||
} satisfies RvwMnpwMngService;
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
export {
|
||||
useDeleteRvwMnpwMng,
|
||||
useRvwMnpwMng,
|
||||
useRvwMnpwMngLoginUserList,
|
||||
useRvwMnpwMngPage,
|
||||
useSaveRvwMnpwMng,
|
||||
useUpdateRvwMnpwMng,
|
||||
} from "./api/queries";
|
||||
export { rvwMnpwMngService } from "./api/rvw-mnpw-service";
|
||||
export type {
|
||||
RvwMnpwMngPageSearchDto,
|
||||
RvwMnpwMngResponseDto,
|
||||
RvwMnpwMngSaveRequestDto,
|
||||
RvwMnpwMngSearchDto,
|
||||
|
||||
@@ -14,13 +14,16 @@ export interface RvwMnpwMngResponseDto {
|
||||
regDt?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 RvwMnpwMngSearchDto + Pageable */
|
||||
export interface RvwMnpwMngSearchDto extends PageableParams {
|
||||
/** 백엔드 RvwMnpwMngSearchDto */
|
||||
export interface RvwMnpwMngSearchDto {
|
||||
atrzSeComCd?: string;
|
||||
ognzSn?: number;
|
||||
userUuid?: string;
|
||||
}
|
||||
|
||||
/** findPage — RvwMnpwMngSearchDto + Pageable */
|
||||
export type RvwMnpwMngPageSearchDto = RvwMnpwMngSearchDto & PageableParams;
|
||||
|
||||
/** 백엔드 RvwMnpwMngSaveRequestDto */
|
||||
export interface RvwMnpwMngSaveRequestDto {
|
||||
atrzSeComCd: string;
|
||||
@@ -35,7 +38,10 @@ export interface RvwMnpwMngUpdateRequestDto {
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface RvwMnpwMngService {
|
||||
findPage(params: RvwMnpwMngSearchDto): Promise<PageResponse<RvwMnpwMngResponseDto>>;
|
||||
findPage(params: RvwMnpwMngPageSearchDto): Promise<PageResponse<RvwMnpwMngResponseDto>>;
|
||||
findRvwLoginUserList(
|
||||
searchDto: RvwMnpwMngSearchDto,
|
||||
): Promise<PageResponse<RvwMnpwMngResponseDto>>;
|
||||
findById(atrzSeComCd: string, ognzSn: number, userUuid: string): Promise<RvwMnpwMngResponseDto>;
|
||||
save(body: RvwMnpwMngSaveRequestDto): Promise<RvwMnpwMngResponseDto>;
|
||||
update(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export { handleTusUpload, type TusUploadOptions } from "./lib/utils";
|
||||
@@ -0,0 +1,61 @@
|
||||
import { Upload } from "tus-js-client";
|
||||
|
||||
import { ENDPOINT, PROXY_HOST } from "@/shared/config";
|
||||
import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config/env";
|
||||
import { getCookie } from "@/shared/lib";
|
||||
|
||||
interface TusUploadOptions {
|
||||
metadata?: Record<string, string>;
|
||||
handleUploadStart?: (upload: Upload) => void;
|
||||
handleProgress?: (percentage: number) => void;
|
||||
handleSuccess?: (upload: Upload, atflCd: string, atflId: string) => void;
|
||||
handleError?: (error: Error) => void;
|
||||
}
|
||||
|
||||
function handleTusUpload(file: File, options?: TusUploadOptions) {
|
||||
let atflCd = "";
|
||||
let atflId = "";
|
||||
|
||||
const metadata = {
|
||||
filename: file.name,
|
||||
filetype: file.type,
|
||||
...options?.metadata,
|
||||
};
|
||||
|
||||
const upload = new Upload(file, {
|
||||
endpoint: `${PROXY_HOST}${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`,
|
||||
chunkSize: 10 * 1024 * 1024,
|
||||
headers: {
|
||||
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
|
||||
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
|
||||
},
|
||||
metadata,
|
||||
onError(error) {
|
||||
console.error("TUS upload failed:", error);
|
||||
options?.handleError?.(error);
|
||||
},
|
||||
onProgress(bytesUploaded, bytesTotal) {
|
||||
const percentage = (bytesUploaded / bytesTotal) * 100;
|
||||
options?.handleProgress?.(percentage);
|
||||
},
|
||||
onAfterResponse(_req, res) {
|
||||
const fileInfo = res.getHeader("X-File-Info");
|
||||
if (!fileInfo) return;
|
||||
|
||||
const parsed = JSON.parse(fileInfo) as { atflCd?: string; atflId?: string };
|
||||
atflCd = parsed.atflCd ?? "";
|
||||
atflId = parsed.atflId ?? "";
|
||||
},
|
||||
onSuccess() {
|
||||
options?.handleSuccess?.(upload, atflCd, atflId);
|
||||
},
|
||||
});
|
||||
|
||||
upload.start();
|
||||
options?.handleUploadStart?.(upload);
|
||||
|
||||
return upload;
|
||||
}
|
||||
|
||||
export { handleTusUpload };
|
||||
export type { TusUploadOptions };
|
||||
@@ -1 +0,0 @@
|
||||
export { AcntgCodeManagementPage } from "./ui/acntg-code-management-page";
|
||||
@@ -1,245 +0,0 @@
|
||||
import type { AcntgSeMngResponseDto } from "@/entities/acntg";
|
||||
|
||||
import type {
|
||||
AcntgDisplaySlot,
|
||||
AcntgSeBaseEntry,
|
||||
AcntgSeGridState,
|
||||
} from "../model/acntg-se-grid-types";
|
||||
import type { AcntgSeRow } from "../model/acntg-se-row-types";
|
||||
import { buildAcntgDisplayRows } from "../model/build-acntg-display-rows";
|
||||
|
||||
export function createLoadedRowKey(index: number, acntgSeCd: string): string {
|
||||
return `loaded-${index}-${acntgSeCd}`;
|
||||
}
|
||||
|
||||
export function createDraftRowKey(): string {
|
||||
return `draft-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
export function createEmptyGridState(): AcntgSeGridState {
|
||||
return {
|
||||
baseByKey: {},
|
||||
structure: [],
|
||||
drafts: {},
|
||||
patches: {},
|
||||
deletedKeys: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
function toInitialRow(entry: AcntgSeBaseEntry): AcntgSeRow {
|
||||
return {
|
||||
...entry,
|
||||
isNew: false,
|
||||
isDirty: false,
|
||||
isDeleted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildStructureFromBaseRows(
|
||||
baseByKey: Record<string, AcntgSeBaseEntry>,
|
||||
): AcntgDisplaySlot[] {
|
||||
const rows = Object.values(baseByKey).map(toInitialRow);
|
||||
const display = buildAcntgDisplayRows(rows);
|
||||
const cdToRowKey = new Map(rows.map((row) => [row.acntgSeCd, row.rowKey]));
|
||||
|
||||
return display.map(({ row, depth }) => ({
|
||||
rowKey: row.rowKey,
|
||||
depth,
|
||||
parentRowKey: row.upAcntgSeCd?.trim() ? (cdToRowKey.get(row.upAcntgSeCd.trim()) ?? null) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
export function hydrateGridFromServer(content: AcntgSeMngResponseDto[]): AcntgSeGridState {
|
||||
const baseByKey: Record<string, AcntgSeBaseEntry> = {};
|
||||
|
||||
content.forEach((item, index) => {
|
||||
const rowKey = createLoadedRowKey(index, item.acntgSeCd);
|
||||
baseByKey[rowKey] = { ...item, rowKey };
|
||||
});
|
||||
|
||||
return {
|
||||
baseByKey,
|
||||
structure: buildStructureFromBaseRows(baseByKey),
|
||||
drafts: {},
|
||||
patches: {},
|
||||
deletedKeys: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveParentAcntgSeCd(
|
||||
state: AcntgSeGridState,
|
||||
parentRowKey: string | null,
|
||||
): string | undefined {
|
||||
if (!parentRowKey) return undefined;
|
||||
const parent = mergeAcntgRow(state, parentRowKey);
|
||||
const cd = parent?.acntgSeCd?.trim();
|
||||
return cd || undefined;
|
||||
}
|
||||
|
||||
export function mergeAcntgRow(state: AcntgSeGridState, rowKey: string): AcntgSeRow | null {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (draft) {
|
||||
return {
|
||||
rowKey,
|
||||
acntgSeCd: draft.acntgSeCd,
|
||||
acntgSeCdNm: draft.acntgSeCdNm,
|
||||
expln: draft.expln,
|
||||
useYn: draft.useYn,
|
||||
cdLvNo: draft.cdLvNo,
|
||||
upAcntgSeCd: resolveParentAcntgSeCd(state, draft.parentRowKey),
|
||||
isNew: true,
|
||||
isDirty: true,
|
||||
isDeleted: false,
|
||||
};
|
||||
}
|
||||
|
||||
const base = state.baseByKey[rowKey];
|
||||
if (!base) return null;
|
||||
|
||||
const patch = state.patches[rowKey] ?? {};
|
||||
const hasPatch = Object.keys(patch).length > 0;
|
||||
|
||||
return {
|
||||
...base,
|
||||
...patch,
|
||||
rowKey,
|
||||
isNew: false,
|
||||
isDirty: hasPatch,
|
||||
isDeleted: state.deletedKeys.has(rowKey),
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeAllAcntgRows(state: AcntgSeGridState): AcntgSeRow[] {
|
||||
const keys = new Set([...Object.keys(state.baseByKey), ...Object.keys(state.drafts)]);
|
||||
|
||||
return [...keys]
|
||||
.map((rowKey) => mergeAcntgRow(state, rowKey))
|
||||
.filter((row): row is AcntgSeRow => row != null);
|
||||
}
|
||||
|
||||
export function hasAcntgGridPendingChanges(state: AcntgSeGridState): boolean {
|
||||
return (
|
||||
state.deletedKeys.size > 0 ||
|
||||
Object.keys(state.drafts).length > 0 ||
|
||||
Object.keys(state.patches).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
export function collectDescendantRowKeys(
|
||||
structure: AcntgDisplaySlot[],
|
||||
rootRowKey: string,
|
||||
): Set<string> {
|
||||
const result = new Set<string>([rootRowKey]);
|
||||
const queue = [rootRowKey];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const parentKey = queue.shift()!;
|
||||
for (const slot of structure) {
|
||||
if (slot.parentRowKey === parentKey && !result.has(slot.rowKey)) {
|
||||
result.add(slot.rowKey);
|
||||
queue.push(slot.rowKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function insertRootSlot(
|
||||
structure: AcntgDisplaySlot[],
|
||||
slot: AcntgDisplaySlot,
|
||||
): AcntgDisplaySlot[] {
|
||||
const firstRootIndex = structure.findIndex((item) => item.parentRowKey === null);
|
||||
if (firstRootIndex === -1) return [slot, ...structure];
|
||||
return [...structure.slice(0, firstRootIndex), slot, ...structure.slice(firstRootIndex)];
|
||||
}
|
||||
|
||||
export function insertChildSlot(
|
||||
structure: AcntgDisplaySlot[],
|
||||
parentRowKey: string,
|
||||
slot: AcntgDisplaySlot,
|
||||
): AcntgDisplaySlot[] {
|
||||
const parentIndex = structure.findIndex((item) => item.rowKey === parentRowKey);
|
||||
if (parentIndex === -1) return [...structure, slot];
|
||||
|
||||
const insertAt = parentIndex + 1;
|
||||
return [...structure.slice(0, insertAt), slot, ...structure.slice(insertAt)];
|
||||
}
|
||||
|
||||
export function removeSlotsFromStructure(
|
||||
structure: AcntgDisplaySlot[],
|
||||
rowKeysToRemove: Set<string>,
|
||||
): AcntgDisplaySlot[] {
|
||||
return structure.filter((slot) => !rowKeysToRemove.has(slot.rowKey));
|
||||
}
|
||||
|
||||
export function patchAcntgGridRow(
|
||||
state: AcntgSeGridState,
|
||||
rowKey: string,
|
||||
patch: Partial<AcntgSeRow>,
|
||||
): AcntgSeGridState {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (draft) {
|
||||
return {
|
||||
...state,
|
||||
drafts: {
|
||||
...state.drafts,
|
||||
[rowKey]: {
|
||||
...draft,
|
||||
...(patch.acntgSeCd !== undefined ? { acntgSeCd: patch.acntgSeCd } : {}),
|
||||
...(patch.acntgSeCdNm !== undefined ? { acntgSeCdNm: patch.acntgSeCdNm ?? "" } : {}),
|
||||
...(patch.expln !== undefined ? { expln: patch.expln ?? "" } : {}),
|
||||
...(patch.useYn !== undefined ? { useYn: patch.useYn } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const { rowKey: _rk, isNew: _n, isDirty: _d, isDeleted: _del, ...dtoPatch } = patch;
|
||||
|
||||
return {
|
||||
...state,
|
||||
patches: {
|
||||
...state.patches,
|
||||
[rowKey]: { ...state.patches[rowKey], ...dtoPatch },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function markAcntgRowsDeleted(
|
||||
state: AcntgSeGridState,
|
||||
rootRowKey: string,
|
||||
): AcntgSeGridState {
|
||||
const keysToDelete = collectDescendantRowKeys(state.structure, rootRowKey);
|
||||
const deletedKeys = new Set(state.deletedKeys);
|
||||
for (const key of keysToDelete) {
|
||||
deletedKeys.add(key);
|
||||
}
|
||||
return { ...state, deletedKeys };
|
||||
}
|
||||
|
||||
export function validateDraftParentAcntgSeCd(
|
||||
state: AcntgSeGridState,
|
||||
rowKey: string,
|
||||
): string | null {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (!draft?.parentRowKey) return null;
|
||||
|
||||
const parent = mergeAcntgRow(state, draft.parentRowKey);
|
||||
if (!parent?.acntgSeCd?.trim()) {
|
||||
return "하위 항목을 추가하려면 상위 코드를 먼저 입력하세요.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function removeDraftFromGrid(state: AcntgSeGridState, rowKey: string): AcntgSeGridState {
|
||||
const { [rowKey]: _removed, ...restDrafts } = state.drafts;
|
||||
const keysToRemove = collectDescendantRowKeys(state.structure, rowKey);
|
||||
|
||||
return {
|
||||
...state,
|
||||
drafts: restDrafts,
|
||||
structure: removeSlotsFromStructure(state.structure, keysToRemove),
|
||||
};
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { ACNTG_SE_SAVE_MODE, type AcntgSeMngSaveRequestDto } from "@/entities/acntg";
|
||||
|
||||
import type { AcntgSeRow } from "../model/acntg-se-row-types";
|
||||
|
||||
import { getVisibleRows } from "./row-state";
|
||||
|
||||
function compareCdLvNoDesc(a: AcntgSeRow, b: AcntgSeRow): number {
|
||||
return (b.cdLvNo ?? 1) - (a.cdLvNo ?? 1);
|
||||
}
|
||||
|
||||
function compareCdLvNoAsc(a: AcntgSeRow, b: AcntgSeRow): number {
|
||||
return (a.cdLvNo ?? 1) - (b.cdLvNo ?? 1);
|
||||
}
|
||||
|
||||
export function buildSaveMultiPayload(rows: AcntgSeRow[]): AcntgSeMngSaveRequestDto[] {
|
||||
const payload: AcntgSeMngSaveRequestDto[] = [];
|
||||
|
||||
const deletedRows = rows.filter((row) => row.isDeleted && !row.isNew).sort(compareCdLvNoDesc);
|
||||
|
||||
for (const row of deletedRows) {
|
||||
payload.push({
|
||||
mode: ACNTG_SE_SAVE_MODE.DELETE,
|
||||
acntgSeCd: row.acntgSeCd,
|
||||
acntgSeCdNm: row.acntgSeCdNm?.trim() || "",
|
||||
upAcntgSeCd: row.upAcntgSeCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const visibleRows = getVisibleRows(rows).sort(compareCdLvNoAsc);
|
||||
|
||||
for (const row of visibleRows) {
|
||||
if (row.isNew) {
|
||||
payload.push({
|
||||
mode: ACNTG_SE_SAVE_MODE.CREATE,
|
||||
acntgSeCd: row.acntgSeCd.trim(),
|
||||
acntgSeCdNm: row.acntgSeCdNm.trim(),
|
||||
upAcntgSeCd: row.upAcntgSeCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row.isDirty) {
|
||||
payload.push({
|
||||
mode: ACNTG_SE_SAVE_MODE.UPDATE,
|
||||
acntgSeCd: row.acntgSeCd.trim(),
|
||||
acntgSeCdNm: row.acntgSeCdNm.trim(),
|
||||
upAcntgSeCd: row.upAcntgSeCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { AcntgSeRow } from "../model/acntg-se-row-types";
|
||||
|
||||
export function getVisibleRows(rows: AcntgSeRow[]): AcntgSeRow[] {
|
||||
return rows.filter((row) => !row.isDeleted);
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import type { AcntgSeMngResponseDto } from "@/entities/acntg";
|
||||
|
||||
import type { AcntgSeGridState } from "../model/acntg-se-grid-types";
|
||||
import type { AcntgSeRow } from "../model/acntg-se-row-types";
|
||||
|
||||
import {
|
||||
createDraftRowKey,
|
||||
createEmptyGridState,
|
||||
hasAcntgGridPendingChanges,
|
||||
hydrateGridFromServer,
|
||||
insertChildSlot,
|
||||
insertRootSlot,
|
||||
markAcntgRowsDeleted,
|
||||
mergeAcntgRow,
|
||||
mergeAllAcntgRows,
|
||||
patchAcntgGridRow,
|
||||
removeDraftFromGrid,
|
||||
validateDraftParentAcntgSeCd,
|
||||
} from "./acntg-se-grid";
|
||||
import { buildSaveMultiPayload } from "./build-save-multi-payload";
|
||||
|
||||
function validateAcntgSeRow(row: AcntgSeRow): string | null {
|
||||
if (!row.acntgSeCd.trim()) return "코드를 입력하세요.";
|
||||
if (!row.acntgSeCdNm?.trim()) return "코드명을 입력하세요.";
|
||||
const parentCd = row.upAcntgSeCd?.trim();
|
||||
if (parentCd && row.acntgSeCd.trim() === parentCd) {
|
||||
return "상위 코드와 동일한 코드는 사용할 수 없습니다.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface UseAcntgSeRowsOptions {
|
||||
serverContent: AcntgSeMngResponseDto[] | undefined;
|
||||
onAlert: (message: string) => void;
|
||||
onSave: (payload: ReturnType<typeof buildSaveMultiPayload>) => Promise<void>;
|
||||
}
|
||||
|
||||
export function useAcntgSeRows({ serverContent, onAlert, onSave }: UseAcntgSeRowsOptions) {
|
||||
const [grid, setGrid] = useState<AcntgSeGridState>(createEmptyGridState);
|
||||
const hasPendingRef = useRef(false);
|
||||
|
||||
hasPendingRef.current = hasAcntgGridPendingChanges(grid);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverContent == null) return;
|
||||
if (hasPendingRef.current) return;
|
||||
setGrid(hydrateGridFromServer(serverContent));
|
||||
}, [serverContent]);
|
||||
|
||||
const displayRows = useMemo(() => {
|
||||
return grid.structure
|
||||
.filter((slot) => !grid.deletedKeys.has(slot.rowKey))
|
||||
.map((slot) => {
|
||||
const row = mergeAcntgRow(grid, slot.rowKey);
|
||||
return row ? { row, depth: slot.depth } : null;
|
||||
})
|
||||
.filter((item): item is { row: AcntgSeRow; depth: number } => item != null);
|
||||
}, [grid]);
|
||||
|
||||
const canSave = hasAcntgGridPendingChanges(grid);
|
||||
|
||||
const updateRow = useCallback((rowKey: string, patch: Partial<AcntgSeRow>) => {
|
||||
setGrid((prev) => patchAcntgGridRow(prev, rowKey, patch));
|
||||
}, []);
|
||||
|
||||
const addRootRow = useCallback(() => {
|
||||
const rowKey = createDraftRowKey();
|
||||
setGrid((prev) => ({
|
||||
...prev,
|
||||
drafts: {
|
||||
...prev.drafts,
|
||||
[rowKey]: {
|
||||
rowKey,
|
||||
parentRowKey: null,
|
||||
cdLvNo: 1,
|
||||
acntgSeCd: "",
|
||||
acntgSeCdNm: "",
|
||||
expln: "",
|
||||
useYn: "Y",
|
||||
},
|
||||
},
|
||||
structure: insertRootSlot(prev.structure, { rowKey, depth: 0, parentRowKey: null }),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const addChildRow = useCallback((parent: AcntgSeRow) => {
|
||||
const rowKey = createDraftRowKey();
|
||||
const slot = {
|
||||
rowKey,
|
||||
depth: 0,
|
||||
parentRowKey: parent.rowKey,
|
||||
};
|
||||
|
||||
setGrid((prev) => {
|
||||
const parentSlot = prev.structure.find((item) => item.rowKey === parent.rowKey);
|
||||
const depth = (parentSlot?.depth ?? 0) + 1;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
drafts: {
|
||||
...prev.drafts,
|
||||
[rowKey]: {
|
||||
rowKey,
|
||||
parentRowKey: parent.rowKey,
|
||||
cdLvNo: (parent.cdLvNo ?? 1) + 1,
|
||||
acntgSeCd: "",
|
||||
acntgSeCdNm: "",
|
||||
expln: "",
|
||||
useYn: "Y",
|
||||
},
|
||||
},
|
||||
structure: insertChildSlot(prev.structure, parent.rowKey, {
|
||||
...slot,
|
||||
depth,
|
||||
}),
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const removeDraft = useCallback((rowKey: string) => {
|
||||
setGrid((prev) => removeDraftFromGrid(prev, rowKey));
|
||||
}, []);
|
||||
|
||||
const markDeleted = useCallback((rowKey: string) => {
|
||||
setGrid((prev) => markAcntgRowsDeleted(prev, rowKey));
|
||||
}, []);
|
||||
|
||||
const resetFromServer = useCallback((content: AcntgSeMngResponseDto[]) => {
|
||||
setGrid(hydrateGridFromServer(content));
|
||||
}, []);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
const mergedRows = mergeAllAcntgRows(grid);
|
||||
const payload = buildSaveMultiPayload(mergedRows);
|
||||
if (payload.length === 0) return false;
|
||||
|
||||
for (const row of mergedRows) {
|
||||
if (!row.isDeleted && (row.isNew || row.isDirty)) {
|
||||
const parentCodeError = validateDraftParentAcntgSeCd(grid, row.rowKey);
|
||||
if (parentCodeError) {
|
||||
onAlert(parentCodeError);
|
||||
return false;
|
||||
}
|
||||
|
||||
const fieldError = validateAcntgSeRow(row);
|
||||
if (fieldError) {
|
||||
onAlert(fieldError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await onSave(payload);
|
||||
return true;
|
||||
}, [grid, onAlert, onSave]);
|
||||
|
||||
return {
|
||||
displayRows,
|
||||
canSave,
|
||||
updateRow,
|
||||
addRootRow,
|
||||
addChildRow,
|
||||
removeDraft,
|
||||
markDeleted,
|
||||
resetFromServer,
|
||||
save,
|
||||
};
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useAcntgSeMngPage, useSaveMultiAcntgSeMng } from "@/entities/acntg";
|
||||
import {
|
||||
Button,
|
||||
Skeleton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
import { useAcntgSeRows } from "../lib/use-acntg-se-rows";
|
||||
import type { AcntgSeRow } from "../model/acntg-se-row-types";
|
||||
|
||||
import { AcntgSeRowComponent } from "./acntg-se-row";
|
||||
|
||||
function AcntgCodeManagementPage() {
|
||||
const { confirm } = useConfirm();
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const pageQuery = useAcntgSeMngPage({
|
||||
page: 0,
|
||||
size: 500,
|
||||
sort: "cdLvNo,asc",
|
||||
});
|
||||
|
||||
const saveMultiMutation = useSaveMultiAcntgSeMng();
|
||||
|
||||
const handlePersist = useCallback(
|
||||
async (payload: Parameters<typeof saveMultiMutation.mutateAsync>[0]) => {
|
||||
await saveMultiMutation.mutateAsync(payload);
|
||||
},
|
||||
[saveMultiMutation],
|
||||
);
|
||||
|
||||
const {
|
||||
displayRows,
|
||||
canSave,
|
||||
updateRow,
|
||||
addRootRow,
|
||||
addChildRow,
|
||||
removeDraft,
|
||||
markDeleted,
|
||||
resetFromServer,
|
||||
save,
|
||||
} = useAcntgSeRows({
|
||||
serverContent: pageQuery.data?.content,
|
||||
onAlert: alert,
|
||||
onSave: handlePersist,
|
||||
});
|
||||
|
||||
const handleDelete = async (row: AcntgSeRow) => {
|
||||
if (!(await confirm("삭제하시겠습니까?"))) return;
|
||||
|
||||
if (row.isNew) {
|
||||
removeDraft(row.rowKey);
|
||||
return;
|
||||
}
|
||||
|
||||
markDeleted(row.rowKey);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!(await confirm("저장하시겠습니까?"))) return;
|
||||
|
||||
const saved = await save();
|
||||
if (!saved) return;
|
||||
|
||||
alert("저장되었습니다.");
|
||||
const result = await pageQuery.refetch();
|
||||
resetFromServer(result.data?.content ?? []);
|
||||
};
|
||||
|
||||
const isSaving = saveMultiMutation.isPending;
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-[1.8rem] pb-6">
|
||||
<section className="card card-border mx-0 bg-background pb-6 pt-0">
|
||||
<div className="card-header flex flex-wrap items-center justify-between gap-3">
|
||||
<h3 className="text-2xl font-semibold">회계코드 관리</h3>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Button type="button" color="green" onClick={addRootRow}>
|
||||
코드 추가
|
||||
</Button>
|
||||
<Button type="button" disabled={!canSave || isSaving} onClick={handleSave}>
|
||||
저장
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pageQuery.isPending ? (
|
||||
<div className="flex flex-col gap-2 px-4 pb-4">
|
||||
<Skeleton className="h-[4rem]" />
|
||||
<Skeleton className="h-[16rem]" />
|
||||
</div>
|
||||
) : displayRows.length === 0 ? (
|
||||
<p className="py-8 text-center text-lg text-muted-foreground">
|
||||
등록된 코드가 없습니다. [코드 추가]로 1단계 코드를 등록하세요.
|
||||
</p>
|
||||
) : (
|
||||
<div className="max-h-[60rem] overflow-y-auto pt-3">
|
||||
<Table className="w-full">
|
||||
<colgroup>
|
||||
<col width="15%" />
|
||||
<col width="20%" />
|
||||
<col width="35%" />
|
||||
<col width="12%" />
|
||||
<col width="8%" />
|
||||
<col width="10%" />
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>코드</TableHead>
|
||||
<TableHead>코드명</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{displayRows.map(({ row, depth }) => (
|
||||
<AcntgSeRowComponent
|
||||
key={row.rowKey}
|
||||
row={row}
|
||||
depth={depth}
|
||||
onChange={(patch) => updateRow(row.rowKey, patch)}
|
||||
onAddChild={() => addChildRow(row)}
|
||||
onDelete={() => handleDelete(row)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { AcntgCodeManagementPage };
|
||||
@@ -1,6 +0,0 @@
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
|
||||
/** 그리드 행 순서(1-based)로 결재순서번호 일괄 채번 */
|
||||
export function assignAtrzSeqNumbers(rows: ElaprAplnRow[]): ElaprAplnRow[] {
|
||||
return rows.map((row, index) => ({ ...row, atrzSeqNo: index + 1 }));
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { ElaprApln } from "@/entities/elapr";
|
||||
|
||||
import { assignAtrzSeqNumbers } from "./assign-atrz-seq";
|
||||
import { getVisibleRows } from "./validators";
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
|
||||
function rowDiffersFromServer(row: ElaprAplnRow, serverRow: ElaprApln): boolean {
|
||||
return (
|
||||
row.aprvrUuid !== serverRow.aprvrUuid ||
|
||||
row.atrzSeComCd !== serverRow.atrzSeComCd ||
|
||||
row.atrzSeqNo !== serverRow.atrzSeqNo
|
||||
);
|
||||
}
|
||||
|
||||
export function hasUnsavedChanges(rows: ElaprAplnRow[], serverRows: ElaprApln[]): boolean {
|
||||
if (rows.some((row) => row.isDeleted && row.aplnId)) return true;
|
||||
if (rows.some((row) => row.isNew && !row.isDeleted)) return true;
|
||||
|
||||
const visibleWithSeq = assignAtrzSeqNumbers(getVisibleRows(rows));
|
||||
|
||||
for (const row of visibleWithSeq) {
|
||||
if (row.isNew) continue;
|
||||
if (!row.aplnId) continue;
|
||||
if (row.isDirty) return true;
|
||||
|
||||
const serverRow = serverRows.find((s) => s.aplnId === row.aplnId);
|
||||
if (!serverRow) continue;
|
||||
if (rowDiffersFromServer(row, serverRow)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -7,7 +7,19 @@ import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useElaprAplnPage, useSaveMultiElaprApln } from "@/entities/elapr";
|
||||
import {
|
||||
buildSaveMultiPayload,
|
||||
type ElaprAplnRow,
|
||||
hasUnsavedChanges,
|
||||
useAplnRows,
|
||||
useDndSensors,
|
||||
useElaprAplnPage,
|
||||
useSaveMultiElaprApln,
|
||||
validateAllRowsForSave,
|
||||
validateAplnRow,
|
||||
validateAtrzOrder,
|
||||
validateUniqueSeqNo,
|
||||
} from "@/entities/elapr";
|
||||
import {
|
||||
Button,
|
||||
Skeleton,
|
||||
@@ -18,17 +30,6 @@ import {
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
import { buildSaveMultiPayload } from "../lib/build-save-multi-payload";
|
||||
import { hasUnsavedChanges } from "../lib/has-unsaved-changes";
|
||||
import { useAplnRows } from "../lib/use-apln-rows";
|
||||
import { useDndSensors } from "../lib/use-dnd-sensors";
|
||||
import {
|
||||
validateAllRowsForSave,
|
||||
validateAplnRow,
|
||||
validateAtrzOrder,
|
||||
validateUniqueSeqNo,
|
||||
} from "../lib/validators";
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
import type { ReviewerOption } from "../model/reviewer-option";
|
||||
|
||||
import { AprvrPickerDialog } from "./aprvr-picker-dialog";
|
||||
|
||||
@@ -92,7 +92,7 @@ function ElaprTmpltFormDialog({
|
||||
<div className="space-y-2">
|
||||
<Label>문서구분</Label>
|
||||
<CodeSelect
|
||||
upCd="ASDOCSECD"
|
||||
upCd="ASDOCSE"
|
||||
value={docSeComCd}
|
||||
onValueChange={(v) => setValue("docSeComCd", v)}
|
||||
placeholder="문서구분 선택"
|
||||
|
||||
@@ -5,10 +5,9 @@ import { CSS } from "@dnd-kit/utilities";
|
||||
import { Equal } from "lucide-react";
|
||||
|
||||
import { CodeSelect, findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import type { ElaprAplnRow } from "@/entities/elapr";
|
||||
import { Button, TableCell, TableRow } from "@common/components";
|
||||
|
||||
import type { ElaprAplnRow } from "../model/apln-row-types";
|
||||
|
||||
interface SortableAplnRowProps {
|
||||
row: ElaprAplnRow;
|
||||
displaySeqNo: number;
|
||||
|
||||
@@ -102,7 +102,7 @@ function CklstMngFormDialog({
|
||||
<div className="space-y-2">
|
||||
<Label>문서구분</Label>
|
||||
<CodeSelect
|
||||
upCd="ASDOCSECD"
|
||||
upCd="ASDOCSE"
|
||||
value={docSeComCd}
|
||||
onValueChange={(v) => setValue("docSeComCd", v)}
|
||||
placeholder="문서구분 선택"
|
||||
@@ -112,7 +112,7 @@ function CklstMngFormDialog({
|
||||
<div className="space-y-2">
|
||||
<Label>업무구분</Label>
|
||||
<CodeSelect
|
||||
upCd={docSeComCd || "ASDOCSECD"}
|
||||
upCd={docSeComCd || "ASDOCSE"}
|
||||
value={watch("taskSeComCd")}
|
||||
onValueChange={(v) => setValue("taskSeComCd", v)}
|
||||
placeholder="업무구분 선택"
|
||||
|
||||
@@ -67,6 +67,7 @@ function CklstPreviewDialog({
|
||||
<TableBody>
|
||||
{displayRows.map(({ row, depth }) => {
|
||||
const branchPrefix = formatTreeBranchPrefix(depth);
|
||||
const showRadios = row.bchckUseYn !== "N";
|
||||
|
||||
if (row.useYn === "N") return null;
|
||||
|
||||
@@ -82,16 +83,20 @@ function CklstPreviewDialog({
|
||||
</TableCell>
|
||||
<TableCell>{row.rvwCn ?? "—"}</TableCell>
|
||||
<TableCell align="center">
|
||||
<RadioGroup className="flex items-center justify-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<RadioGroupItem value="Y" id={`${row.rowKey}-use-y`} />
|
||||
<Label htmlFor={`${row.rowKey}-use-y`}>예</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<RadioGroupItem value="N" id={`${row.rowKey}-use-n`} />
|
||||
<Label htmlFor={`${row.rowKey}-use-n`}>아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
{showRadios ? (
|
||||
<RadioGroup className="flex items-center justify-center">
|
||||
<div className="flex items-center gap-1">
|
||||
<RadioGroupItem value="Y" id={`${row.rowKey}-preview-y`} />
|
||||
<Label htmlFor={`${row.rowKey}-preview-y`}>예</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<RadioGroupItem value="N" id={`${row.rowKey}-preview-n`} />
|
||||
<Label htmlFor={`${row.rowKey}-preview-n`}>아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
) : (
|
||||
"—"
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -113,6 +113,7 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
rvwCnLvNo: 1,
|
||||
rvwCn: "",
|
||||
useYn: "Y",
|
||||
bchckUseYn: "Y",
|
||||
sortSeq: maxSort + 1,
|
||||
isNew: true,
|
||||
isDirty: true,
|
||||
@@ -145,6 +146,7 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
rvwCnLvNo: parentLv + 1,
|
||||
rvwCn: "",
|
||||
useYn: "Y",
|
||||
bchckUseYn: "Y",
|
||||
sortSeq: maxSort + 1,
|
||||
isNew: true,
|
||||
isDirty: true,
|
||||
@@ -189,6 +191,8 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
const handleSave = async () => {
|
||||
if (!cklstSn) return;
|
||||
|
||||
if (!(await confirm("체크리스트 항목을 저장하시겠습니까?"))) return;
|
||||
|
||||
const dirty = getDirtyRows().sort((a, b) => (a.rvwCnLvNo ?? 1) - (b.rvwCnLvNo ?? 1));
|
||||
|
||||
for (const row of dirty) {
|
||||
@@ -198,8 +202,6 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(await confirm("체크리스트 항목을 저장하시겠습니까?"))) return;
|
||||
|
||||
if (row.isNew) {
|
||||
await createMutation.mutateAsync({
|
||||
cklstSn: row.cklstSn,
|
||||
@@ -209,7 +211,7 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
rvwCn: row.rvwCn,
|
||||
useYn: row.useYn,
|
||||
sortSeq: row.sortSeq,
|
||||
bchckUseYn: "Y",
|
||||
bchckUseYn: row.bchckUseYn ?? "Y",
|
||||
});
|
||||
} else if (row.cklstRvwSn != null) {
|
||||
await updateMutation.mutateAsync({
|
||||
@@ -277,17 +279,19 @@ function CklstRvwGrid({ selectedCklst }: CklstRvwGridProps) {
|
||||
) : (
|
||||
<Table className="w-full">
|
||||
<colgroup>
|
||||
<col width="20%" />
|
||||
<col width="50%" />
|
||||
<col width="15%" />
|
||||
<col width="18%" />
|
||||
<col width="42%" />
|
||||
<col width="12%" />
|
||||
<col width="12%" />
|
||||
<col width="5%" />
|
||||
<col width="10%" />
|
||||
<col width="11%" />
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>점검항목</TableHead>
|
||||
<TableHead>주요 검토내용</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>체크 사용</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
</TableRow>
|
||||
|
||||
@@ -91,6 +91,22 @@ function CklstRvwRowComponent({
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<RadioGroup
|
||||
value={row.bchckUseYn ?? "Y"}
|
||||
onValueChange={(v) => onChange({ bchckUseYn: v as "Y" | "N", isDirty: true })}
|
||||
className="flex justify-center gap-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="Y" id={`${row.rowKey}-bchck-y`} />
|
||||
<Label htmlFor={`${row.rowKey}-bchck-y`}>예</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<RadioGroupItem value="N" id={`${row.rowKey}-bchck-n`} />
|
||||
<Label htmlFor={`${row.rowKey}-bchck-n`}>아니오</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</TableCell>
|
||||
<TableCell align="center">{levelLabel}</TableCell>
|
||||
<TableCell align="center">
|
||||
<div className="flex justify-center gap-2">
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { CnstpCodeManagementPage } from "./ui/cnstp-code-management-page";
|
||||
@@ -1,62 +0,0 @@
|
||||
import { CNSTP_SE_SAVE_MODE, type CnstpSeMngSaveRequestDto } from "@/entities/cnstp";
|
||||
|
||||
import type { CnstpSeRow } from "../model/cnstp-se-row-types";
|
||||
|
||||
import { getVisibleRows } from "./row-state";
|
||||
|
||||
function compareCdLvNoDesc(a: CnstpSeRow, b: CnstpSeRow): number {
|
||||
return (b.cdLvNo ?? 1) - (a.cdLvNo ?? 1);
|
||||
}
|
||||
|
||||
function compareCdLvNoAsc(a: CnstpSeRow, b: CnstpSeRow): number {
|
||||
return (a.cdLvNo ?? 1) - (b.cdLvNo ?? 1);
|
||||
}
|
||||
|
||||
export function buildSaveMultiPayload(rows: CnstpSeRow[]): CnstpSeMngSaveRequestDto[] {
|
||||
const payload: CnstpSeMngSaveRequestDto[] = [];
|
||||
|
||||
const deletedRows = rows.filter((row) => row.isDeleted && !row.isNew).sort(compareCdLvNoDesc);
|
||||
|
||||
for (const row of deletedRows) {
|
||||
payload.push({
|
||||
mode: CNSTP_SE_SAVE_MODE.DELETE,
|
||||
cnstpSeCd: row.cnstpSeCd,
|
||||
cnstpSeCdNm: row.cnstpSeCdNm?.trim() || undefined,
|
||||
upCnstpCd: row.upCnstpCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
const visibleRows = getVisibleRows(rows).sort(compareCdLvNoAsc);
|
||||
|
||||
for (const row of visibleRows) {
|
||||
if (row.isNew) {
|
||||
payload.push({
|
||||
mode: CNSTP_SE_SAVE_MODE.CREATE,
|
||||
cnstpSeCd: row.cnstpSeCd.trim(),
|
||||
cnstpSeCdNm: row.cnstpSeCdNm?.trim() || undefined,
|
||||
upCnstpCd: row.upCnstpCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row.isDirty) {
|
||||
payload.push({
|
||||
mode: CNSTP_SE_SAVE_MODE.UPDATE,
|
||||
cnstpSeCd: row.cnstpSeCd.trim(),
|
||||
cnstpSeCdNm: row.cnstpSeCdNm?.trim() || undefined,
|
||||
upCnstpCd: row.upCnstpCd,
|
||||
cdLvNo: row.cdLvNo,
|
||||
useYn: row.useYn,
|
||||
expln: row.expln?.trim() || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
import type { CnstpSeMngResponseDto } from "@/entities/cnstp";
|
||||
|
||||
import { buildCnstpDisplayRows } from "../model/build-cnstp-display-rows";
|
||||
import type {
|
||||
CnstpDisplaySlot,
|
||||
CnstpSeBaseEntry,
|
||||
CnstpSeGridState,
|
||||
} from "../model/cnstp-se-grid-types";
|
||||
import type { CnstpSeRow } from "../model/cnstp-se-row-types";
|
||||
|
||||
export function createLoadedRowKey(index: number, cnstpSeCd: string): string {
|
||||
return `loaded-${index}-${cnstpSeCd}`;
|
||||
}
|
||||
|
||||
export function createDraftRowKey(): string {
|
||||
return `draft-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
export function createEmptyGridState(): CnstpSeGridState {
|
||||
return {
|
||||
baseByKey: {},
|
||||
structure: [],
|
||||
drafts: {},
|
||||
patches: {},
|
||||
deletedKeys: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
function toInitialRow(entry: CnstpSeBaseEntry): CnstpSeRow {
|
||||
return {
|
||||
...entry,
|
||||
isNew: false,
|
||||
isDirty: false,
|
||||
isDeleted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildStructureFromBaseRows(
|
||||
baseByKey: Record<string, CnstpSeBaseEntry>,
|
||||
): CnstpDisplaySlot[] {
|
||||
const rows = Object.values(baseByKey).map(toInitialRow);
|
||||
const display = buildCnstpDisplayRows(rows);
|
||||
const cdToRowKey = new Map(rows.map((row) => [row.cnstpSeCd, row.rowKey]));
|
||||
|
||||
return display.map(({ row, depth }) => ({
|
||||
rowKey: row.rowKey,
|
||||
depth,
|
||||
parentRowKey: row.upCnstpCd?.trim() ? (cdToRowKey.get(row.upCnstpCd.trim()) ?? null) : null,
|
||||
}));
|
||||
}
|
||||
|
||||
export function hydrateGridFromServer(content: CnstpSeMngResponseDto[]): CnstpSeGridState {
|
||||
const baseByKey: Record<string, CnstpSeBaseEntry> = {};
|
||||
|
||||
content.forEach((item, index) => {
|
||||
const rowKey = createLoadedRowKey(index, item.cnstpSeCd);
|
||||
baseByKey[rowKey] = { ...item, rowKey };
|
||||
});
|
||||
|
||||
return {
|
||||
baseByKey,
|
||||
structure: buildStructureFromBaseRows(baseByKey),
|
||||
drafts: {},
|
||||
patches: {},
|
||||
deletedKeys: new Set(),
|
||||
};
|
||||
}
|
||||
|
||||
export function resolveParentCnstpSeCd(
|
||||
state: CnstpSeGridState,
|
||||
parentRowKey: string | null,
|
||||
): string | undefined {
|
||||
if (!parentRowKey) return undefined;
|
||||
const parent = mergeCnstpRow(state, parentRowKey);
|
||||
const cd = parent?.cnstpSeCd?.trim();
|
||||
return cd || undefined;
|
||||
}
|
||||
|
||||
export function mergeCnstpRow(state: CnstpSeGridState, rowKey: string): CnstpSeRow | null {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (draft) {
|
||||
return {
|
||||
rowKey,
|
||||
cnstpSeCd: draft.cnstpSeCd,
|
||||
cnstpSeCdNm: draft.cnstpSeCdNm,
|
||||
expln: draft.expln,
|
||||
useYn: draft.useYn,
|
||||
cdLvNo: draft.cdLvNo,
|
||||
upCnstpCd: resolveParentCnstpSeCd(state, draft.parentRowKey),
|
||||
isNew: true,
|
||||
isDirty: true,
|
||||
isDeleted: false,
|
||||
};
|
||||
}
|
||||
|
||||
const base = state.baseByKey[rowKey];
|
||||
if (!base) return null;
|
||||
|
||||
const patch = state.patches[rowKey] ?? {};
|
||||
const hasPatch = Object.keys(patch).length > 0;
|
||||
|
||||
return {
|
||||
...base,
|
||||
...patch,
|
||||
rowKey,
|
||||
isNew: false,
|
||||
isDirty: hasPatch,
|
||||
isDeleted: state.deletedKeys.has(rowKey),
|
||||
};
|
||||
}
|
||||
|
||||
export function mergeAllCnstpRows(state: CnstpSeGridState): CnstpSeRow[] {
|
||||
const keys = new Set([...Object.keys(state.baseByKey), ...Object.keys(state.drafts)]);
|
||||
|
||||
return [...keys]
|
||||
.map((rowKey) => mergeCnstpRow(state, rowKey))
|
||||
.filter((row): row is CnstpSeRow => row != null);
|
||||
}
|
||||
|
||||
export function hasCnstpGridPendingChanges(state: CnstpSeGridState): boolean {
|
||||
return (
|
||||
state.deletedKeys.size > 0 ||
|
||||
Object.keys(state.drafts).length > 0 ||
|
||||
Object.keys(state.patches).length > 0
|
||||
);
|
||||
}
|
||||
|
||||
export function collectDescendantRowKeys(
|
||||
structure: CnstpDisplaySlot[],
|
||||
rootRowKey: string,
|
||||
): Set<string> {
|
||||
const result = new Set<string>([rootRowKey]);
|
||||
const queue = [rootRowKey];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const parentKey = queue.shift()!;
|
||||
for (const slot of structure) {
|
||||
if (slot.parentRowKey === parentKey && !result.has(slot.rowKey)) {
|
||||
result.add(slot.rowKey);
|
||||
queue.push(slot.rowKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function insertRootSlot(
|
||||
structure: CnstpDisplaySlot[],
|
||||
slot: CnstpDisplaySlot,
|
||||
): CnstpDisplaySlot[] {
|
||||
const firstRootIndex = structure.findIndex((item) => item.parentRowKey === null);
|
||||
if (firstRootIndex === -1) return [slot, ...structure];
|
||||
return [...structure.slice(0, firstRootIndex), slot, ...structure.slice(firstRootIndex)];
|
||||
}
|
||||
|
||||
export function insertChildSlot(
|
||||
structure: CnstpDisplaySlot[],
|
||||
parentRowKey: string,
|
||||
slot: CnstpDisplaySlot,
|
||||
): CnstpDisplaySlot[] {
|
||||
const parentIndex = structure.findIndex((item) => item.rowKey === parentRowKey);
|
||||
if (parentIndex === -1) return [...structure, slot];
|
||||
|
||||
const insertAt = parentIndex + 1;
|
||||
return [...structure.slice(0, insertAt), slot, ...structure.slice(insertAt)];
|
||||
}
|
||||
|
||||
export function removeSlotsFromStructure(
|
||||
structure: CnstpDisplaySlot[],
|
||||
rowKeysToRemove: Set<string>,
|
||||
): CnstpDisplaySlot[] {
|
||||
return structure.filter((slot) => !rowKeysToRemove.has(slot.rowKey));
|
||||
}
|
||||
|
||||
export function patchCnstpGridRow(
|
||||
state: CnstpSeGridState,
|
||||
rowKey: string,
|
||||
patch: Partial<CnstpSeRow>,
|
||||
): CnstpSeGridState {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (draft) {
|
||||
return {
|
||||
...state,
|
||||
drafts: {
|
||||
...state.drafts,
|
||||
[rowKey]: {
|
||||
...draft,
|
||||
...(patch.cnstpSeCd !== undefined ? { cnstpSeCd: patch.cnstpSeCd } : {}),
|
||||
...(patch.cnstpSeCdNm !== undefined ? { cnstpSeCdNm: patch.cnstpSeCdNm ?? "" } : {}),
|
||||
...(patch.expln !== undefined ? { expln: patch.expln ?? "" } : {}),
|
||||
...(patch.useYn !== undefined ? { useYn: patch.useYn } : {}),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const { rowKey: _rk, isNew: _n, isDirty: _d, isDeleted: _del, ...dtoPatch } = patch;
|
||||
|
||||
return {
|
||||
...state,
|
||||
patches: {
|
||||
...state.patches,
|
||||
[rowKey]: { ...state.patches[rowKey], ...dtoPatch },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function markCnstpRowsDeleted(
|
||||
state: CnstpSeGridState,
|
||||
rootRowKey: string,
|
||||
): CnstpSeGridState {
|
||||
const keysToDelete = collectDescendantRowKeys(state.structure, rootRowKey);
|
||||
const deletedKeys = new Set(state.deletedKeys);
|
||||
for (const key of keysToDelete) {
|
||||
deletedKeys.add(key);
|
||||
}
|
||||
return { ...state, deletedKeys };
|
||||
}
|
||||
|
||||
export function validateDraftParentCnstpSeCd(
|
||||
state: CnstpSeGridState,
|
||||
rowKey: string,
|
||||
): string | null {
|
||||
const draft = state.drafts[rowKey];
|
||||
if (!draft?.parentRowKey) return null;
|
||||
|
||||
const parent = mergeCnstpRow(state, draft.parentRowKey);
|
||||
if (!parent?.cnstpSeCd?.trim()) {
|
||||
return "하위 항목을 추가하려면 상위 코드를 먼저 입력하세요.";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function removeDraftFromGrid(state: CnstpSeGridState, rowKey: string): CnstpSeGridState {
|
||||
const { [rowKey]: _removed, ...restDrafts } = state.drafts;
|
||||
const keysToRemove = collectDescendantRowKeys(state.structure, rowKey);
|
||||
|
||||
return {
|
||||
...state,
|
||||
drafts: restDrafts,
|
||||
structure: removeSlotsFromStructure(state.structure, keysToRemove),
|
||||
};
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import type { CnstpSeRow } from "../model/cnstp-se-row-types";
|
||||
|
||||
export function getVisibleRows(rows: CnstpSeRow[]): CnstpSeRow[] {
|
||||
return rows.filter((row) => !row.isDeleted);
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import type { CnstpSeMngResponseDto } from "@/entities/cnstp";
|
||||
|
||||
import type { CnstpSeGridState } from "../model/cnstp-se-grid-types";
|
||||
import type { CnstpSeRow } from "../model/cnstp-se-row-types";
|
||||
|
||||
import { buildSaveMultiPayload } from "./build-save-multi-payload";
|
||||
import {
|
||||
createDraftRowKey,
|
||||
createEmptyGridState,
|
||||
hasCnstpGridPendingChanges,
|
||||
hydrateGridFromServer,
|
||||
insertChildSlot,
|
||||
insertRootSlot,
|
||||
markCnstpRowsDeleted,
|
||||
mergeAllCnstpRows,
|
||||
mergeCnstpRow,
|
||||
patchCnstpGridRow,
|
||||
removeDraftFromGrid,
|
||||
validateDraftParentCnstpSeCd,
|
||||
} from "./cnstp-se-grid";
|
||||
|
||||
function validateCnstpSeRow(row: CnstpSeRow): string | null {
|
||||
if (!row.cnstpSeCd.trim()) return "코드를 입력하세요.";
|
||||
const parentCd = row.upCnstpCd?.trim();
|
||||
if (parentCd && row.cnstpSeCd.trim() === parentCd) {
|
||||
return "상위 코드와 동일한 코드는 사용할 수 없습니다.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface UseCnstpSeRowsOptions {
|
||||
serverContent: CnstpSeMngResponseDto[] | undefined;
|
||||
onAlert: (message: string) => void;
|
||||
onSave: (payload: ReturnType<typeof buildSaveMultiPayload>) => Promise<void>;
|
||||
}
|
||||
|
||||
export function useCnstpSeRows({ serverContent, onAlert, onSave }: UseCnstpSeRowsOptions) {
|
||||
const [grid, setGrid] = useState<CnstpSeGridState>(createEmptyGridState);
|
||||
const hasPendingRef = useRef(false);
|
||||
|
||||
hasPendingRef.current = hasCnstpGridPendingChanges(grid);
|
||||
|
||||
useEffect(() => {
|
||||
if (serverContent == null) return;
|
||||
if (hasPendingRef.current) return;
|
||||
setGrid(hydrateGridFromServer(serverContent));
|
||||
}, [serverContent]);
|
||||
|
||||
const displayRows = useMemo(() => {
|
||||
return grid.structure
|
||||
.filter((slot) => !grid.deletedKeys.has(slot.rowKey))
|
||||
.map((slot) => {
|
||||
const row = mergeCnstpRow(grid, slot.rowKey);
|
||||
return row ? { row, depth: slot.depth } : null;
|
||||
})
|
||||
.filter((item): item is { row: CnstpSeRow; depth: number } => item != null);
|
||||
}, [grid]);
|
||||
|
||||
const canSave = hasCnstpGridPendingChanges(grid);
|
||||
|
||||
const updateRow = useCallback((rowKey: string, patch: Partial<CnstpSeRow>) => {
|
||||
setGrid((prev) => patchCnstpGridRow(prev, rowKey, patch));
|
||||
}, []);
|
||||
|
||||
const addRootRow = useCallback(() => {
|
||||
const rowKey = createDraftRowKey();
|
||||
setGrid((prev) => ({
|
||||
...prev,
|
||||
drafts: {
|
||||
...prev.drafts,
|
||||
[rowKey]: {
|
||||
rowKey,
|
||||
parentRowKey: null,
|
||||
cdLvNo: 1,
|
||||
cnstpSeCd: "",
|
||||
cnstpSeCdNm: "",
|
||||
expln: "",
|
||||
useYn: "Y",
|
||||
},
|
||||
},
|
||||
structure: insertRootSlot(prev.structure, { rowKey, depth: 0, parentRowKey: null }),
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const addChildRow = useCallback((parent: CnstpSeRow) => {
|
||||
const rowKey = createDraftRowKey();
|
||||
|
||||
setGrid((prev) => {
|
||||
const parentSlot = prev.structure.find((item) => item.rowKey === parent.rowKey);
|
||||
const depth = (parentSlot?.depth ?? 0) + 1;
|
||||
|
||||
return {
|
||||
...prev,
|
||||
drafts: {
|
||||
...prev.drafts,
|
||||
[rowKey]: {
|
||||
rowKey,
|
||||
parentRowKey: parent.rowKey,
|
||||
cdLvNo: (parent.cdLvNo ?? 1) + 1,
|
||||
cnstpSeCd: "",
|
||||
cnstpSeCdNm: "",
|
||||
expln: "",
|
||||
useYn: "Y",
|
||||
},
|
||||
},
|
||||
structure: insertChildSlot(prev.structure, parent.rowKey, {
|
||||
rowKey,
|
||||
depth,
|
||||
parentRowKey: parent.rowKey,
|
||||
}),
|
||||
};
|
||||
});
|
||||
}, []);
|
||||
|
||||
const removeDraft = useCallback((rowKey: string) => {
|
||||
setGrid((prev) => removeDraftFromGrid(prev, rowKey));
|
||||
}, []);
|
||||
|
||||
const markDeleted = useCallback((rowKey: string) => {
|
||||
setGrid((prev) => markCnstpRowsDeleted(prev, rowKey));
|
||||
}, []);
|
||||
|
||||
const resetFromServer = useCallback((content: CnstpSeMngResponseDto[]) => {
|
||||
setGrid(hydrateGridFromServer(content));
|
||||
}, []);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
const mergedRows = mergeAllCnstpRows(grid);
|
||||
const payload = buildSaveMultiPayload(mergedRows);
|
||||
if (payload.length === 0) return false;
|
||||
|
||||
for (const row of mergedRows) {
|
||||
if (!row.isDeleted && (row.isNew || row.isDirty)) {
|
||||
const parentCodeError = validateDraftParentCnstpSeCd(grid, row.rowKey);
|
||||
if (parentCodeError) {
|
||||
onAlert(parentCodeError);
|
||||
return false;
|
||||
}
|
||||
|
||||
const fieldError = validateCnstpSeRow(row);
|
||||
if (fieldError) {
|
||||
onAlert(fieldError);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await onSave(payload);
|
||||
return true;
|
||||
}, [grid, onAlert, onSave]);
|
||||
|
||||
return {
|
||||
displayRows,
|
||||
canSave,
|
||||
updateRow,
|
||||
addRootRow,
|
||||
addChildRow,
|
||||
removeDraft,
|
||||
markDeleted,
|
||||
resetFromServer,
|
||||
save,
|
||||
};
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useCnstpSeMngPage, useSaveMultiCnstpSeMng } from "@/entities/cnstp";
|
||||
import {
|
||||
Button,
|
||||
Skeleton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
import { useCnstpSeRows } from "../lib/use-cnstp-se-rows";
|
||||
import type { CnstpSeRow } from "../model/cnstp-se-row-types";
|
||||
|
||||
import { CnstpSeRowComponent } from "./cnstp-se-row";
|
||||
|
||||
function CnstpCodeManagementPage() {
|
||||
const { confirm } = useConfirm();
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const pageQuery = useCnstpSeMngPage({
|
||||
page: 0,
|
||||
size: 500,
|
||||
sort: "cdLvNo,asc",
|
||||
});
|
||||
|
||||
const saveMultiMutation = useSaveMultiCnstpSeMng();
|
||||
|
||||
const handlePersist = useCallback(
|
||||
async (payload: Parameters<typeof saveMultiMutation.mutateAsync>[0]) => {
|
||||
await saveMultiMutation.mutateAsync(payload);
|
||||
},
|
||||
[saveMultiMutation],
|
||||
);
|
||||
|
||||
const {
|
||||
displayRows,
|
||||
canSave,
|
||||
updateRow,
|
||||
addRootRow,
|
||||
addChildRow,
|
||||
removeDraft,
|
||||
markDeleted,
|
||||
resetFromServer,
|
||||
save,
|
||||
} = useCnstpSeRows({
|
||||
serverContent: pageQuery.data?.content,
|
||||
onAlert: alert,
|
||||
onSave: handlePersist,
|
||||
});
|
||||
|
||||
const handleDelete = async (row: CnstpSeRow) => {
|
||||
if (!(await confirm("삭제하시겠습니까?"))) return;
|
||||
|
||||
if (row.isNew) {
|
||||
removeDraft(row.rowKey);
|
||||
return;
|
||||
}
|
||||
|
||||
markDeleted(row.rowKey);
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!(await confirm("저장하시겠습니까?"))) return;
|
||||
|
||||
const saved = await save();
|
||||
if (!saved) return;
|
||||
|
||||
alert("저장되었습니다.");
|
||||
const result = await pageQuery.refetch();
|
||||
resetFromServer(result.data?.content ?? []);
|
||||
};
|
||||
|
||||
const isSaving = saveMultiMutation.isPending;
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-[1.8rem] pb-6">
|
||||
<section className="card card-border mx-0 bg-background pb-6 pt-0">
|
||||
<div className="card-header flex flex-wrap items-center justify-between gap-3">
|
||||
<h3 className="text-2xl font-semibold">공종코드 관리</h3>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Button type="button" color="green" onClick={addRootRow}>
|
||||
코드 추가
|
||||
</Button>
|
||||
<Button type="button" disabled={!canSave || isSaving} onClick={handleSave}>
|
||||
저장
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{pageQuery.isPending ? (
|
||||
<div className="flex flex-col gap-2 px-4 pb-4">
|
||||
<Skeleton className="h-[4rem]" />
|
||||
<Skeleton className="h-[16rem]" />
|
||||
</div>
|
||||
) : displayRows.length === 0 ? (
|
||||
<p className="py-8 text-center text-lg text-muted-foreground">
|
||||
등록된 코드가 없습니다. [코드 추가]로 1단계 코드를 등록하세요.
|
||||
</p>
|
||||
) : (
|
||||
<div className="max-h-[60rem] overflow-y-auto pt-3">
|
||||
<Table className="w-full">
|
||||
<colgroup>
|
||||
<col width="15%" />
|
||||
<col width="20%" />
|
||||
<col width="35%" />
|
||||
<col width="12%" />
|
||||
<col width="8%" />
|
||||
<col width="10%" />
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>코드</TableHead>
|
||||
<TableHead>코드명</TableHead>
|
||||
<TableHead>설명</TableHead>
|
||||
<TableHead>사용 여부</TableHead>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{displayRows.map(({ row, depth }) => (
|
||||
<CnstpSeRowComponent
|
||||
key={row.rowKey}
|
||||
row={row}
|
||||
depth={depth}
|
||||
onChange={(patch) => updateRow(row.rowKey, patch)}
|
||||
onAddChild={() => addChildRow(row)}
|
||||
onDelete={() => handleDelete(row)}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { CnstpCodeManagementPage };
|
||||
@@ -0,0 +1 @@
|
||||
export { CodeManagementPage } from "./ui/code-management-page";
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user