[as] chore: 행정지원 메뉴 수정

This commit is contained in:
2026-08-05 17:18:38 +09:00
parent 1d8d9c7b3d
commit 4aa2d0a62c
18 changed files with 108 additions and 72 deletions
@@ -6,6 +6,7 @@ import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import type { DesignCnstnRvwOpnnResponseDto } from "@/entities/design-cnstn";
@@ -240,7 +241,9 @@ const TechReviewOpinionSection = forwardRef<
<TableCell>
{uploadedFile ? (
<div className="flex items-center gap-2">
<span className="text-xl">{uploadedFile.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{uploadedFile.fileName}
</TextShimmer>
<Button
size="icon"
onClick={(e) => {
@@ -46,12 +46,7 @@ function ChecklistTaskPanel({
.forEach((row) => {
nextResults[buildChecklistRowKey(row, selectedReviewStep)] = value;
});
return patchChecklistFormState(
prev,
{ results: nextResults },
allRows,
selectedReviewStep,
);
return patchChecklistFormState(prev, { results: nextResults }, allRows, selectedReviewStep);
});
}
@@ -77,6 +72,7 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="white"
disabled={!isStepEditable}
onClick={() => handleSelectAll("Y")}
>
@@ -87,9 +83,9 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="gray"
disabled={!isStepEditable}
onClick={() => handleSelectAll("N")}
className="!bg-destructive"
>
전체 아니오
</Button>
@@ -6,6 +6,7 @@ import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import type { FnrsCnsltnRvwOpnnResponseDto } from "@/entities/fnrs-cnsltn";
@@ -247,7 +248,9 @@ const TechReviewOpinionSection = forwardRef<
<TableCell>
{uploadedFile ? (
<div className="flex items-center gap-2">
<span className="text-xl">{uploadedFile.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{uploadedFile.fileName}
</TextShimmer>
<Button
size="icon"
onClick={(e) => {
@@ -45,12 +45,7 @@ function ChecklistTaskPanel({
.forEach((row) => {
nextResults[buildChecklistRowKey(row, selectedReviewStep)] = value;
});
return patchChecklistFormState(
prev,
{ results: nextResults },
allRows,
selectedReviewStep,
);
return patchChecklistFormState(prev, { results: nextResults }, allRows, selectedReviewStep);
});
}
@@ -76,6 +71,7 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="white"
disabled={!isStepEditable}
onClick={() => handleSelectAll("Y")}
>
@@ -86,9 +82,9 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="gray"
disabled={!isStepEditable}
onClick={() => handleSelectAll("N")}
className="!bg-destructive"
>
전체 아니오
</Button>
@@ -6,6 +6,7 @@ import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import type { InstlSncnRvwOpnnResponseDto, ReviewStepCode } from "@/entities/instl-sncn";
@@ -240,7 +241,9 @@ const TechReviewOpinionSection = forwardRef<
<TableCell>
{uploadedFile ? (
<div className="flex items-center gap-2">
<span className="text-xl">{uploadedFile.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{uploadedFile.fileName}
</TextShimmer>
<Button
size="icon"
onClick={(e) => {
@@ -49,23 +49,12 @@ export const REVIEW_STEP_LABELS: Record<ReviewStepCode, string> = {
};
export const TASK_CODE_LABELS: Record<string, string> = {
RE: "재이용수",
SP: "하수관로",
SF: "하수처리시설",
MP: "변경",
CP: "신규",
CP: "부분변경",
AP: "전체변경",
MP: "병합",
};
export const PBADMS_PROGRESS_ORDER = [
"SV",
"SL",
"TA",
"EP",
"LE",
"TP",
"ER",
"TE",
] as const;
export const PBADMS_PROGRESS_ORDER = ["SV", "SL", "TA", "EP", "LE", "TP", "ER", "TE"] as const;
export const ROLE_TO_REVIEW_STEP: Record<string, ReviewStepCode> = {
ROLE_USER05: "LOCAL",
@@ -71,6 +71,7 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="white"
disabled={!isStepEditable}
onClick={() => handleSelectAll("Y")}
>
@@ -81,9 +82,9 @@ function ChecklistTaskPanel({
<Button
type="button"
size="sm"
color="gray"
disabled={!isStepEditable}
onClick={() => handleSelectAll("N")}
className="!bg-destructive"
>
전체 아니오
</Button>
@@ -6,6 +6,7 @@ import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import type { SewerBscPlanRvwOpnnResponseDto } from "@/entities/sewer-bsc-plan";
@@ -247,7 +248,9 @@ const TechReviewOpinionSection = forwardRef<
<TableCell>
{uploadedFile ? (
<div className="flex items-center gap-2">
<span className="text-xl">{uploadedFile.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{uploadedFile.fileName}
</TextShimmer>
<Button
size="icon"
onClick={(e) => {
@@ -6,6 +6,7 @@ import { Controller, useFieldArray, useFormContext, useWatch } from "react-hook-
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import { useFacilityList } from "@/entities/bns-if";
@@ -319,7 +320,9 @@ function TmplntFacilityAttachments({
<TableCell>
{hasFile ? (
<div className="flex items-center gap-2">
<span className="text-xl">{displayFileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{displayFileName}
</TextShimmer>
<Button
type="button"
size="icon"
+2 -8
View File
@@ -1,12 +1,6 @@
"use client";
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
} from "@common/components";
import { Button, Dialog, DialogClose, DialogContent, DialogFooter } from "@common/components";
import { useAlertStore } from "../lib/use-alert-store";
@@ -20,7 +14,7 @@ function AlertDialog() {
<Dialog open={!!message} onOpenChange={handleOpenChange}>
<DialogContent aria-describedby={undefined}>
<div className="py-10">
<p className="text-foreground whitespace-pre-line text-center text-[1.4rem]">
<p className="whitespace-pre-line break-keep text-center text-[1.4rem] text-foreground">
{message}
</p>
</div>
+3 -1
View File
@@ -1,2 +1,4 @@
export { PageLayout } from "./page-layout";
export { NoData } from "./no-data";
export { PageLayout } from "./page-layout";
export type { TextShimmerProps } from "./text-shimmer";
export { TEXT_SHIMMER_CLASS, TextShimmer } from "./text-shimmer";
+19
View File
@@ -0,0 +1,19 @@
import { cn } from "@common/components";
const TEXT_SHIMMER_CLASS =
"animate-text-shimmer bg-gradient-to-r from-muted-foreground via-muted to-muted-foreground bg-[length:200%_100%] bg-clip-text text-transparent";
interface TextShimmerProps extends React.ComponentPropsWithoutRef<"span"> {
active?: boolean;
}
function TextShimmer({ active = false, className, children, ...props }: TextShimmerProps) {
return (
<span className={cn(className, active && TEXT_SHIMMER_CLASS)} {...props}>
{children}
</span>
);
}
export { TEXT_SHIMMER_CLASS, TextShimmer };
export type { TextShimmerProps };
@@ -8,6 +8,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { ENDPOINT } from "@/shared/config";
import { useTus } from "@/shared/lib";
import type { AtflMngClientResponseDto, EsntlAtchFileMngTreeResponseDto } from "@/shared/model";
import { TextShimmer } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
@@ -413,20 +414,21 @@ function AttachmentsFormSection({
return (
<div key={item.fileKey} className="flex items-center gap-2">
<span className="text-xl">
<TextShimmer className="text-xl" active={uploading}>
{item.file?.name ?? item.fileName}
</span>
</TextShimmer>
{!uploading && (
<Button
size="icon"
onClick={(e) => {
e.preventDefault();
void removeEssentialFile(file.esntlAtchFileId, idx);
}}
disabled={uploading}
className="size-8 !bg-destructive"
>
<i className="diveicon di-x text-xs" aria-label="삭제" />
</Button>
)}
</div>
);
})}
@@ -489,7 +491,9 @@ function AttachmentsFormSection({
key={item.fileKey}
className="flex items-center justify-between rounded-sm border p-2"
>
<span className="px-2 text-xl">{item.file?.name ?? item.fileName}</span>
<TextShimmer className="px-2 text-xl" active={uploading}>
{item.file?.name ?? item.fileName}
</TextShimmer>
<Button
size="sm"
color="gray"
@@ -25,7 +25,11 @@ import {
TabsTrigger,
} from "@common/components";
import { buildChecklistTabs, normalizeTaskSeComCdArray, syncCklstResultListFromTree } from "../lib/checklist-form-utils";
import {
buildChecklistTabs,
normalizeTaskSeComCdArray,
syncCklstResultListFromTree,
} from "../lib/checklist-form-utils";
import { SectionCollapsible } from "./section-collapsible";
@@ -49,10 +53,7 @@ function ChecklistFormSection({ docSeComCd, fieldPrefix }: ChecklistFormSectionP
const { watch, setValue, control, getValues } = useFormContext();
const [activeTab, setActiveTab] = useState("");
const taskSeComCd = watch(`${fieldPrefix}.taskSeComCd`) as string | string[] | undefined;
const taskSeComCdArray = useMemo(
() => normalizeTaskSeComCdArray(taskSeComCd),
[taskSeComCd],
);
const taskSeComCdArray = useMemo(() => normalizeTaskSeComCdArray(taskSeComCd), [taskSeComCd]);
const cklstResultList =
(useWatch({ control, name: "cklstResultList" }) as CklstResultListItem[] | undefined) ?? [];
@@ -206,6 +207,7 @@ function ChecklistFormSection({ docSeComCd, fieldPrefix }: ChecklistFormSectionP
<Button
type="button"
size="sm"
color="white"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "Y");
@@ -218,11 +220,11 @@ function ChecklistFormSection({ docSeComCd, fieldPrefix }: ChecklistFormSectionP
<Button
type="button"
size="sm"
color="gray"
onClick={(e) => {
e.preventDefault();
handleSelectAll(tab.code, "N");
}}
className="!bg-destructive"
>
전체 아니오
</Button>
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { Field } from "@/shared/ui/field";
import { useAcntgSeMngTree } from "@/entities/acntg";
import type { AtflMngClientResponseDto } from "@/entities/fnrs-cnsltn";
@@ -257,12 +258,18 @@ function FnrsSharFormSection({ initialSharTree, initialSharAtchFiles }: FnrsShar
<div className="mt-6 px-2 pb-2">
<h3 className="mb-2 text-xl font-semibold">첨부파일</h3>
<div className="flex flex-wrap items-center gap-2">
{isSharUploading && uploadedFileName ? (
<TextShimmer className="max-w-lg text-xl" active>
{uploadedFileName}
</TextShimmer>
) : (
<Input
value={uploadedFileName}
readOnly
placeholder="총사업비 신청서(.hwp) 파일을 등록해주세요."
className="max-w-lg rounded-sm"
/>
)}
<Button type="button" color="white" disabled={isSharUploading} asChild>
<label className="cursor-pointer">
첨부파일
@@ -3,6 +3,7 @@
import { useEffect, useId, useState } from "react";
import { getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { CodeSelect } from "@/entities/code";
import {
@@ -269,7 +270,9 @@ function SplmntDmndDialog({
<div className="flex items-center gap-2">
{row.uploadedFile ? (
<div className="flex items-stretch gap-2">
<span className="text-xl">{row.uploadedFile.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{row.uploadedFile.fileName}
</TextShimmer>
<Button
type="button"
size="icon"
@@ -6,6 +6,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form";
import { Paperclip } from "lucide-react";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { attachService, useDownloadAttachmentByAtflCd } from "@/entities/attach";
import {
Badge,
@@ -272,7 +273,9 @@ function SplmntDmndFormSection({ readOnly = false }: SplmntDmndFormSectionProps)
{uploaded.fileName}
</a>
) : (
<span className="text-xl">{uploaded.fileName}</span>
<TextShimmer className="text-xl" active={uploading}>
{uploaded.fileName}
</TextShimmer>
)}
{!isRowReadOnly ? (
<Button
+5
View File
@@ -83,12 +83,17 @@ export default {
from: { transform: "translate(0,0) rotate(0deg)" },
to: { transform: "translate(0,0) rotate(360deg)" },
},
"text-shimmer": {
"0%": { backgroundPosition: "200% center" },
"100%": { backgroundPosition: "-200% center" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"spin-parent": "spin-parent 6s linear infinite",
"spin-child": "spin-child 1s ease-in-out infinite",
"text-shimmer": "text-shimmer 1.5s linear infinite",
},
backgroundImage: {
"gradient-blue": "linear-gradient(.25turn, #2094fa, #00e5dd)",