[as] feat: 웹기안기 다이얼로그 내 금액검증결과 섹션 구현
This commit is contained in:
@@ -6,36 +6,57 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Hwp SDK Wasm Sample</title>
|
||||
<script type="text/javascript" data-type="css/app" data-framework="aori|lab" src="js/loadVersion.js"></script>
|
||||
<script type="text/javascript" data-type="css/app" data-framework="aori|lab" src="js/loadHead.js"></script>
|
||||
<script data-main="js/main" data-bundle="dist/main_bundle" src="js/loadMain.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="status" style="display:none;">
|
||||
<div class="sppiner-content">
|
||||
<div class="spinner" id='spinner'></div>
|
||||
<div class="emscripten" id="statustext"></div>
|
||||
</div>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Hwp SDK Wasm Sample</title>
|
||||
<script
|
||||
type="text/javascript"
|
||||
data-type="css/app"
|
||||
data-framework="aori|lab"
|
||||
src="js/loadVersion.js"
|
||||
></script>
|
||||
<script
|
||||
type="text/javascript"
|
||||
data-type="css/app"
|
||||
data-framework="aori|lab"
|
||||
src="js/loadHead.js"
|
||||
></script>
|
||||
<script data-main="js/main" data-bundle="dist/main_bundle" src="js/loadMain.js"></script>
|
||||
<style>
|
||||
#hcwoViewScroll::-webkit-scrollbar {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
}
|
||||
#hcwoViewScroll::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
border-radius: 5px;
|
||||
}
|
||||
#hcwoViewScroll::-webkit-scrollbar-track {
|
||||
background: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="status" style="display: none">
|
||||
<div class="sppiner-content">
|
||||
<div class="spinner" id="spinner"></div>
|
||||
<div class="emscripten" id="statustext"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="status_downloading">
|
||||
<div class="status-content">
|
||||
<div class="emscriptenDownloading">
|
||||
<span id="statusDownloadingText">구성 중 ... 0% </span>
|
||||
<span id="statusDownloadingPercent" class="emscriptenDownloadingPercent"></span>
|
||||
</div>
|
||||
<div class="emscriptenDownloadingInfo"></div>
|
||||
<progress id="download_progress" value="0" max="100"></progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="status_downloading">
|
||||
<div class="status-content">
|
||||
<div class="emscriptenDownloading">
|
||||
<span id="statusDownloadingText">구성 중 ... 0% </span>
|
||||
<span id="statusDownloadingPercent" class="emscriptenDownloadingPercent"></span>
|
||||
</div>
|
||||
<div class="emscriptenDownloadingInfo"></div>
|
||||
<progress id="download_progress" value="0" max="100"></progress>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<iframe id="download_iframe" style="width:0px;height:0px;border:0;display:none;"></iframe>
|
||||
<script type="text/javascript" src="hwpsdk/loadHwpsdk.js"></script>
|
||||
</body>
|
||||
<iframe id="download_iframe" style="width: 0px; height: 0px; border: 0; display: none"></iframe>
|
||||
<script type="text/javascript" src="hwpsdk/loadHwpsdk.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import type { PageResponse } from "@/shared/model";
|
||||
|
||||
import type {
|
||||
PwrkCnsltnDocPjtcoAjmtMngResponseDto,
|
||||
PwrkCnsltnDocPjtcoAjmtMngSearchDto,
|
||||
PwrkCnsltnDocPjtcoAjmtMngService,
|
||||
} from "../model/pwrk-cnsltn-doc-pjtco-ajmt-mng-interface";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.PWRK_CNSLTN_DOC_PJTCO_AJMT_MNG;
|
||||
|
||||
async function findPage(
|
||||
searchDto: PwrkCnsltnDocPjtcoAjmtMngSearchDto,
|
||||
): Promise<PageResponse<PwrkCnsltnDocPjtcoAjmtMngResponseDto>> {
|
||||
const response = await client.get<PageResponse<PwrkCnsltnDocPjtcoAjmtMngResponseDto>>(BASE, {
|
||||
params: searchDto,
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const pwrkCnsltnDocPjtcoAjmtMngService = {
|
||||
findPage,
|
||||
} satisfies PwrkCnsltnDocPjtcoAjmtMngService;
|
||||
@@ -0,0 +1,22 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import type { PwrkCnsltnDocPjtcoAjmtMngSearchDto } from "../model/pwrk-cnsltn-doc-pjtco-ajmt-mng-interface";
|
||||
|
||||
import { pwrkCnsltnDocPjtcoAjmtMngService } from "./pwrk-cnsltn-doc-pjtco-ajmt-mng-service";
|
||||
|
||||
export const pwrkCnsltnDocPjtcoAjmtMngKeys = {
|
||||
all: ["pwrk-cnsltn-doc-pjtco-ajmt-mng"] as const,
|
||||
page: (params: PwrkCnsltnDocPjtcoAjmtMngSearchDto) =>
|
||||
[...pwrkCnsltnDocPjtcoAjmtMngKeys.all, "findPage", params] as const,
|
||||
};
|
||||
|
||||
export function usePwrkCnsltnDocPjtcoAjmtMngPage(
|
||||
params: PwrkCnsltnDocPjtcoAjmtMngSearchDto,
|
||||
isEnabled = true,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: pwrkCnsltnDocPjtcoAjmtMngKeys.page(params),
|
||||
queryFn: () => pwrkCnsltnDocPjtcoAjmtMngService.findPage(params),
|
||||
enabled: Boolean(params.pwrkCnsltnDocNo) && isEnabled,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { pwrkCnsltnDocPjtcoAjmtMngService } from "./api/pwrk-cnsltn-doc-pjtco-ajmt-mng-service";
|
||||
export {
|
||||
pwrkCnsltnDocPjtcoAjmtMngKeys,
|
||||
usePwrkCnsltnDocPjtcoAjmtMngPage,
|
||||
} from "./api/queries";
|
||||
export type {
|
||||
PwrkCnsltnDocPjtcoAjmtMngResponseDto,
|
||||
PwrkCnsltnDocPjtcoAjmtMngSearchDto,
|
||||
PwrkCnsltnDocPjtcoAjmtMngService,
|
||||
} from "./model/pwrk-cnsltn-doc-pjtco-ajmt-mng-interface";
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import type { PageableParams, PageResponse } from "@/shared/model";
|
||||
|
||||
/** 백엔드 PwrkCnsltnDocPjtcoAjmtMngResponseDto */
|
||||
export interface PwrkCnsltnDocPjtcoAjmtMngResponseDto {
|
||||
pwrkCnsltnDocNo: string;
|
||||
rvwOpnnSn: number;
|
||||
acntgSeCd: string;
|
||||
acntgSeCdNm?: string;
|
||||
rvwOpnnClsfComCd?: string;
|
||||
rvwOpnnClsfComCdNm?: string;
|
||||
ognTpcoAmt?: number;
|
||||
ognGvexpAmt?: number;
|
||||
ognLcexpAmt?: number;
|
||||
ognOrgprBrdnAmt?: number;
|
||||
chgTpcoAmt?: number;
|
||||
chgGvexpAmt?: number;
|
||||
chgLcexpAmt?: number;
|
||||
chgOrgprBrdnAmt?: number;
|
||||
ajmtRsn?: string;
|
||||
vrfcTpcoAmt?: number;
|
||||
vrfcGvexpAmt?: number;
|
||||
vrfcLcexpAmt?: number;
|
||||
vrfcOrgprBrdnAmt?: number;
|
||||
rsbnYn?: string;
|
||||
vrfcCn?: string;
|
||||
regDt?: string;
|
||||
rgtr?: string;
|
||||
rgtrNm?: string;
|
||||
mdfcnDt?: string;
|
||||
mdfr?: string;
|
||||
}
|
||||
|
||||
/** 백엔드 PwrkCnsltnDocPjtcoAjmtMngSearchDto + Pageable */
|
||||
export interface PwrkCnsltnDocPjtcoAjmtMngSearchDto extends PageableParams {
|
||||
pwrkCnsltnDocNo?: string;
|
||||
rvwOpnnSn?: number;
|
||||
acntgSeCd?: string;
|
||||
}
|
||||
|
||||
export interface PwrkCnsltnDocPjtcoAjmtMngService {
|
||||
findPage(
|
||||
searchDto: PwrkCnsltnDocPjtcoAjmtMngSearchDto,
|
||||
): Promise<PageResponse<PwrkCnsltnDocPjtcoAjmtMngResponseDto>>;
|
||||
}
|
||||
@@ -72,9 +72,9 @@ export const fnrsCnsltnRegistSchema = z.object({
|
||||
cklstSn: z.number(),
|
||||
cklstArtclSn: z.number(),
|
||||
cklstRvwSn: z.number(),
|
||||
rvwStpComCd: z.string(),
|
||||
rvwStpComCd: z.string().nullish(),
|
||||
selected: z.enum(["Y", "N"]).nullable(),
|
||||
picRvwCn: z.string().nullable().optional(),
|
||||
picRvwCn: z.string().nullish(),
|
||||
}),
|
||||
),
|
||||
esntlAtchFileList: z.array(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { PanelRightClose, PanelRightOpen } from "lucide-react";
|
||||
|
||||
import { getDefaultFileKey, useAlertStore, useTus } from "@/shared/lib";
|
||||
import { AttachmentFileField } from "@/shared/ui";
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
pwrkCnsltnDocCrtRsltSaveRequestSchema,
|
||||
pwrkCnsltnDocCrtRsltService,
|
||||
} from "@/entities/pwrk-cnsltn-doc-crt-rslt";
|
||||
import { usePwrkCnsltnDocPjtcoAjmtMngPage } from "@/entities/pwrk-cnsltn-doc-pjtco-ajmt-mng";
|
||||
import {
|
||||
findCompletedPwrkDraft,
|
||||
pwrkCnsltnMngKeys,
|
||||
@@ -27,6 +29,8 @@ import {
|
||||
Skeleton,
|
||||
} from "@common/components";
|
||||
|
||||
import { PwrkCnsltnDocPjtcoAjmtTable } from "./pwrk-cnsltn-doc-pjtco-ajmt-table";
|
||||
|
||||
const HangulGiangi = dynamic(
|
||||
() => import("@/widgets/hangulgiangi").then((module) => module.HangulGiangi),
|
||||
{
|
||||
@@ -69,6 +73,7 @@ function PwrkCnsltnDocMngDialog({
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const [atchFileGroupId, setAtchFileGroupId] = useState("");
|
||||
const [uploadedFiles, setUploadedFiles] = useState<UploadedAtchFile[]>([]);
|
||||
const [isPjtcoAjmtTableOpen, setIsPjtcoAjmtTableOpen] = useState(true);
|
||||
|
||||
const {
|
||||
data: pwrkCnsltnData,
|
||||
@@ -92,6 +97,20 @@ function PwrkCnsltnDocMngDialog({
|
||||
const documentFileName = `실무협의서_${pwrkCnsltnData?.pwrkCnsltnDocNo ?? crtDmndUuid}.hwpx`;
|
||||
const isLoading = isPwrkCnsltnLoading || isDocumentLoading;
|
||||
const isError = isPwrkCnsltnError || isDocumentError;
|
||||
const pjtcoAjmtSearchParams = useMemo(
|
||||
() => ({
|
||||
pwrkCnsltnDocNo: pwrkCnsltnData?.pwrkCnsltnDocNo,
|
||||
page: 0,
|
||||
size: 1000,
|
||||
sort: "rvwOpnnSn,asc",
|
||||
}),
|
||||
[pwrkCnsltnData?.pwrkCnsltnDocNo],
|
||||
);
|
||||
const {
|
||||
data: pjtcoAjmtPage,
|
||||
isLoading: isPjtcoAjmtLoading,
|
||||
isError: isPjtcoAjmtError,
|
||||
} = usePwrkCnsltnDocPjtcoAjmtMngPage(pjtcoAjmtSearchParams, open);
|
||||
|
||||
const { uploadFileAsync, isUploading, uploads } = useTus({
|
||||
onSuccess: (atflCd, atflId, _file, fileKey) => {
|
||||
@@ -258,17 +277,52 @@ function PwrkCnsltnDocMngDialog({
|
||||
생성 완료된 실무협의서 초안이 없습니다.
|
||||
</p>
|
||||
) : null}
|
||||
{completedDraft ? (
|
||||
documentBlob ? (
|
||||
<HangulGiangi
|
||||
documentBlob={documentBlob}
|
||||
documentKey={completedDraft.crtDmndUuid}
|
||||
documentFileName={documentFileName}
|
||||
canSave={canSave}
|
||||
onSaveDocument={handleSaveDocument}
|
||||
onDownload={handleDownload}
|
||||
/>
|
||||
) : null
|
||||
{completedDraft && documentBlob ? (
|
||||
<div className="flex min-h-[40rem] items-stretch gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<HangulGiangi
|
||||
documentBlob={documentBlob}
|
||||
documentKey={completedDraft.crtDmndUuid}
|
||||
documentFileName={documentFileName}
|
||||
canSave={canSave}
|
||||
onSaveDocument={handleSaveDocument}
|
||||
onDownload={handleDownload}
|
||||
/>
|
||||
</div>
|
||||
{isPjtcoAjmtTableOpen ? (
|
||||
<section className="flex h-[calc(64vh+4rem)] w-0 flex-[0.4] flex-col overflow-hidden rounded-md border border-border">
|
||||
<header className="flex items-center justify-between gap-2 border-b border-border px-3 py-2">
|
||||
<h3 className="text-xl font-semibold">금액검증결과</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-sm border border-border p-2 hover:opacity-90"
|
||||
onClick={() => setIsPjtcoAjmtTableOpen(false)}
|
||||
aria-label="접기"
|
||||
>
|
||||
<PanelRightClose className="size-6" />
|
||||
</button>
|
||||
</header>
|
||||
<div className="min-h-0 flex-1 overflow-y-auto">
|
||||
<PwrkCnsltnDocPjtcoAjmtTable
|
||||
rows={pjtcoAjmtPage?.content ?? []}
|
||||
isLoading={isPjtcoAjmtLoading}
|
||||
isError={isPjtcoAjmtError}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1 self-start rounded-sm border border-border px-1 py-2 hover:opacity-90"
|
||||
style={{ writingMode: "vertical-rl" }}
|
||||
onClick={() => setIsPjtcoAjmtTableOpen(true)}
|
||||
aria-label="펼치기"
|
||||
>
|
||||
<PanelRightOpen className="size-5" />
|
||||
<span className="text-xl tracking-wide">금액검증결과</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{completedDraft ? (
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
import type { PwrkCnsltnDocPjtcoAjmtMngResponseDto } from "@/entities/pwrk-cnsltn-doc-pjtco-ajmt-mng";
|
||||
import { fmtNumber } from "@/widgets/pbadms-sprt";
|
||||
import {
|
||||
Skeleton,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
interface PwrkCnsltnDocPjtcoAjmtTableProps {
|
||||
rows: PwrkCnsltnDocPjtcoAjmtMngResponseDto[];
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
}
|
||||
|
||||
function PwrkCnsltnDocPjtcoAjmtTable({
|
||||
rows,
|
||||
isLoading,
|
||||
isError,
|
||||
}: PwrkCnsltnDocPjtcoAjmtTableProps) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-2 p-3">
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Skeleton className="h-24 w-full" />
|
||||
<Skeleton className="h-24 w-full" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<p className="px-3 py-10 text-center text-muted-foreground">
|
||||
사업비조정 정보를 불러오지 못했습니다.
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table variant="secondary">
|
||||
<TableHeader className="sticky top-0">
|
||||
<TableRow>
|
||||
<TableHead>구분</TableHead>
|
||||
<TableHead>검증결과</TableHead>
|
||||
<TableHead className="text-right">검증금액</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{rows.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="py-10 text-center text-muted-foreground">
|
||||
조회된 사업비조정 항목이 없습니다.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
rows.map((row) => (
|
||||
<TableRow key={getPjtcoAjmtRowKey(row)}>
|
||||
<TableCell>{formatDivision(row)}</TableCell>
|
||||
<TableCell>
|
||||
<p className="whitespace-pre-wrap">{formatVrfcCn(row.vrfcCn)}</p>
|
||||
</TableCell>
|
||||
<TableCell className="text-right">{formatVrfcAmt(row.vrfcTpcoAmt)}</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
function getPjtcoAjmtRowKey(row: PwrkCnsltnDocPjtcoAjmtMngResponseDto) {
|
||||
return [row.pwrkCnsltnDocNo, row.rvwOpnnSn, row.acntgSeCd].join("::");
|
||||
}
|
||||
|
||||
function formatDivision(row: PwrkCnsltnDocPjtcoAjmtMngResponseDto) {
|
||||
const label = row.acntgSeCdNm?.trim() || row.acntgSeCd?.trim();
|
||||
return label || "-";
|
||||
}
|
||||
|
||||
function formatVrfcCn(value: string | undefined) {
|
||||
const text = value?.trim();
|
||||
return text || "-";
|
||||
}
|
||||
|
||||
function formatVrfcAmt(value: number | undefined) {
|
||||
if (value === undefined || value === null) return "-";
|
||||
const formatted = fmtNumber(value);
|
||||
return formatted || "-";
|
||||
}
|
||||
|
||||
export { PwrkCnsltnDocPjtcoAjmtTable };
|
||||
export type { PwrkCnsltnDocPjtcoAjmtTableProps };
|
||||
@@ -28,6 +28,7 @@ export const ENDPOINT = {
|
||||
DCRAG_TO: "/as-service/api/v1/dcrag-to",
|
||||
PWRK_CNSLTN_MNG: "/as-service/api/v1/pwrk-cnsltn-mng",
|
||||
PWRK_CNSLTN_DOC_CRT_RSLT: "/as-service/api/v1/pwrk-cnsltn-doc-crt-rslt",
|
||||
PWRK_CNSLTN_DOC_PJTCO_AJMT_MNG: "/as-service/api/v1/pwrk-cnsltn-doc-pjtco-ajmt-mng",
|
||||
},
|
||||
USER_SERVICE: {
|
||||
LOGIN: "/user-service/login",
|
||||
|
||||
@@ -191,7 +191,7 @@ export interface CklstResultSaveBase {
|
||||
cklstRvwSn?: number;
|
||||
cklstArtclSn?: number;
|
||||
cklstSn?: number;
|
||||
rvwStpComCd: string;
|
||||
rvwStpComCd?: string | null;
|
||||
picRvwCn?: string | null;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export interface HwpCtrl {
|
||||
callback?: HwpCtrlSaveAsCallback,
|
||||
callbackUserData?: unknown,
|
||||
) => boolean;
|
||||
ShowHorizontalScroll: (show: boolean) => boolean;
|
||||
}
|
||||
|
||||
export interface HwpCtrlFrameWindow {
|
||||
|
||||
@@ -87,7 +87,10 @@ function HangulGiangi({
|
||||
"HWPX",
|
||||
"",
|
||||
(result) => {
|
||||
const callbackDocumentKey = getCallbackDocumentKey(result.CallbackUserData, documentKey);
|
||||
const callbackDocumentKey = getCallbackDocumentKey(
|
||||
result.CallbackUserData,
|
||||
documentKey,
|
||||
);
|
||||
if (openingDocumentKeyRef.current !== callbackDocumentKey) return;
|
||||
|
||||
openingDocumentKeyRef.current = null;
|
||||
|
||||
@@ -21,14 +21,12 @@ interface CklstResultListItem {
|
||||
cklstSn: number;
|
||||
cklstArtclSn: number;
|
||||
cklstRvwSn: number;
|
||||
rvwStpComCd: string;
|
||||
rvwStpComCd?: string | null;
|
||||
selected: "Y" | "N" | null;
|
||||
picRvwCn?: string | null;
|
||||
}
|
||||
|
||||
export function normalizeTaskSeComCdArray(
|
||||
value: string | string[] | undefined | null,
|
||||
): string[] {
|
||||
export function normalizeTaskSeComCdArray(value: string | string[] | undefined | null): string[] {
|
||||
if (!value) return [];
|
||||
if (Array.isArray(value)) return value.filter(Boolean);
|
||||
return value
|
||||
|
||||
Reference in New Issue
Block a user