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

This commit is contained in:
2026-08-28 17:45:20 +09:00
@@ -11,7 +11,7 @@ import { useTus } from "@/shared/lib";
import type { AtflMngClientResponseDto, EsntlAtchFileMngTreeResponseDto } from "@/shared/model";
import { TextShimmer } from "@/shared/ui";
import { SectionCollapsible } from "@/shared/ui";
import { attachService } from "@/entities/attach";
import { attachService, useDownloadAttachmentByAtflId } from "@/entities/attach";
import { useEsntlAtchFileMngTree } from "@/entities/esntl";
import {
Button,
@@ -212,6 +212,7 @@ function AttachmentsFormSection({
const { data: essentialFiles = [], isPending: isLoadingEssentialFiles } = useEsntlAtchFileMngTree(
{ docSeComCd },
);
const { mutate: downloadTemplate } = useDownloadAttachmentByAtflId();
async function invalidateAttachmentQueries() {
await queryClient.invalidateQueries({
@@ -375,9 +376,10 @@ function AttachmentsFormSection({
<h3 className="mb-4 text-xl font-semibold">필수 첨부파일</h3>
<Table variant="secondary" className="table-fixed">
<colgroup>
<col width="40%" />
<col width="15%" />
<col width="45%" />
<col width="30%" />
<col width="12%" />
<col width="23%" />
<col width="35%" />
</colgroup>
<TableHeader>
<TableRow>
@@ -385,83 +387,106 @@ function AttachmentsFormSection({
파일명
</TableHead>
<TableHead>파일 확장자</TableHead>
<TableHead align="left">파일 양식</TableHead>
<TableHead>파일첨부</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{essentialFiles.length === 0 ? (
<TableRow>
<TableCell colSpan={3} className="py-12 text-center text-muted-foreground">
<TableCell colSpan={4} className="py-12 text-center text-muted-foreground">
조회된 필수 첨부파일 항목이 없습니다.
</TableCell>
</TableRow>
) : (
essentialFiles.map((file) => (
<TableRow key={file.esntlAtchFileId}>
<TableHead
className="break-keep font-medium"
data-required={file.esntlAtchYn === "Y"}
align="left"
>
{file.esntlAtchFileNm}
</TableHead>
<TableCell align="center">{file.esntlAtchFileRmrk}</TableCell>
<TableCell className="space-y-2">
{uploadedFiles[file.esntlAtchFileId]?.length > 0 && (
<div className="flex flex-wrap gap-2">
{uploadedFiles[file.esntlAtchFileId].map((item, idx) => {
const uploading = isUploading[item.fileKey] ?? false;
essentialFiles.map((file) => {
const templateFiles = file.templateAtchFileList ?? [];
return (
<div
key={item.fileKey}
className="inline-flex items-center justify-between gap-2 rounded-sm border bg-accent p-2"
return (
<TableRow key={file.esntlAtchFileId}>
<TableHead
className="break-keep font-medium"
data-required={file.esntlAtchYn === "Y"}
align="left"
>
{file.esntlAtchFileNm}
</TableHead>
<TableCell align="center">{file.esntlAtchFileRmrk}</TableCell>
<TableCell align="left">
{templateFiles.length === 0 ? (
<span className="text-muted-foreground">-</span>
) : (
<div className="space-y-1">
{templateFiles.map((templateFile) => (
<a
key={templateFile.atflId}
className="flex cursor-pointer items-center gap-1 text-blue-600 hover:underline"
onClick={() => downloadTemplate(templateFile.atflId)}
>
<Paperclip className="size-5 shrink-0" />
<TextShimmer className="text-xl" active={uploading}>
{item.file?.name ?? item.fileName}
</TextShimmer>
<button
type="button"
onClick={(e) => {
e.preventDefault();
void removeEssentialFile(file.esntlAtchFileId, idx);
}}
disabled={uploading}
<Paperclip className="size-4 shrink-0" />
{templateFile.orgnlFileNm || "다운로드"}
</a>
))}
</div>
)}
</TableCell>
<TableCell className="space-y-2">
{uploadedFiles[file.esntlAtchFileId]?.length > 0 && (
<div className="flex flex-wrap gap-2">
{uploadedFiles[file.esntlAtchFileId].map((item, idx) => {
const uploading = isUploading[item.fileKey] ?? false;
return (
<div
key={item.fileKey}
className="inline-flex items-center justify-between gap-2 rounded-sm border bg-accent p-2"
>
<X
className="size-5 shrink-0 text-destructive"
aria-label="삭제"
/>
</button>
</div>
);
})}
</div>
)}
<div>
<label>
<input
type="file"
multiple
className="hidden"
accept={toFileAccept(file.esntlAtchFileRmrk) || undefined}
onChange={(e) => {
void handleEssentialFileUpload(
file.esntlAtchFileId,
e.target.files,
<Paperclip className="size-5 shrink-0" />
<TextShimmer className="text-xl" active={uploading}>
{item.file?.name ?? item.fileName}
</TextShimmer>
<button
type="button"
onClick={(e) => {
e.preventDefault();
void removeEssentialFile(file.esntlAtchFileId, idx);
}}
disabled={uploading}
>
<X
className="size-5 shrink-0 text-destructive"
aria-label="삭제"
/>
</button>
</div>
);
e.target.value = "";
}}
/>
<Button size="sm" variant="outline" asChild>
<span>파일추가</span>
</Button>
</label>
</div>
</TableCell>
</TableRow>
))
})}
</div>
)}
<div>
<label>
<input
type="file"
multiple
className="hidden"
accept={toFileAccept(file.esntlAtchFileRmrk) || undefined}
onChange={(e) => {
void handleEssentialFileUpload(
file.esntlAtchFileId,
e.target.files,
);
e.target.value = "";
}}
/>
<Button size="sm" variant="outline" asChild>
<span>파일추가</span>
</Button>
</label>
</div>
</TableCell>
</TableRow>
);
})
)}
</TableBody>
</Table>