Merge branch 'iipbs/as/dev'
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
import { client } from "@/shared/api";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import type {
|
||||
PwrkCnsltnDocEvlId,
|
||||
PwrkCnsltnDocEvlService,
|
||||
} from "../model/pwrk-cnsltn-doc-evl-interface";
|
||||
import type { PwrkCnsltnDocEvlSaveRequestDto } from "../model/pwrk-cnsltn-doc-evl-payload-schema";
|
||||
|
||||
const BASE = ENDPOINT.AS_SERVICE.PWRK_CNSLTN_DOC_EVL;
|
||||
|
||||
async function save(body: PwrkCnsltnDocEvlSaveRequestDto): Promise<PwrkCnsltnDocEvlId> {
|
||||
const response = await client.post<PwrkCnsltnDocEvlId>(BASE, body);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export const pwrkCnsltnDocEvlService = {
|
||||
save,
|
||||
} satisfies PwrkCnsltnDocEvlService;
|
||||
@@ -0,0 +1,11 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import type { PwrkCnsltnDocEvlSaveRequestDto } from "../model/pwrk-cnsltn-doc-evl-payload-schema";
|
||||
|
||||
import { pwrkCnsltnDocEvlService } from "./pwrk-cnsltn-doc-evl-service";
|
||||
|
||||
export function useSavePwrkCnsltnDocEvl() {
|
||||
return useMutation({
|
||||
mutationFn: (body: PwrkCnsltnDocEvlSaveRequestDto) => pwrkCnsltnDocEvlService.save(body),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export { pwrkCnsltnDocEvlService } from "./api/pwrk-cnsltn-doc-evl-service";
|
||||
export { useSavePwrkCnsltnDocEvl } from "./api/queries";
|
||||
export type {
|
||||
PwrkCnsltnDocEvlId,
|
||||
PwrkCnsltnDocEvlService,
|
||||
} from "./model/pwrk-cnsltn-doc-evl-interface";
|
||||
export {
|
||||
type PwrkCnsltnDocEvlSaveRequestDto,
|
||||
pwrkCnsltnDocEvlSaveRequestSchema,
|
||||
} from "./model/pwrk-cnsltn-doc-evl-payload-schema";
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { PwrkCnsltnDocEvlSaveRequestDto } from "./pwrk-cnsltn-doc-evl-payload-schema";
|
||||
|
||||
/** 백엔드 PwrkCnsltnDocEvlId (IdClass) */
|
||||
export interface PwrkCnsltnDocEvlId {
|
||||
pwrkCnsltnDocMng: string;
|
||||
pwrkCnsltnDocEvlUuid: string;
|
||||
}
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 (save만) */
|
||||
export interface PwrkCnsltnDocEvlService {
|
||||
save(body: PwrkCnsltnDocEvlSaveRequestDto): Promise<PwrkCnsltnDocEvlId>;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { z } from "zod";
|
||||
|
||||
/** 백엔드 PwrkCnsltnDocEvlSaveRequestDto */
|
||||
export const pwrkCnsltnDocEvlSaveRequestSchema = z.object({
|
||||
pwrkCnsltnDocNo: z.string().min(1),
|
||||
pwrkCnsltnDocEvlUuid: z.string().uuid().optional(),
|
||||
pwrkCnsltnDocEvlCn: z.string().max(2000).optional(),
|
||||
pwrkCnsltnDocEvlScr: z.number().optional(),
|
||||
pwrkCnsltnDocEvlDt: z.string().optional(),
|
||||
});
|
||||
|
||||
export type PwrkCnsltnDocEvlSaveRequestDto = z.infer<typeof pwrkCnsltnDocEvlSaveRequestSchema>;
|
||||
@@ -141,11 +141,9 @@ const ROLE_USER01_ATRZ_STATUS_ACTIONS: Record<
|
||||
ATRZ01: [
|
||||
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
|
||||
FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr,
|
||||
],
|
||||
ATRZ02: [
|
||||
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
|
||||
FNRS_CNSLTN_VIEW_BUTTON_ACTION.costVrfc,
|
||||
],
|
||||
ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
|
||||
ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
|
||||
ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
|
||||
},
|
||||
@@ -235,6 +233,9 @@ function resolveRoleUser01AtrzButtonActions(
|
||||
if (action === FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileVrfc) {
|
||||
return options?.canRetryFileVrfc === true;
|
||||
}
|
||||
if (action === FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileSplmnt) {
|
||||
return options?.canRetryFileVrfc !== true;
|
||||
}
|
||||
if (action === FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocCrt) {
|
||||
return options?.canRetryDraftGen === true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { pwrkCnsltnDocEvlSaveRequestSchema } from "@/entities/pwrk-cnsltn-doc-evl";
|
||||
|
||||
export const PWRK_CNSLTN_DOC_EVL_SCR_OPTIONS = [
|
||||
{ value: "5", label: "매우 만족" },
|
||||
{ value: "4", label: "만족" },
|
||||
{ value: "3", label: "보통" },
|
||||
{ value: "2", label: "불만족" },
|
||||
{ value: "1", label: "매우 불만족" },
|
||||
] as const;
|
||||
|
||||
export const pwrkCnsltnDocEvlFormSchema = z.object({
|
||||
pwrkCnsltnDocEvlScr: z.enum(["1", "2", "3", "4", "5"], {
|
||||
required_error: "만족도를 선택해 주세요.",
|
||||
invalid_type_error: "만족도를 선택해 주세요.",
|
||||
}),
|
||||
pwrkCnsltnDocEvlCn: z.string().max(2000).optional(),
|
||||
});
|
||||
|
||||
export type PwrkCnsltnDocEvlFormValues = z.infer<typeof pwrkCnsltnDocEvlFormSchema>;
|
||||
|
||||
export function buildPwrkCnsltnDocEvlSavePayload(
|
||||
pwrkCnsltnDocNo: string,
|
||||
form: PwrkCnsltnDocEvlFormValues,
|
||||
) {
|
||||
return pwrkCnsltnDocEvlSaveRequestSchema.parse({
|
||||
pwrkCnsltnDocNo,
|
||||
pwrkCnsltnDocEvlScr: Number(form.pwrkCnsltnDocEvlScr),
|
||||
pwrkCnsltnDocEvlCn: form.pwrkCnsltnDocEvlCn?.trim() || undefined,
|
||||
});
|
||||
}
|
||||
@@ -709,6 +709,7 @@ function FloatingActionButtons({
|
||||
)}
|
||||
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileVrfc) && (
|
||||
<Button
|
||||
color="gray"
|
||||
onClick={handleFileVrfcClick}
|
||||
disabled={isBusy}
|
||||
title={fileVrfcLatest?.prcsErrCn ?? undefined}
|
||||
@@ -716,6 +717,16 @@ function FloatingActionButtons({
|
||||
파일 검증
|
||||
</Button>
|
||||
)}
|
||||
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.costVrfc) && (
|
||||
<Button
|
||||
color="gray"
|
||||
onClick={handleCostVrfcClick}
|
||||
disabled={isBusy}
|
||||
title={costVrfcLatest?.prcsErrCn ?? undefined}
|
||||
>
|
||||
금액 검증
|
||||
</Button>
|
||||
)}
|
||||
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr) &&
|
||||
!isRagIndexingInProgress(prgrsSttsComCd, data.ragIndexingInfo) && (
|
||||
<Button
|
||||
@@ -727,15 +738,6 @@ function FloatingActionButtons({
|
||||
검토자 지정
|
||||
</Button>
|
||||
)}
|
||||
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.costVrfc) && (
|
||||
<Button
|
||||
onClick={handleCostVrfcClick}
|
||||
disabled={isBusy}
|
||||
title={costVrfcLatest?.prcsErrCn ?? undefined}
|
||||
>
|
||||
금액 검증
|
||||
</Button>
|
||||
)}
|
||||
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.actnCmptn) && (
|
||||
<Button color="green" onClick={handleActnCmptnClick} disabled={isBusy}>
|
||||
조치 완료
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
|
||||
import { useAlertStore } from "@/shared/lib";
|
||||
import { useSavePwrkCnsltnDocEvl } from "@/entities/pwrk-cnsltn-doc-evl";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
Label,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Textarea,
|
||||
} from "@common/components";
|
||||
|
||||
import {
|
||||
buildPwrkCnsltnDocEvlSavePayload,
|
||||
PWRK_CNSLTN_DOC_EVL_SCR_OPTIONS,
|
||||
pwrkCnsltnDocEvlFormSchema,
|
||||
type PwrkCnsltnDocEvlFormValues,
|
||||
} from "../../lib/pwrk-cnsltn-doc-evl-schema";
|
||||
|
||||
interface PwrkCnsltnDocEvlDialogProps {
|
||||
open: boolean;
|
||||
pwrkCnsltnDocNo: string;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const DEFAULT_VALUES: Partial<PwrkCnsltnDocEvlFormValues> = {
|
||||
pwrkCnsltnDocEvlScr: undefined,
|
||||
pwrkCnsltnDocEvlCn: "",
|
||||
};
|
||||
|
||||
function PwrkCnsltnDocEvlDialog({ open, pwrkCnsltnDocNo, onClose }: PwrkCnsltnDocEvlDialogProps) {
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const { mutateAsync: saveEvl, isPending } = useSavePwrkCnsltnDocEvl();
|
||||
|
||||
const { handleSubmit, reset, setValue, watch, register } = useForm<PwrkCnsltnDocEvlFormValues>({
|
||||
resolver: zodResolver(pwrkCnsltnDocEvlFormSchema),
|
||||
defaultValues: DEFAULT_VALUES,
|
||||
});
|
||||
|
||||
const pwrkCnsltnDocEvlScr = watch("pwrkCnsltnDocEvlScr");
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
reset(DEFAULT_VALUES);
|
||||
}, [open, reset]);
|
||||
|
||||
function handleOpenChange(nextOpen: boolean) {
|
||||
if (!nextOpen) onClose();
|
||||
}
|
||||
|
||||
async function onSubmit(values: PwrkCnsltnDocEvlFormValues) {
|
||||
try {
|
||||
const payload = buildPwrkCnsltnDocEvlSavePayload(pwrkCnsltnDocNo, values);
|
||||
await saveEvl(payload);
|
||||
alert("AI 초안 만족도 평가를 등록했습니다.");
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("AI 초안 만족도 평가 등록 실패:", error);
|
||||
alert("AI 초안 만족도 평가 등록에 실패했습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent className="max-w-[40rem]" aria-describedby={undefined}>
|
||||
<DialogHeader>
|
||||
<DialogTitle>AI 초안 만족도 평가</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<form className="space-y-4" onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="space-y-2">
|
||||
<Label>만족도</Label>
|
||||
<Select
|
||||
value={pwrkCnsltnDocEvlScr || undefined}
|
||||
onValueChange={(value) =>
|
||||
setValue(
|
||||
"pwrkCnsltnDocEvlScr",
|
||||
value as PwrkCnsltnDocEvlFormValues["pwrkCnsltnDocEvlScr"],
|
||||
{
|
||||
shouldValidate: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="만족도를 선택해 주세요" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PWRK_CNSLTN_DOC_EVL_SCR_OPTIONS.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pwrkCnsltnDocEvlCn">의견</Label>
|
||||
<Textarea
|
||||
id="pwrkCnsltnDocEvlCn"
|
||||
rows={4}
|
||||
placeholder="의견을 입력해 주세요 (선택)"
|
||||
{...register("pwrkCnsltnDocEvlCn")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" color="white" onClick={onClose} disabled={isPending}>
|
||||
닫기
|
||||
</Button>
|
||||
<Button type="submit" disabled={isPending}>
|
||||
제출
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { PwrkCnsltnDocEvlDialog };
|
||||
export type { PwrkCnsltnDocEvlDialogProps };
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
Skeleton,
|
||||
} from "@common/components";
|
||||
|
||||
import { PwrkCnsltnDocEvlDialog } from "./pwrk-cnsltn-doc-evl-dialog";
|
||||
import { PwrkCnsltnDocPjtcoAjmtTable } from "./pwrk-cnsltn-doc-pjtco-ajmt-table";
|
||||
|
||||
const HangulGiangi = dynamic(
|
||||
@@ -45,6 +46,8 @@ const HangulGiangi = dynamic(
|
||||
|
||||
const PWRK_ATCH_FILE_KEY_PREFIX = "pwrk-cnsltn-doc::";
|
||||
|
||||
const AI_DRAFT_EVL_MAX_VER_SN = 2;
|
||||
|
||||
const APPROVER_ATRZ_SE_COM_CDS = new Set(["ATRZ03", "ATRZ99"]);
|
||||
|
||||
const VIEW_ONLY_PRGRS_STTS_COM_CDS = new Set(["TP", "RA", "AV", "EV", "RR", "EP", "TA"]);
|
||||
@@ -82,6 +85,7 @@ function PwrkCnsltnDocMngDialog({
|
||||
const [atchFileGroupId, setAtchFileGroupId] = useState("");
|
||||
const [uploadedFiles, setUploadedFiles] = useState<UploadedAtchFile[]>([]);
|
||||
const [isPjtcoAjmtTableOpen, setIsPjtcoAjmtTableOpen] = useState(false);
|
||||
const [isEvlDialogOpen, setIsEvlDialogOpen] = useState(false);
|
||||
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
|
||||
|
||||
const {
|
||||
@@ -138,6 +142,7 @@ function PwrkCnsltnDocMngDialog({
|
||||
if (!open) {
|
||||
setAtchFileGroupId("");
|
||||
setUploadedFiles([]);
|
||||
setIsEvlDialogOpen(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -194,6 +199,10 @@ function PwrkCnsltnDocMngDialog({
|
||||
}),
|
||||
]);
|
||||
alert(`실무협의서를 저장했습니다. (버전 ${savedDocument.verSn})`);
|
||||
|
||||
if (savedDocument.verSn <= AI_DRAFT_EVL_MAX_VER_SN) {
|
||||
setIsEvlDialogOpen(true);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDownload() {
|
||||
@@ -269,105 +278,115 @@ function PwrkCnsltnDocMngDialog({
|
||||
const canSave = !viewOnly && (canSaveProp ?? canSaveByAtrz);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent
|
||||
className="max-h-[96vh] max-w-[80vw] overflow-y-auto"
|
||||
aria-describedby={undefined}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>실무협의서 관리</DialogTitle>
|
||||
</DialogHeader>
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={handleOpenChange}>
|
||||
<DialogContent
|
||||
className="max-h-[96vh] max-w-[80vw] overflow-y-auto"
|
||||
aria-describedby={undefined}
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>실무협의서 관리</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
{isLoading ? <Skeleton className="h-[70vh] min-h-[40rem] w-full" /> : null}
|
||||
{isError ? (
|
||||
<p className="py-16 text-center text-lg text-muted-foreground">
|
||||
실무협의서 정보를 불러오지 못했습니다.
|
||||
</p>
|
||||
) : null}
|
||||
{!isLoading && !isError && !completedDraft ? (
|
||||
<p className="py-16 text-center text-lg text-muted-foreground">
|
||||
생성 완료된 실무협의서 초안이 없습니다.
|
||||
</p>
|
||||
) : 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}
|
||||
/>
|
||||
{isLoading ? <Skeleton className="h-[70vh] min-h-[40rem] w-full" /> : null}
|
||||
{isError ? (
|
||||
<p className="py-16 text-center text-lg text-muted-foreground">
|
||||
실무협의서 정보를 불러오지 못했습니다.
|
||||
</p>
|
||||
) : null}
|
||||
{!isLoading && !isError && !completedDraft ? (
|
||||
<p className="py-16 text-center text-lg text-muted-foreground">
|
||||
생성 완료된 실무협의서 초안이 없습니다.
|
||||
</p>
|
||||
) : 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>
|
||||
{pjtcoAjmtPage?.content?.length ? (
|
||||
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>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
{pjtcoAjmtPage?.content?.length ? (
|
||||
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>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
) : null}
|
||||
|
||||
{completedDraft ? (
|
||||
<section className="space-y-2 rounded-md border border-border p-4">
|
||||
<h3 className="text-xl font-semibold">첨부파일</h3>
|
||||
{uploadedFiles.length === 0 ? (
|
||||
<p className="text-lg text-muted-foreground">등록된 첨부파일이 없습니다.</p>
|
||||
) : null}
|
||||
<AttachmentFileField
|
||||
files={uploadedFiles.map((item) => ({
|
||||
fileKey: item.fileKey,
|
||||
fileName: item.fileName,
|
||||
onDownload: item.atflId ? () => downloadByAtflId(item.atflId!) : undefined,
|
||||
}))}
|
||||
inputId="pwrk-cnsltn-doc-atch-file-input"
|
||||
isUploadingByKey={isUploading}
|
||||
addDisabled={!canSave || isAnyAtchUploading}
|
||||
showAddButton={canSave}
|
||||
onAdd={(files) => void handleAtchFileUpload(files)}
|
||||
onRemove={canSave ? (index) => void handleAtchFileRemove(index) : undefined}
|
||||
/>
|
||||
</section>
|
||||
) : null}
|
||||
{completedDraft ? (
|
||||
<section className="space-y-2 rounded-md border border-border p-4">
|
||||
<h3 className="text-xl font-semibold">첨부파일</h3>
|
||||
{uploadedFiles.length === 0 ? (
|
||||
<p className="text-lg text-muted-foreground">등록된 첨부파일이 없습니다.</p>
|
||||
) : null}
|
||||
<AttachmentFileField
|
||||
files={uploadedFiles.map((item) => ({
|
||||
fileKey: item.fileKey,
|
||||
fileName: item.fileName,
|
||||
onDownload: item.atflId ? () => downloadByAtflId(item.atflId!) : undefined,
|
||||
}))}
|
||||
inputId="pwrk-cnsltn-doc-atch-file-input"
|
||||
isUploadingByKey={isUploading}
|
||||
addDisabled={!canSave || isAnyAtchUploading}
|
||||
showAddButton={canSave}
|
||||
onAdd={(files) => void handleAtchFileUpload(files)}
|
||||
onRemove={canSave ? (index) => void handleAtchFileRemove(index) : undefined}
|
||||
/>
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<DialogFooter>
|
||||
<Button type="button" color="white" onClick={onClose}>
|
||||
닫기
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<DialogFooter>
|
||||
<Button type="button" color="white" onClick={onClose}>
|
||||
닫기
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{completedDraft ? (
|
||||
<PwrkCnsltnDocEvlDialog
|
||||
open={isEvlDialogOpen}
|
||||
pwrkCnsltnDocNo={completedDraft.pwrkCnsltnDocNo}
|
||||
onClose={() => setIsEvlDialogOpen(false)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ export const ENDPOINT = {
|
||||
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",
|
||||
PWRK_CNSLTN_DOC_EVL: "/as-service/api/v1/pwrk-cnsltn-doc-evl",
|
||||
},
|
||||
USER_SERVICE: {
|
||||
LOGIN: "/user-service/login",
|
||||
|
||||
Reference in New Issue
Block a user