[as] feat: UploadedFileChip 컴포넌트 props 추가(onDownload)

This commit is contained in:
2026-09-18 17:33:45 +09:00
parent f40d6fd5e8
commit 110d6c689c
10 changed files with 90 additions and 29 deletions
@@ -5,7 +5,7 @@ import { useForm } from "react-hook-form";
import { getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { attachService, useDownloadAttachmentByAtflId } from "@/entities/attach";
import { CodeSelect } from "@/entities/code";
import type { EsntlAtchFileMng } from "@/entities/esntl";
import {
@@ -61,6 +61,7 @@ function EsntlAtchFileFormDialog({
const formId = useId();
const atchFileGroupIdRef = useRef("");
const [uploadedFiles, setUploadedFiles] = useState<UploadedTemplateFile[]>([]);
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
const { register, handleSubmit, reset, setValue, watch } = useForm<EsntlAtchFileFormValues>({
defaultValues: {
@@ -219,6 +220,9 @@ function EsntlAtchFileFormDialog({
files={uploadedFiles.map((item) => ({
fileKey: item.fileKey,
fileName: item.fileName,
onDownload: item.atflId
? () => downloadByAtflId(item.atflId!)
: undefined,
}))}
inputId={fileInputId}
isUploadingByKey={isUploading}
@@ -597,7 +597,7 @@ function FloatingActionButtons({
</Button>
)}
{shouldShowButton(FNRS_CNSLTN_VIEW_BUTTON_ACTION.sncnAprv) && (
<Button onClick={handleSncnAprvClick} disabled={isProcessing}>
<Button color="green" onClick={handleSncnAprvClick} disabled={isProcessing}>
결과 회신
</Button>
)}
@@ -8,7 +8,7 @@ import { PanelRightClose, PanelRightOpen } from "lucide-react";
import { getDefaultFileKey, useAlertStore, useTus } from "@/shared/lib";
import { AttachmentFileField } from "@/shared/ui";
import { attachService, parseFilenameFromResponse } from "@/entities/attach";
import { attachService, parseFilenameFromResponse, useDownloadAttachmentByAtflId } from "@/entities/attach";
import {
pwrkCnsltnDocCrtRsltSaveRequestSchema,
pwrkCnsltnDocCrtRsltService,
@@ -78,6 +78,7 @@ function PwrkCnsltnDocMngDialog({
const [atchFileGroupId, setAtchFileGroupId] = useState("");
const [uploadedFiles, setUploadedFiles] = useState<UploadedAtchFile[]>([]);
const [isPjtcoAjmtTableOpen, setIsPjtcoAjmtTableOpen] = useState(false);
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
const {
data: pwrkCnsltnData,
@@ -344,6 +345,9 @@ function PwrkCnsltnDocMngDialog({
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}
@@ -332,24 +332,26 @@ function FnrsFormPage({ mode, initialData, createDefaults }: FnrsFormPageProps)
)}
</>
) : (
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완 완료
</Button>
<>
{fnrsCnsltnDocNo ? (
<Button
color="orange"
type="button"
onClick={() => setIsPwrkCnsltnDocMngOpen(true)}
disabled={isSubmitting}
>
실무협의서 관리
</Button>
) : null}
<Button
type="button"
onClick={form.handleSubmit(onSplmntCmptn)}
disabled={isSubmitting}
>
보완 완료
</Button>
</>
)}
{isSplmntPrgrsStts && fnrsCnsltnDocNo ? (
<Button
color="orange"
type="button"
onClick={() => setIsPwrkCnsltnDocMngOpen(true)}
disabled={isSubmitting}
>
실무협의서 관리
</Button>
) : null}
</div>
</div>
</div>
@@ -5,7 +5,11 @@ import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField, SectionCollapsible } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import {
AttachmentDownloadLink,
attachService,
useDownloadAttachmentByAtflId,
} from "@/entities/attach";
import type { FnrsCnsltnRvwOpnnResponseDto } from "@/entities/fnrs-cnsltn";
import {
RadioGroup,
@@ -79,6 +83,7 @@ const TechReviewOpinionSection = forwardRef<
opnrSeComCd: editableReviewStep ?? "",
});
const [uploadedFile, setUploadedFile] = useState<UploadedOpinionFile | null>(null);
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
useEffect(() => {
setFormData((prev) => ({
@@ -243,7 +248,15 @@ const TechReviewOpinionSection = forwardRef<
<AttachmentFileField
files={
uploadedFile
? [{ fileKey: uploadedFile.fileKey, fileName: uploadedFile.fileName }]
? [
{
fileKey: uploadedFile.fileKey,
fileName: uploadedFile.fileName,
onDownload: uploadedFile.atflId
? () => downloadByAtflId(uploadedFile.atflId!)
: undefined,
},
]
: []
}
inputId={`rvw-opnn-${reviewStep}-file-input`}
@@ -6,6 +6,7 @@ import { UploadedFileChip } from "./uploaded-file-chip";
interface AttachmentFileFieldItem {
fileKey: string;
fileName: string;
onDownload?: () => void;
}
interface AttachmentFileFieldProps {
@@ -41,6 +42,7 @@ function AttachmentFileField({
fileName={file.fileName}
isUploading={isUploadingByKey[file.fileKey] ?? false}
onRemove={onRemove ? () => onRemove(index) : undefined}
onDownload={file.onDownload}
/>
))}
</div>
+25 -4
View File
@@ -8,15 +8,36 @@ interface UploadedFileChipProps {
fileName: string;
isUploading?: boolean;
onRemove?: () => void;
onDownload?: () => void;
}
function UploadedFileChip({ fileName, isUploading = false, onRemove }: UploadedFileChipProps) {
function UploadedFileChip({
fileName,
isUploading = false,
onRemove,
onDownload,
}: UploadedFileChipProps) {
const canDownload = Boolean(onDownload) && !isUploading;
return (
<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}
</TextShimmer>
{canDownload ? (
<button
type="button"
className="cursor-pointer text-left hover:underline"
aria-label={`${fileName} 다운로드`}
onClick={onDownload}
>
<TextShimmer className="text-xl" active={isUploading}>
{fileName}
</TextShimmer>
</button>
) : (
<TextShimmer className="text-xl" active={isUploading}>
{fileName}
</TextShimmer>
)}
{onRemove ? (
<button
type="button"
@@ -15,7 +15,7 @@ import {
SectionCollapsible,
UploadedFileChip,
} from "@/shared/ui";
import { attachService, useDownloadAttachmentByAtflCd } from "@/entities/attach";
import { attachService, useDownloadAttachmentByAtflCd, useDownloadAttachmentByAtflId } from "@/entities/attach";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
Button,
@@ -217,6 +217,7 @@ function AttachmentsFormSection({
{ docSeComCd },
);
const { mutate: downloadTemplate } = useDownloadAttachmentByAtflCd();
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
async function invalidateAttachmentQueries() {
await queryClient.invalidateQueries({
@@ -431,6 +432,9 @@ function AttachmentsFormSection({
files={(uploadedFiles[file.esntlAtchFileId] ?? []).map((item) => ({
fileKey: item.fileKey,
fileName: item.file?.name ?? item.fileName,
onDownload: item.atflId
? () => downloadByAtflId(item.atflId!)
: undefined,
}))}
inputId={`${file.esntlAtchFileId}-file-input`}
isUploadingByKey={isUploading}
@@ -470,6 +474,9 @@ function AttachmentsFormSection({
fileName={item.file?.name ?? item.fileName}
isUploading={isUploading[item.fileKey] ?? false}
onRemove={() => void removeEtcFile(idx)}
onDownload={
item.atflId ? () => downloadByAtflId(item.atflId!) : undefined
}
/>
))}
</div>
@@ -4,7 +4,7 @@ import { useEffect, useId, useState } from "react";
import { getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { attachService, useDownloadAttachmentByAtflId } from "@/entities/attach";
import { CodeSelect } from "@/entities/code";
import {
Button,
@@ -96,6 +96,7 @@ function SplmntDmndDialog({
}: SplmntDmndDialogProps) {
const formId = useId();
const [rows, setRows] = useState<SplmntDmndRow[]>([createEmptyRow()]);
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
const { uploadFileAsync, isUploading, uploads } = useTus({
onSuccess: (atflCd, atflId, _file, fileKey) => {
@@ -289,6 +290,9 @@ function SplmntDmndDialog({
files={row.uploadedFiles.map((file) => ({
fileKey: file.fileKey,
fileName: file.fileName,
onDownload: file.atflId
? () => downloadByAtflId(file.atflId!)
: undefined,
}))}
inputId={fileInputId}
isUploadingByKey={isUploading}
@@ -5,7 +5,7 @@ import { Controller, useFormContext, useWatch } from "react-hook-form";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField, SectionCollapsible } from "@/shared/ui";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService, useDownloadAttachmentByAtflId } from "@/entities/attach";
import {
Badge,
Table,
@@ -73,6 +73,7 @@ function SplmntDmndFormSection({ readOnly = false }: SplmntDmndFormSectionProps)
const initialFilesMappedRef = useRef(false);
const isMountedRef = useRef(true);
const atflCdByRowRef = useRef<Record<string, string>>({});
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
const { uploadFileAsync, isUploading, uploads } = useTus({
onSuccess: (atflCd, atflId, _file, fileKey) => {
@@ -306,6 +307,9 @@ function SplmntDmndFormSection({ readOnly = false }: SplmntDmndFormSectionProps)
files={rowFiles.map((file) => ({
fileKey: file.fileKey,
fileName: file.fileName,
onDownload: file.atflId
? () => downloadByAtflId(file.atflId!)
: undefined,
}))}
inputId={fileInputId}
isUploadingByKey={isUploading}