[as] feat: AttachmentDownloadLink 컴포넌트 구현 및 적용

This commit is contained in:
2026-09-18 16:51:00 +09:00
parent cc3bc548c9
commit f40d6fd5e8
10 changed files with 109 additions and 109 deletions
+4
View File
@@ -6,3 +6,7 @@ export {
} from "./api/queries";
export { parseFilenameFromResponse } from "./lib/parse-filename-from-response";
export type { Attach, AttachService } from "./model/attach-interface";
export {
AttachmentDownloadLink,
type AttachmentDownloadLinkProps,
} from "./ui/attachment-download-link";
@@ -0,0 +1,54 @@
"use client";
import { Paperclip } from "lucide-react";
import { cn } from "@common/components";
import { useDownloadAttachmentByAtflCd, useDownloadAttachmentByAtflId } from "../api/queries";
interface AttachmentDownloadLinkProps {
atflCd?: string;
atflId?: string;
fileName?: string;
className?: string;
}
function AttachmentDownloadLink({
atflCd,
atflId,
fileName,
className,
}: AttachmentDownloadLinkProps) {
const downloadByAtflCd = useDownloadAttachmentByAtflCd();
const downloadByAtflId = useDownloadAttachmentByAtflId();
const displayName = fileName || "첨부파일";
const canDownload = Boolean(atflId || atflCd);
const isPending = atflId ? downloadByAtflId.isPending : downloadByAtflCd.isPending;
function handleClick() {
if (atflId) {
downloadByAtflId.mutate(atflId);
return;
}
if (atflCd) downloadByAtflCd.mutate(atflCd);
}
return (
<button
type="button"
className={cn(
"inline-flex cursor-pointer items-start gap-1 text-left text-blue-600 hover:underline disabled:cursor-wait disabled:opacity-60",
className,
)}
aria-label={`${displayName} 다운로드`}
disabled={isPending || !canDownload}
onClick={handleClick}
>
<Paperclip className="my-1 size-5 shrink-0" />
{displayName}
</button>
);
}
export { AttachmentDownloadLink };
export type { AttachmentDownloadLinkProps };
@@ -2,14 +2,11 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { SectionCollapsible } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import type { DesignCnstnRvwOpnnResponseDto } from "@/entities/design-cnstn";
import {
Button,
@@ -319,15 +316,10 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) {
</TableCell>
<TableCell align="center">
{item.atchFileInfo?.atflCd ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-left text-blue-600 hover:underline"
href={`${PROXY_HOST}/attach-service/api/v1/atflcd/download/${item.atchFileInfo.atflCd}`}
target="_blank"
rel="noreferrer"
>
<Paperclip className="my-1 size-5 shrink-0" />
{item.atchFileInfo.orgnlFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.atchFileInfo.atflCd}
fileName={item.atchFileInfo.orgnlFileNm || "다운로드"}
/>
) : (
"-"
)}
@@ -2,13 +2,10 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField, SectionCollapsible } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import type { FnrsCnsltnRvwOpnnResponseDto } from "@/entities/fnrs-cnsltn";
import {
RadioGroup,
@@ -320,15 +317,10 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) {
</TableCell>
<TableCell align="center">
{item.atchFileInfo?.atflCd ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-left text-blue-600 hover:underline"
href={`${PROXY_HOST}/attach-service/api/v1/atflcd/download/${item.atchFileInfo.atflCd}`}
target="_blank"
rel="noreferrer"
>
<Paperclip className="my-1 size-5 shrink-0" />
{item.atchFileInfo.orgnlFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.atchFileInfo.atflCd}
fileName={item.atchFileInfo.orgnlFileNm || "다운로드"}
/>
) : (
"-"
)}
@@ -2,14 +2,11 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { SectionCollapsible } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import type { InstlSncnRvwOpnnResponseDto, ReviewStepCode } from "@/entities/instl-sncn";
import {
Button,
@@ -331,15 +328,10 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) {
</TableCell>
<TableCell align="center">
{item.atchFileInfo?.atflCd ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-left text-blue-600 hover:underline"
href={`${PROXY_HOST}/attach-service/api/v1/atflcd/download/${item.atchFileInfo.atflCd}`}
target="_blank"
rel="noreferrer"
>
<Paperclip className="my-1 size-5 shrink-0" />
{item.atchFileInfo.orgnlFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.atchFileInfo.atflCd}
fileName={item.atchFileInfo.orgnlFileNm || "다운로드"}
/>
) : (
"-"
)}
@@ -2,14 +2,11 @@
import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
import { Paperclip } from "lucide-react";
import { PROXY_HOST } from "@/shared/config";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { TextShimmer } from "@/shared/ui";
import { SectionCollapsible } from "@/shared/ui";
import { Field, FieldLabel, FieldSet } from "@/shared/ui/field";
import { attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import type { SewerBscPlanRvwOpnnResponseDto } from "@/entities/sewer-bsc-plan";
import {
Button,
@@ -338,15 +335,10 @@ function TechReviewOpinionTable({ items }: TechReviewOpinionTableProps) {
</TableCell>
<TableCell align="center">
{item.atchFileInfo?.atflCd ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-left text-blue-600 hover:underline"
href={`${PROXY_HOST}/attach-service/api/v1/atflcd/download/${item.atchFileInfo.atflCd}`}
target="_blank"
rel="noreferrer"
>
<Paperclip className="my-1 size-5 shrink-0" />
{item.atchFileInfo.orgnlFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.atchFileInfo.atflCd}
fileName={item.atchFileInfo.orgnlFileNm || "다운로드"}
/>
) : (
"-"
)}
@@ -2,10 +2,8 @@
import { useMemo, useState } from "react";
import { Paperclip } from "lucide-react";
import { SectionCollapsible } from "@/shared/ui";
import { useDownloadAttachmentByAtflCd } from "@/entities/attach";
import { AttachmentDownloadLink } from "@/entities/attach";
import { useFacilityList } from "@/entities/bns-if";
import { useNowDmgMtncGnsvy } from "@/entities/dc";
import type {
@@ -176,7 +174,6 @@ function TmplntFacilityMatrix({
}
function TmplntSection({ tmplntList, det, dtl }: TmplntSectionProps) {
const { mutate: downloadAttachment } = useDownloadAttachmentByAtflCd();
const [selectedCode, setSelectedCode] = useState(tmplntList[0]?.swtrmtFcltCd ?? "");
const { data: facilityListData } = useFacilityList(
{ sido: det.sido ?? "", gugun: det.gugun ?? "" },
@@ -261,13 +258,11 @@ function TmplntSection({ tmplntList, det, dtl }: TmplntSectionProps) {
<TableCell>{facilityName} 계획평면도</TableCell>
<TableCell>
{item.atchFileGroupId ? (
<a
className="inline-flex cursor-pointer items-center gap-1 text-blue-600 hover:underline"
onClick={() => downloadAttachment(item.atchFileGroupId ?? "")}
>
<Paperclip className="size-5 shrink-0" />
{item.atchFileInfo?.orgnlFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.atchFileGroupId}
fileName={item.atchFileInfo?.orgnlFileNm || "다운로드"}
className="items-center"
/>
) : (
"-"
)}
@@ -1,11 +1,9 @@
"use client";
import { Paperclip } from "lucide-react";
import { formatFileSize } from "@/shared/lib/file-utils";
import type { AtflMngClientResponseDto, EsntlAtchFileMngTreeResponseDto } from "@/shared/model";
import { SectionCollapsible } from "@/shared/ui";
import { useDownloadAttachmentByAtflCd, useDownloadAttachmentByAtflId } from "@/entities/attach";
import { AttachmentDownloadLink } from "@/entities/attach";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
interface AttachmentsViewSectionProps {
@@ -14,9 +12,6 @@ interface AttachmentsViewSectionProps {
}
function AttachmentsViewSection({ esntlFileList, etcFileList }: AttachmentsViewSectionProps) {
const { mutate: downloadByAtflCd } = useDownloadAttachmentByAtflCd();
const { mutate: downloadByAtflId } = useDownloadAttachmentByAtflId();
return (
<>
{esntlFileList.length > 0 && (
@@ -48,14 +43,12 @@ function AttachmentsViewSection({ esntlFileList, etcFileList }: AttachmentsViewS
) : (
<div className="space-y-2">
{files.map((file) => (
<a
<AttachmentDownloadLink
key={file.atflId}
className="flex cursor-pointer items-center gap-1 text-blue-600 hover:underline"
onClick={() => downloadByAtflId(file.atflId)}
>
<Paperclip className="size-5 shrink-0 text-blue-600" />
{file.orgnlFileNm || "다운로드"}
</a>
atflId={file.atflId}
fileName={file.orgnlFileNm || "다운로드"}
className="items-center"
/>
))}
</div>
)}
@@ -95,13 +88,11 @@ function AttachmentsViewSection({ esntlFileList, etcFileList }: AttachmentsViewS
<TableRow key={file.atflId}>
<TableCell className="text-left">
{file.atflCd ? (
<a
className="flex cursor-pointer items-center gap-1 text-blue-600 hover:underline"
onClick={() => downloadByAtflCd(file.atflCd)}
>
<Paperclip className="size-5 shrink-0 text-blue-600" />
{file.orgnlFileNm || file.physFileNm || file.atflId}
</a>
<AttachmentDownloadLink
atflCd={file.atflCd}
fileName={file.orgnlFileNm || file.physFileNm || file.atflId}
className="items-center"
/>
) : (
<span>{file.orgnlFileNm || file.physFileNm || file.atflId}</span>
)}
@@ -3,11 +3,9 @@
import { useEffect, useId, useRef, useState } from "react";
import { Controller, useFormContext, useWatch } from "react-hook-form";
import { Paperclip } from "lucide-react";
import { dayjs, getDefaultFileKey, useTus } from "@/shared/lib";
import { AttachmentFileField, SectionCollapsible } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { AttachmentDownloadLink, attachService } from "@/entities/attach";
import {
Badge,
Table,
@@ -274,10 +272,10 @@ function SplmntDmndFormSection({ readOnly = false }: SplmntDmndFormSectionProps)
<div className="space-y-2">
<div className="whitespace-pre-wrap">{item.splmntDmndCn || "-"}</div>
{item.splmntDmndAtchFileGroupId ? (
<span className="inline-flex items-start gap-1">
<Paperclip className="my-1 size-5 shrink-0" />
{item.splmntDmndAtchFileNm || "첨부파일"}
</span>
<AttachmentDownloadLink
atflCd={item.splmntDmndAtchFileGroupId}
fileName={item.splmntDmndAtchFileNm}
/>
) : null}
</div>
</TableCell>
@@ -1,11 +1,9 @@
"use client";
import { Paperclip } from "lucide-react";
import { dayjs } from "@/shared/lib";
import type { SplmntDmndResponseExtension, SplmntDmndSaveBase } from "@/shared/model";
import { SectionCollapsible } from "@/shared/ui";
import { useDownloadAttachmentByAtflCd } from "@/entities/attach";
import { AttachmentDownloadLink } from "@/entities/attach";
import {
Badge,
Table,
@@ -23,8 +21,6 @@ interface SupplementDemandSectionProps {
}
function SupplementDemandSection({ splmntDmndList }: SupplementDemandSectionProps) {
const { mutate: downloadAttachment } = useDownloadAttachmentByAtflCd();
if (splmntDmndList.length === 0) {
return null;
}
@@ -70,13 +66,10 @@ function SupplementDemandSection({ splmntDmndList }: SupplementDemandSectionProp
<div className="space-y-2">
<div className="whitespace-pre-wrap">{item.splmntDmndCn || "-"}</div>
{item.splmntDmndAtchFileGroupId ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-blue-600 hover:underline"
onClick={() => downloadAttachment(item.splmntDmndAtchFileGroupId!)}
>
<Paperclip className="my-1 size-5 shrink-0" />
{dmndAtchFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.splmntDmndAtchFileGroupId}
fileName={dmndAtchFileNm || "다운로드"}
/>
) : null}
</div>
</TableCell>
@@ -89,13 +82,10 @@ function SupplementDemandSection({ splmntDmndList }: SupplementDemandSectionProp
<div className="space-y-2">
<div className="whitespace-pre-wrap">{item.splmntCn || "-"}</div>
{item.splmntAtchFileGroupId ? (
<a
className="inline-flex cursor-pointer items-start gap-1 text-blue-600 hover:underline"
onClick={() => downloadAttachment(item.splmntAtchFileGroupId!)}
>
<Paperclip className="my-1 size-5 shrink-0" />
{cmptnAtchFileNm || "다운로드"}
</a>
<AttachmentDownloadLink
atflCd={item.splmntAtchFileGroupId}
fileName={cmptnAtchFileNm || "다운로드"}
/>
) : null}
</div>
</TableCell>