[refactor] 대시보드 - 통계처리현황 반려되어 제출중인 상태 구분 처리 포함
This commit is contained in:
@@ -69,10 +69,13 @@ export interface ThreeStageStatusResponse {
|
||||
statsYr: string;
|
||||
laeStatusCd: string;
|
||||
laeStatusNm: string;
|
||||
laeRejected: boolean;
|
||||
ctpvStatusCd: string;
|
||||
ctpvStatusNm: string;
|
||||
ctpvRejected: boolean;
|
||||
pbcpStatusCd: string;
|
||||
pbcpStatusNm: string;
|
||||
pbcpRejected: boolean;
|
||||
}
|
||||
|
||||
export interface DashboardService {
|
||||
|
||||
@@ -15,26 +15,62 @@ interface StepDef {
|
||||
label: string;
|
||||
subLabel: string;
|
||||
description: string;
|
||||
rejectedDescription: string;
|
||||
}
|
||||
|
||||
const STEPS: StepDef[] = [
|
||||
{ key: "input", label: "입력", subLabel: "작성자", description: "담당자가 통계 자료를 입력합니다." },
|
||||
{ key: "submit", label: "제출", subLabel: "작성자", description: "입력을 마친 자료를 제출합니다." },
|
||||
{ key: "approve", label: "승인", subLabel: "승인자", description: "지자체 승인자가 제출 내용을 승인합니다." },
|
||||
{ key: "review", label: "검토", subLabel: "시도", description: "광역시도 담당자가 자료를 검토합니다." },
|
||||
{ key: "verify", label: "검증", subLabel: "공단", description: "환경공단이 최종 검증을 진행합니다." },
|
||||
{
|
||||
key: "input",
|
||||
label: "입력",
|
||||
subLabel: "작성자",
|
||||
description: "담당자가 통계 자료를 입력합니다.",
|
||||
rejectedDescription: "반려된 자료에 대한 수정보완 후 제출합니다.",
|
||||
},
|
||||
{
|
||||
key: "submit",
|
||||
label: "제출",
|
||||
subLabel: "작성자",
|
||||
description: "입력을 마친 자료를 제출합니다.",
|
||||
rejectedDescription: "반려된 자료에 대한 수정보완 후 제출합니다.",
|
||||
},
|
||||
{
|
||||
key: "approve",
|
||||
label: "승인",
|
||||
subLabel: "승인자",
|
||||
description: "지자체 승인자가 제출 내용을 승인합니다.",
|
||||
rejectedDescription: "반려된 자료에 대한 수정보완 후 제출합니다.",
|
||||
},
|
||||
{
|
||||
key: "review",
|
||||
label: "검토",
|
||||
subLabel: "시도",
|
||||
description: "광역시도 담당자가 자료를 검토합니다.",
|
||||
rejectedDescription: "반려된 자료에 대한 수정보완 후 재검토를 요청합니다.",
|
||||
},
|
||||
{
|
||||
key: "verify",
|
||||
label: "검증",
|
||||
subLabel: "공단",
|
||||
description: "환경공단이 최종 검증을 진행합니다.",
|
||||
rejectedDescription: "반려된 자료에 대한 수정보완 후 재검증을 요청합니다.",
|
||||
},
|
||||
];
|
||||
|
||||
interface ThreeStageData {
|
||||
laeStatusCd: string;
|
||||
laeRejected: boolean;
|
||||
ctpvStatusCd: string;
|
||||
ctpvRejected: boolean;
|
||||
pbcpStatusCd: string;
|
||||
pbcpRejected: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* lae_sbmsn_stts_cd(지자체) : 0=미작성 1=작성중 2=제출완료 3=승인
|
||||
* stat_pic_sbmsn_stts_cd(광역시도), pbcp_sbmsn_stts_cd(공단) 도 동일 코드 도메인 가정
|
||||
* - 실제 코드값 의미는 STINPUTSTP 코드 그룹 기준 재확인 필요
|
||||
*/
|
||||
function resolveCheckedSteps(data?: {
|
||||
laeStatusCd: string;
|
||||
ctpvStatusCd: string;
|
||||
pbcpStatusCd: string;
|
||||
}): boolean[] {
|
||||
function resolveCheckedSteps(data?: ThreeStageData): boolean[] {
|
||||
if (!data) return STEPS.map(() => false);
|
||||
|
||||
const lae = Number(data.laeStatusCd);
|
||||
@@ -50,6 +86,18 @@ function resolveCheckedSteps(data?: {
|
||||
];
|
||||
}
|
||||
|
||||
// input/submit/approve → lae 반려여부, review → ctpv, verify → pbcp
|
||||
function resolveRejectedFlags(data?: ThreeStageData): boolean[] {
|
||||
if (!data) return STEPS.map(() => false);
|
||||
return [
|
||||
data.laeRejected,
|
||||
data.laeRejected,
|
||||
data.laeRejected,
|
||||
data.ctpvRejected,
|
||||
data.pbcpRejected,
|
||||
];
|
||||
}
|
||||
|
||||
interface ThreeStageStatusPanelProps {
|
||||
params: DashboardSearchParams | null;
|
||||
}
|
||||
@@ -59,10 +107,23 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
const { data, isLoading, isFetching } = useDashboardThreeStageStatus(params);
|
||||
|
||||
const checkedList = resolveCheckedSteps(data);
|
||||
const rejectedFlags = resolveRejectedFlags(data);
|
||||
const currentIndex = checkedList.findIndex((v) => !v);
|
||||
const isAllDone = currentIndex === -1;
|
||||
const currentStep = STEPS[isAllDone ? STEPS.length - 1 : currentIndex]!;
|
||||
const currentLabel = isAllDone ? "전 단계 완료" : `${currentStep.label} 대기중`;
|
||||
const isCurrentRejected = !isAllDone && rejectedFlags[currentIndex];
|
||||
|
||||
const currentLabel = isAllDone
|
||||
? "전 단계 완료"
|
||||
: isCurrentRejected
|
||||
? `${currentStep.label} 반려`
|
||||
: `${currentStep.label} 대기중`;
|
||||
|
||||
const currentDescription = isAllDone
|
||||
? "모든 절차가 완료되었습니다."
|
||||
: isCurrentRejected
|
||||
? currentStep.rejectedDescription
|
||||
: currentStep.description;
|
||||
|
||||
return (
|
||||
<div className="card card-border !m-0 flex h-full flex-col border-border bg-background text-foreground">
|
||||
@@ -76,7 +137,9 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
className={`rounded-full px-4 py-1.5 text-base font-medium ${
|
||||
isAllDone
|
||||
? "bg-blue-50 text-blue-600"
|
||||
: "bg-amber-50 text-amber-600"
|
||||
: isCurrentRejected
|
||||
? "bg-red-50 text-red-600"
|
||||
: "bg-amber-50 text-amber-600"
|
||||
}`}
|
||||
>
|
||||
{currentLabel}
|
||||
@@ -94,6 +157,7 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
{STEPS.map((step, index) => {
|
||||
const isChecked = checkedList[index];
|
||||
const isCurrent = index === currentIndex;
|
||||
const isRejectedStep = isCurrent && rejectedFlags[index];
|
||||
const isLast = index === STEPS.length - 1;
|
||||
const lineFilled = checkedList[index];
|
||||
|
||||
@@ -101,16 +165,29 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
<Fragment key={step.key}>
|
||||
<div className="flex flex-col items-center">
|
||||
<div className="relative flex flex-col items-center">
|
||||
{isCurrent && (
|
||||
{isRejectedStep && (
|
||||
<div className="absolute -top-11 flex flex-col items-center">
|
||||
<span className="whitespace-nowrap rounded-lg bg-slate-700 px-3 py-1.5 text-base font-bold text-white shadow-md">
|
||||
반려
|
||||
</span>
|
||||
<span className="-mt-px h-0 w-0 border-x-[6px] border-t-[6px] border-x-transparent border-t-slate-700" />
|
||||
</div>
|
||||
)}
|
||||
{isCurrent && !isRejectedStep && (
|
||||
<span className="absolute inset-0 -m-1.5 animate-ping rounded-full bg-blue-400 opacity-40" />
|
||||
)}
|
||||
{isRejectedStep && (
|
||||
<span className="absolute inset-0 -m-1.5 animate-ping rounded-full bg-red-400 opacity-40" />
|
||||
)}
|
||||
<div
|
||||
className={`relative flex size-20 items-center justify-center rounded-full border-4 transition-colors ${
|
||||
isChecked
|
||||
? "border-blue-600 bg-blue-600 shadow-lg shadow-blue-600/20"
|
||||
: isCurrent
|
||||
? "border-blue-500 bg-white"
|
||||
: "border-gray-200 bg-white"
|
||||
: isRejectedStep
|
||||
? "border-red-500 bg-white"
|
||||
: isCurrent
|
||||
? "border-blue-500 bg-white"
|
||||
: "border-gray-200 bg-white"
|
||||
}`}
|
||||
>
|
||||
{isChecked ? (
|
||||
@@ -128,7 +205,11 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
) : (
|
||||
<span
|
||||
className={`size-4 rounded-full ${
|
||||
isCurrent ? "bg-blue-500" : "bg-gray-200"
|
||||
isRejectedStep
|
||||
? "bg-red-500"
|
||||
: isCurrent
|
||||
? "bg-blue-500"
|
||||
: "bg-gray-200"
|
||||
}`}
|
||||
/>
|
||||
)}
|
||||
@@ -139,9 +220,11 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
className={`text-xl font-bold ${
|
||||
isChecked
|
||||
? "text-blue-700"
|
||||
: isCurrent
|
||||
? "text-blue-600"
|
||||
: "text-gray-400"
|
||||
: isRejectedStep
|
||||
? "text-red-600"
|
||||
: isCurrent
|
||||
? "text-blue-600"
|
||||
: "text-gray-400"
|
||||
}`}
|
||||
>
|
||||
{step.label}
|
||||
@@ -164,15 +247,22 @@ export function ThreeStageStatusPanel({ params }: ThreeStageStatusPanelProps) {
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="w-full rounded-2xl border border-border bg-blue-50/40 px-7 py-6 text-center">
|
||||
<p className="text-base font-medium text-blue-600">
|
||||
<div
|
||||
className={`w-full rounded-2xl border px-7 py-6 text-center ${
|
||||
isCurrentRejected
|
||||
? "border-red-200 bg-red-50/50"
|
||||
: "border-border bg-blue-50/40"
|
||||
}`}
|
||||
>
|
||||
<p
|
||||
className={`text-base font-medium ${
|
||||
isCurrentRejected ? "text-red-600" : "text-blue-600"
|
||||
}`}
|
||||
>
|
||||
{isAllDone ? "현재 단계" : "다음 진행 단계"} · {currentStep.label}
|
||||
{isCurrentRejected ? " (반려)" : ""}
|
||||
</p>
|
||||
<p className="mt-2 text-lg text-foreground">
|
||||
{isAllDone
|
||||
? "모든 절차가 완료되었습니다."
|
||||
: currentStep.description}
|
||||
</p>
|
||||
<p className="mt-2 text-lg text-foreground">{currentDescription}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user