[as] feat: 재원협의 코드 수정

This commit is contained in:
2026-09-18 16:27:51 +09:00
parent 43bc7db7d5
commit cc3bc548c9
6 changed files with 86 additions and 19 deletions
@@ -90,6 +90,7 @@ function ElaprAplnPickerGrid({
? isExternalLoading
: aplnQuery.isPending && Boolean(elaprTmpltId);
const pickingRow = pickingRowKey ? rows.find((r) => r.rowKey === pickingRowKey) : undefined;
const excludedVisibleRows = visibleRows.filter((r) => r.atrzSeComCd !== "ATRZ01");
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
@@ -149,7 +150,7 @@ function ElaprAplnPickerGrid({
</div>
) : !isReady ? (
<p className="py-8 text-center text-lg text-muted-foreground">결재서식을 선택하세요.</p>
) : visibleRows.length === 0 ? (
) : excludedVisibleRows.length === 0 ? (
<p className="py-16 text-center text-lg text-muted-foreground">
등록된 결재선이 없습니다. [결재선 추가]로 결재자를 등록하세요.
</p>
@@ -174,10 +175,10 @@ function ElaprAplnPickerGrid({
</TableHeader>
<TableBody>
<SortableContext
items={visibleRows.map((r) => r.rowKey)}
items={excludedVisibleRows.map((r) => r.rowKey)}
strategy={verticalListSortingStrategy}
>
{visibleRows.map((row, index) => (
{excludedVisibleRows.map((row, index) => (
<SortableAplnPickerRow
key={row.rowKey}
row={row}
@@ -6,6 +6,7 @@ export const FNRS_CNSLTN_VIEW_BUTTON_ACTION = {
rjct: "rjct",
sncnAprv: "sncnAprv",
rvwRply: "rvwRply",
rvwOpnnRply: "rvwOpnnRply",
rqstTechRvw: "rqstTechRvw",
fileSplmnt: "fileSplmnt",
dsgntRvwr: "dsgntRvwr",
@@ -37,6 +38,7 @@ const ROLE_STATUS_BUTTON_ACTIONS: Record<string, Record<string, FnrsCnsltnViewBu
FNRS_CNSLTN_VIEW_BUTTON_ACTION.del,
],
RB: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng, FNRS_CNSLTN_VIEW_BUTTON_ACTION.actnCmptn],
RA: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
TA: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
},
ROLE_USER04: {
@@ -45,7 +47,11 @@ const ROLE_STATUS_BUTTON_ACTIONS: Record<string, Record<string, FnrsCnsltnViewBu
},
ROLE_USER03: {
LE: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRply, FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstTechRvw],
TP: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRply],
TP: [
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRply,
FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwOpnnRply,
],
RA: [
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstActnRvw,
@@ -54,7 +60,7 @@ const ROLE_STATUS_BUTTON_ACTIONS: Record<string, Record<string, FnrsCnsltnViewBu
},
};
const ROLE_USER01_ATRZ_STATUS_CODES = ["ER", "EV", "TE", "RK"] as const;
const ROLE_USER01_ATRZ_STATUS_CODES = ["ER", "EV", "SL", "TE", "RK"] as const;
const ROLE_USER01_ATRZ_STATUS_ACTIONS: Record<
string,
@@ -63,8 +69,11 @@ const ROLE_USER01_ATRZ_STATUS_ACTIONS: Record<
ER: {
ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileSplmnt, FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr],
},
EV: {
ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr],
SL: {
ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
ATRZ02: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
ATRZ03: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng],
},
TE: {
ATRZ02: [
@@ -79,7 +88,13 @@ const ROLE_USER01_ATRZ_STATUS_ACTIONS: Record<
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzAprv,
],
ATRZ99: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply],
ATRZ99: [
FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng,
FNRS_CNSLTN_VIEW_BUTTON_ACTION.atrzRply,
],
},
EV: {
ATRZ01: [FNRS_CNSLTN_VIEW_BUTTON_ACTION.dsgntRvwr],
},
RK: {
ATRZ02: [
@@ -143,7 +158,7 @@ function resolveRoleUser01AtrzButtonActions(
if (needsPendingApproval && options?.hasPendingApprovalRequest === false) return false;
if (options?.hasCompletedDraft != null) {
if (prgrsSttsComCd === "TE" || prgrsSttsComCd === "RK") {
if (prgrsSttsComCd === "SL" || prgrsSttsComCd === "TE" || prgrsSttsComCd === "RK") {
if (action === FNRS_CNSLTN_VIEW_BUTTON_ACTION.pwrkCnsltnDocMng) {
return options.hasCompletedDraft;
}
@@ -114,7 +114,8 @@ function FloatingActionButtons({
const needsAtrzResolve = needsRoleUser01AtrzResolve(dtlInstCd, prgrsSttsComCd);
const needsElaprPrgrsCheck = needsAtrzResolve && ["TE", "RK"].includes(prgrsSttsComCd);
const needsPwrkDraftCheck = dtlInstCd === "ROLE_USER01" && ["TE", "RK"].includes(prgrsSttsComCd);
const needsPwrkDraftCheck =
dtlInstCd === "ROLE_USER01" && ["SL", "TE", "RK"].includes(prgrsSttsComCd);
const needsPwrkForRb = dtlInstCd === "ROLE_USER05" && prgrsSttsComCd === "RB";
const needsPwrkLookup = needsPwrkDraftCheck || needsPwrkForRb;
@@ -137,6 +138,7 @@ function FloatingActionButtons({
elaprPrgrsForPending,
userInfo?.userUnqId,
);
const viewOnlyPwrkCnsltnDoc = prgrsSttsComCd === "SL";
const actionButtons = useMemo(() => {
if (needsAtrzResolve && isRvwLoginUserLoading) return [];
@@ -317,12 +319,11 @@ function FloatingActionButtons({
return;
}
const isResultReply = prgrsSttsComCd === "RR";
const payload = buildTechReviewReplyPayload(
data,
checklistFormState,
"ENV",
isResultReply ? "EP" : "RB",
"EP",
"CP",
getReviewOpinionFormData(),
opnrNm,
@@ -336,6 +337,36 @@ function FloatingActionButtons({
techReviewOpinionRef?.current?.resetForm();
}
async function handleRvwOpnnRplyClick() {
const { percent } = computeReviewStepChecklistProgress(
data.cklstResultList ?? [],
checklistFormState.results,
"ENV",
);
if (percent !== 100) {
alert("모든 체크리스트 항목을 체크해야 검토 의견 회신이 가능합니다.");
return;
}
const payload = buildTechReviewReplyPayload(
data,
checklistFormState,
"ENV",
"RB",
"CP",
getReviewOpinionFormData(),
opnrNm,
);
await executeAction({
action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwOpnnRply,
request: payload,
successMessage: "검토 의견 회신이 완료되었습니다.",
});
techReviewOpinionRef?.current?.resetForm();
}
async function handleRqstTechRvwClick() {
if (prgrsSttsComCd === "LE") {
const { percent } = computeReviewStepChecklistProgress(
@@ -365,7 +396,7 @@ function FloatingActionButtons({
await executeAction({
action: FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstActnRvw,
request: payload,
successMessage: "조치결과 검토의뢰가 완료되었습니다.",
successMessage: "조치 결과 검토 의뢰가 완료되었습니다.",
});
}
@@ -570,6 +601,11 @@ function FloatingActionButtons({
결과 회신
</Button>
)}
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwOpnnRply) && (
<Button onClick={handleRvwOpnnRplyClick} disabled={isProcessing}>
검토 의견 회신
</Button>
)}
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rvwRply) && (
<Button color="green" onClick={handleRvwRplyClick} disabled={isProcessing}>
검토 결과 회신
@@ -582,7 +618,7 @@ function FloatingActionButtons({
)}
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.rqstActnRvw) && (
<Button onClick={handleRqstActnRvwClick} disabled={isProcessing}>
조치결과 검토 의뢰
조치 결과 검토 의뢰
</Button>
)}
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.fileSplmnt) && (
@@ -698,7 +734,11 @@ function FloatingActionButtons({
fnrsCnsltnDocNo={docNo}
atrzSeComCdList={atrzSeComCdList}
prgrsSttsComCd={prgrsSttsComCd}
canSave={needsElaprPrgrsCheck && !hasPendingApprovalRequest ? false : undefined}
canSave={
(needsElaprPrgrsCheck && !hasPendingApprovalRequest) || viewOnlyPwrkCnsltnDoc
? false
: null
}
onClose={() => setActiveDialog(null)}
/>
@@ -837,6 +877,7 @@ const buttonActionHandlers: Record<FnrsCnsltnViewButtonAction, ActionHandler> =
rjct: handleChangePrgrsSttsComCd,
sncnAprv: handleChangePrgrsSttsComCd,
rvwRply: handleTechReview,
rvwOpnnRply: handleTechReview,
rqstTechRvw: handleRequestExamine,
fileSplmnt: handleDmndSplmnt,
dsgntRvwr: handleDsgntRvwr,
@@ -43,14 +43,14 @@ const PWRK_ATCH_FILE_KEY_PREFIX = "pwrk-cnsltn-doc::";
const APPROVER_ATRZ_SE_COM_CDS = new Set(["ATRZ03", "ATRZ99"]);
const VIEW_ONLY_PRGRS_STTS_COM_CDS = new Set(["RA", "RR", "EP", "TA"]);
const VIEW_ONLY_PRGRS_STTS_COM_CDS = new Set(["TP", "RA", "RR", "EP", "TA"]);
interface PwrkCnsltnDocMngDialogProps {
open: boolean;
fnrsCnsltnDocNo: string;
atrzSeComCdList?: string[];
prgrsSttsComCd?: string;
canSave?: boolean;
canSave?: boolean | null;
onClose: () => void;
}
@@ -43,11 +43,21 @@ function PwrkCnsltnDocPjtcoAjmtTable({
return (
<Table variant="secondary">
<colgroup>
<col width="30%" />
<col width="50%" />
<col width="20%" />
</colgroup>
<TableHeader className="sticky top-0">
<TableRow>
<TableHead>구분</TableHead>
<TableHead>검증결과</TableHead>
<TableHead className="text-right">검증금액</TableHead>
<TableHead className="text-right">
<div className="flex flex-col gap-1">
<span>검증금액</span>
<span className="text-sm">(단위: 백만 원)</span>
</div>
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
+1 -1
View File
@@ -12,7 +12,7 @@ interface UploadedFileChipProps {
function UploadedFileChip({ fileName, isUploading = false, onRemove }: UploadedFileChipProps) {
return (
<div className="inline-flex items-center justify-between gap-2 rounded-sm border bg-accent p-2">
<div className="inline-flex items-center justify-between gap-2 rounded-sm border bg-accent px-3 py-2">
<Paperclip className="size-5 shrink-0" />
<TextShimmer className="text-xl" active={isUploading}>
{fileName}