Merge branch 'main' into deploy
Deploy Frontend / deploy (push) Successful in 3m55s

This commit is contained in:
2026-08-26 17:14:14 +09:00
12 changed files with 586 additions and 404 deletions
@@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useQueryClient } from "@tanstack/react-query";
import { useAlertStore, useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import {
type DesignCnstnApplySaveRequestDto,
designCnstnKeys,
@@ -167,54 +168,64 @@ function DesignFormPage({ mode, initialData, createDefaults }: DesignFormPagePro
return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="mt-6 space-y-4">
{initialData?.detDto?.designCnstnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.designCnstnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="designCnstnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<AttachmentsFormSection
docSeComCd="CNFM"
fieldPrefix="designCnstnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
{initialData?.detDto?.designCnstnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.designCnstnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="designCnstnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<AttachmentsFormSection
docSeComCd="CNFM"
fieldPrefix="designCnstnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/design-cnstn")}
disabled={isSubmitting}
>
목록
</Button>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="green"
color="white"
type="button"
onClick={handleApplyClick}
onClick={() => router.push("/design-cnstn")}
disabled={isSubmitting}
>
신청완료
목록
</Button>
<Button color="red" type="button" onClick={handleDelClick} disabled={isSubmitting}>
삭제
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
</>
)}
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={handleApplyClick}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</div>
</div>
</div>
</div>
</SectionCollapsibleGroup>
</form>
</FormProvider>
);
@@ -3,6 +3,7 @@
import { useMemo, useRef } from "react";
import { useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { useDesignCnstnMng } from "@/entities/design-cnstn";
import { ElaprDocPrgrsStatus } from "@/entities/elapr";
import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw";
@@ -80,35 +81,40 @@ function DesignViewPage({ id }: DesignViewPageProps) {
);
return (
<div className="mt-6 space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.designCnstnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.designCnstnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="CNFM"
designCnstnDocNo={detDto.designCnstnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
<div className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.designCnstnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.designCnstnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="CNFM"
designCnstnDocNo={detDto.designCnstnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
</div>
</SectionCollapsibleGroup>
</div>
);
}
@@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useQueryClient } from "@tanstack/react-query";
import { useAlertStore, useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { useAcntgSeMngTree } from "@/entities/acntg";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
@@ -259,79 +260,84 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps)
return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="mt-6 space-y-4">
{initialData?.detDto?.fnrsCnsltnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.fnrsCnsltnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="fnrsCnsltnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<BusinessCostFormSection />
<FnrsSharFormSection
initialSharTree={isEditMode ? initialData?.fnrsSharList : undefined}
initialSharAtchFiles={isEditMode ? initialData?.sharAtchFileList : undefined}
/>
<ChecklistFormSection docSeComCd="FNRS" fieldPrefix="fnrsCnsltnMng" />
<AttachmentsFormSection
docSeComCd="FNRS"
fieldPrefix="fnrsCnsltnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/fnrs-cnsltn")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={handleApplyClick}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
<form onSubmit={form.handleSubmit(onSave)} className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
{initialData?.detDto?.fnrsCnsltnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.fnrsCnsltnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="fnrsCnsltnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<BusinessCostFormSection />
<FnrsSharFormSection
initialSharTree={isEditMode ? initialData?.fnrsSharList : undefined}
initialSharAtchFiles={isEditMode ? initialData?.sharAtchFileList : undefined}
/>
<ChecklistFormSection docSeComCd="FNRS" fieldPrefix="fnrsCnsltnMng" />
<AttachmentsFormSection
docSeComCd="FNRS"
fieldPrefix="fnrsCnsltnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/fnrs-cnsltn")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={handleApplyClick}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
)}
</div>
</div>
</div>
</div>
</SectionCollapsibleGroup>
</form>
</FormProvider>
);
@@ -3,10 +3,15 @@
import { useMemo, useRef } from "react";
import { useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { ElaprDocPrgrsStatus } from "@/entities/elapr";
import { useFnrsCnsltnMng } from "@/entities/fnrs-cnsltn";
import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw";
import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt";
import {
AttachmentsViewSection,
ProgressStatusSection,
SupplementDemandSection,
} from "@/widgets/pbadms-sprt";
import { Skeleton } from "@common/components";
import { needsRoleUser01AtrzResolve } from "../lib/button-config";
@@ -90,46 +95,51 @@ function FnrsViewPage({ id }: FnrsViewPageProps) {
);
return (
<div className="mt-6 space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.fnrsCnsltnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.fnrsCnsltnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<BusinessCostSection bizCstList={bizCstList} />
<FnrsSharSection fnrsSharList={fnrsSharList} />
<ChecklistSection
cklstResultList={cklstResultList}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="FNRS"
fnrsCnsltnDocNo={detDto.fnrsCnsltnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
<div className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.fnrsCnsltnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.fnrsCnsltnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<BusinessCostSection bizCstList={bizCstList} />
<FnrsSharSection fnrsSharList={fnrsSharList} />
<ChecklistSection
cklstResultList={cklstResultList}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="FNRS"
fnrsCnsltnDocNo={detDto.fnrsCnsltnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
</div>
</SectionCollapsibleGroup>
</div>
);
}
@@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useQueryClient } from "@tanstack/react-query";
import { useAlertStore, useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
type InstlSncnApplySaveRequestDto,
@@ -222,75 +223,80 @@ function InstlFormPage({ mode, initialData, createDefaults }: InstlFormPageProps
return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="mt-6 space-y-4">
{initialData?.detDto.instlSncnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.instlSncnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="instlSncnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<BusinessCostFormSection />
<ChecklistFormSection docSeComCd="INSTL" fieldPrefix="instlSncnMng" />
<AttachmentsFormSection
docSeComCd="INSTL"
fieldPrefix="instlSncnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/instl-sncn")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={form.handleSubmit(onApply)}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
<form onSubmit={form.handleSubmit(onSave)} className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
{initialData?.detDto.instlSncnDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.instlSncnDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="instlSncnMng" />
<PrivacyConsentSection />
<BusinessScaleFormSection />
<BusinessCostFormSection />
<ChecklistFormSection docSeComCd="INSTL" fieldPrefix="instlSncnMng" />
<AttachmentsFormSection
docSeComCd="INSTL"
fieldPrefix="instlSncnMng"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
<SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/instl-sncn")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={form.handleSubmit(onApply)}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
)}
</div>
</div>
</div>
</div>
</SectionCollapsibleGroup>
</form>
</FormProvider>
);
@@ -3,10 +3,15 @@
import { useMemo, useRef } from "react";
import { useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { ElaprDocPrgrsStatus } from "@/entities/elapr";
import { useInstlSncnDetail } from "@/entities/instl-sncn";
import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw";
import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt";
import {
AttachmentsViewSection,
ProgressStatusSection,
SupplementDemandSection,
} from "@/widgets/pbadms-sprt";
import { Skeleton } from "@common/components";
import { needsRoleUser01AtrzResolve } from "../lib/button-config";
@@ -88,48 +93,53 @@ function InstlViewPage({ id }: InstlViewPageProps) {
);
return (
<div className="mt-6 space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.instlSncnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.instlSncnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<BusinessCostSection bizCstList={bizCstList} />
<ChecklistSection
cklstResultList={cklstResultList}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection
esntlFileList={esntlFileList ?? []}
etcFileList={etcFileList ?? []}
/>
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<FloatingActionButtons
data={data}
docSeComCd="INSTL"
instlSncnDocNo={detDto.instlSncnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
<div className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.instlSncnDocNo}
/>
<BasicInfoSection det={detDto} />
<ContactInfoSection det={detDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.instlSncnDocNo} />
<BusinessScaleSection bizSclList={bizSclList} />
<BusinessCostSection bizCstList={bizCstList} />
<ChecklistSection
cklstResultList={cklstResultList}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection
esntlFileList={esntlFileList ?? []}
etcFileList={etcFileList ?? []}
/>
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<FloatingActionButtons
data={data}
docSeComCd="INSTL"
instlSncnDocNo={detDto.instlSncnDocNo}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
</div>
</SectionCollapsibleGroup>
</div>
);
}
@@ -9,6 +9,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { useQueryClient } from "@tanstack/react-query";
import { useAlertStore, useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { useAcntgSeMngTree } from "@/entities/acntg";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
@@ -240,76 +241,81 @@ function SewerBscPlanFormPage({
return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="mt-6 space-y-4">
{initialData?.detDto?.sewerBscPlanDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.sewerBscPlanDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="sewerBscPlanDtl" />
<PrivacyConsentSection />
<OutlineFormSection />
<BizCstMatrixFormSection />
<FnrsCstFormSection />
<TmplntFormSection />
<ChecklistFormSection docSeComCd="SEWER" fieldPrefix="sewerBscPlan" />
<AttachmentsFormSection
docSeComCd="SEWER"
fieldPrefix="sewerBscPlanDtl"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
{isEditMode && <SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />}
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/sewer-bsc-plan")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={handleApplyClick}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
{initialData?.detDto?.sewerBscPlanDocNo && (
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={initialData?.sttsChgHstryList ?? []}
elaprDocNo={initialData.detDto.sewerBscPlanDocNo}
/>
)}
<BasicInfoFormSection />
<ContactInfoFormSection fieldPrefix="sewerBscPlanDtl" />
<PrivacyConsentSection />
<OutlineFormSection />
<BizCstMatrixFormSection />
<FnrsCstFormSection />
<TmplntFormSection />
<ChecklistFormSection docSeComCd="SEWER" fieldPrefix="sewerBscPlan" />
<AttachmentsFormSection
docSeComCd="SEWER"
fieldPrefix="sewerBscPlanDtl"
initialEsntlFiles={isEditMode ? initialData?.esntlFileList : undefined}
initialEtcFiles={isEditMode ? initialData?.etcFileList : undefined}
/>
{isEditMode && <SplmntDmndFormSection readOnly={!isSplmntPrgrsStts} />}
<div className="sticky bottom-0 z-40 !-mb-2 border-t bg-white pb-8 pt-6 shadow-[0_-4px_12px_rgba(0,0,0,0.06)]">
<div className="mx-4 flex flex-wrap justify-center gap-3 sm:gap-4">
<Button
color="white"
type="button"
onClick={() => router.push("/sewer-bsc-plan")}
disabled={isSubmitting}
>
목록
</Button>
{!isSplmntPrgrsStts ? (
<>
<Button type="submit" disabled={isSubmitting}>
저장
</Button>
{isEditMode && (
<>
<Button
color="green"
type="button"
onClick={handleApplyClick}
disabled={isSubmitting}
>
신청완료
</Button>
<Button
color="red"
type="button"
onClick={handleDelClick}
disabled={isSubmitting}
>
삭제
</Button>
</>
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완완료
</Button>
)}
</div>
</div>
</div>
</div>
</SectionCollapsibleGroup>
</form>
</FormProvider>
);
@@ -3,10 +3,15 @@
import { useMemo, useRef } from "react";
import { useUserStore } from "@/shared/lib";
import { SectionCollapsibleGroup, SectionCollapsibleToggle } from "@/shared/ui";
import { ElaprDocPrgrsStatus } from "@/entities/elapr";
import { useRvwMnpwMngLoginUserList } from "@/entities/rvw-mnpw";
import { useSewerBscPlanDetails } from "@/entities/sewer-bsc-plan";
import { AttachmentsViewSection, ProgressStatusSection, SupplementDemandSection } from "@/widgets/pbadms-sprt";
import {
AttachmentsViewSection,
ProgressStatusSection,
SupplementDemandSection,
} from "@/widgets/pbadms-sprt";
import { Skeleton } from "@common/components";
import { needsRoleUser01AtrzResolve } from "../lib/button-config";
@@ -95,48 +100,53 @@ function SewerBscPlanViewPage({ id, propChgCycl }: SewerBscPlanViewPageProps) {
);
return (
<div className="mt-6 space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.sewerBscPlanDocNo}
/>
<BasicInfoSection det={detDto} dtl={activeDtlDto} />
<ContactInfoSection dtl={activeDtlDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.sewerBscPlanDocNo} />
<OutlineSection otlnList={otlnList} dtl={activeDtlDto} />
<BizCstMatrixSection bizCstList={bizCstList} dtl={activeDtlDto} />
<FnrsCstSection fnrsCstList={fnrsCstList} dtl={activeDtlDto} />
<TmplntSection tmplntList={tmplntList} det={detDto} dtl={activeDtlDto} />
<ChecklistSection
cklstResultList={cklstResultRows}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwOpnnList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="SEWER"
sewerBscPlanDocNo={detDto.sewerBscPlanDocNo}
chgCycl={chgCycl}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
<div className="mt-4">
<SectionCollapsibleGroup>
<SectionCollapsibleToggle />
<div className="space-y-4">
<ProgressStatusSection
columns={PROGRESS_STATUS_COLUMNS}
sttsChgHstryList={sttsChgHstryList}
elaprDocNo={detDto.sewerBscPlanDocNo}
/>
<BasicInfoSection det={detDto} dtl={activeDtlDto} />
<ContactInfoSection dtl={activeDtlDto} />
<ElaprDocPrgrsStatus elaprDocNo={detDto.sewerBscPlanDocNo} />
<OutlineSection otlnList={otlnList} dtl={activeDtlDto} />
<BizCstMatrixSection bizCstList={bizCstList} dtl={activeDtlDto} />
<FnrsCstSection fnrsCstList={fnrsCstList} dtl={activeDtlDto} />
<TmplntSection tmplntList={tmplntList} det={detDto} dtl={activeDtlDto} />
<ChecklistSection
cklstResultList={cklstResultRows}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
userRoleCode={userInfo?.dtlInstCd}
formState={checklistFormState}
setFormState={setChecklistFormState}
/>
<AttachmentsViewSection esntlFileList={esntlFileList} etcFileList={etcFileList} />
<SupplementDemandSection splmntDmndList={splmntDmndList} />
<TechReviewOpinionSection
ref={techReviewOpinionRef}
rvwList={rvwOpnnList}
editableReviewStep={editableReviewStep}
atrzSeComCdList={atrzSeComCdList}
isAtrzLoading={needsAtrzResolve && isRvwLoginUserLoading}
/>
<FloatingActionButtons
data={data}
docSeComCd="SEWER"
sewerBscPlanDocNo={detDto.sewerBscPlanDocNo}
chgCycl={chgCycl}
prgrsSttsComCd={detDto.prgrsSttsComCd || ""}
dtlInstCd={userInfo?.dtlInstCd}
atrzSeComCdList={atrzSeComCdList}
isRvwLoginUserLoading={needsAtrzResolve && isRvwLoginUserLoading}
checklistFormState={checklistFormState}
techReviewOpinionRef={techReviewOpinionRef}
opnrNm={userInfo?.userName}
/>
</div>
</SectionCollapsibleGroup>
</div>
);
}
+4
View File
@@ -1,5 +1,9 @@
export { NoData } from "./no-data";
export { PageLayout } from "./page-layout";
export { SectionCollapsible } from "./section-collapsible";
export {
SectionCollapsibleGroup,
SectionCollapsibleToggle,
} from "./section-collapsible-group";
export type { TextShimmerProps } from "./text-shimmer";
export { TEXT_SHIMMER_CLASS, TextShimmer } from "./text-shimmer";
@@ -0,0 +1,89 @@
"use client";
import { createContext, type ReactNode, useCallback, useContext, useMemo, useState } from "react";
import { ChevronsDownUp, ChevronsUpDown } from "lucide-react";
import { Button } from "@common/components";
interface SectionCollapsibleGroupValue {
batchOpen: boolean;
batchVersion: number;
expandAll: () => void;
collapseAll: () => void;
}
const SectionCollapsibleGroupContext = createContext<SectionCollapsibleGroupValue | null>(null);
function useSectionCollapsibleGroup() {
return useContext(SectionCollapsibleGroupContext);
}
interface SectionCollapsibleGroupProps {
children: ReactNode;
}
function SectionCollapsibleGroup({ children }: SectionCollapsibleGroupProps) {
const [batch, setBatch] = useState({ open: true, version: 0 });
const expandAll = useCallback(() => {
setBatch((prev) => ({ open: true, version: prev.version + 1 }));
}, []);
const collapseAll = useCallback(() => {
setBatch((prev) => ({ open: false, version: prev.version + 1 }));
}, []);
const value = useMemo(
() => ({
batchOpen: batch.open,
batchVersion: batch.version,
expandAll,
collapseAll,
}),
[batch.open, batch.version, expandAll, collapseAll],
);
return (
<SectionCollapsibleGroupContext.Provider value={value}>
{children}
</SectionCollapsibleGroupContext.Provider>
);
}
function SectionCollapsibleToggle() {
const group = useSectionCollapsibleGroup();
if (!group) return null;
const { batchOpen, expandAll, collapseAll } = group;
return (
<div className="sticky top-[6rem] z-30 flex justify-start gap-2 bg-container px-4 py-2">
<Button
type="button"
size="sm"
color="white"
className="gap-1 px-4 tracking-tight"
aria-expanded={batchOpen}
onClick={collapseAll}
>
<ChevronsDownUp className="shrink-0" />
전체접기
</Button>
<Button
type="button"
size="sm"
color="white"
className="gap-1 px-4 tracking-tight"
aria-expanded={batchOpen}
onClick={expandAll}
>
<ChevronsUpDown className="shrink-0" />
전체펼침
</Button>
</div>
);
}
export { SectionCollapsibleGroup, SectionCollapsibleToggle, useSectionCollapsibleGroup };
+15 -2
View File
@@ -1,11 +1,13 @@
"use client";
import type { ReactNode } from "react";
import { type ReactNode, useState } from "react";
import { ChevronDown } from "lucide-react";
import { cn, Collapsible, CollapsibleContent, CollapsibleTrigger } from "@common/components";
import { useSectionCollapsibleGroup } from "./section-collapsible-group";
interface SectionCollapsibleProps {
title: ReactNode;
defaultOpen?: boolean;
@@ -13,8 +15,19 @@ interface SectionCollapsibleProps {
}
function SectionCollapsible({ title, defaultOpen = true, children }: SectionCollapsibleProps) {
const group = useSectionCollapsibleGroup();
const [open, setOpen] = useState(group?.batchOpen ?? defaultOpen);
const [seenVersion, setSeenVersion] = useState(group?.batchVersion ?? 0);
if (group && group.batchVersion !== seenVersion) {
setSeenVersion(group.batchVersion);
setOpen(group.batchOpen);
}
const collapsibleProps = group ? { open, onOpenChange: setOpen } : { defaultOpen };
return (
<Collapsible defaultOpen={defaultOpen} className="mx-4 rounded-sm border bg-background">
<Collapsible {...collapsibleProps} className="mx-4 rounded-sm border bg-background">
<CollapsibleTrigger asChild>
<button
type="button"
+11
View File
@@ -1217,6 +1217,9 @@ importers:
dayjs:
specifier: ^1.11.13
version: 1.11.20
decimal.js:
specifier: ^10.6.0
version: 10.6.0
js-cookie:
specifier: ^3.0.5
version: 3.0.5
@@ -1511,6 +1514,9 @@ importers:
'@radix-ui/react-slot':
specifier: ^1.1.2
version: 1.2.4(@types/react@19.2.14)(react@19.2.4)
'@tanstack/react-table':
specifier: ^8.21.2
version: 8.21.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
axios:
specifier: 1.8.1
version: 1.8.1
@@ -4886,6 +4892,9 @@ packages:
decimal.js-light@2.5.1:
resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==}
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
@@ -10599,6 +10608,8 @@ snapshots:
decimal.js-light@2.5.1: {}
decimal.js@10.6.0: {}
deep-eql@5.0.2: {}
deep-is@0.1.4: {}