Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/components/dues/DuesReceiptCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,20 @@ function DuesReceiptCard({ transaction, receiptFiles, onOpenReceiptViewer }: Due
);

if (hasReceipt) {
if (isPdf) {
return (
<a
href={firstReceipt.fileUrl}
target="_blank"
rel="noreferrer"
className={className}
aria-label="PDF 영수증 원본 보기"
>
{content}
</a>
);
}

return (
<button
type="button"
Expand Down
88 changes: 7 additions & 81 deletions src/components/dues/DuesReceiptViewerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import { useState } from 'react';

import { DeleteIcon, DownloadIcon, PaperclipIcon } from '@/assets/icons';
import { DeleteIcon } from '@/assets/icons';
import { Icon } from '@/components/ui';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import { toastError } from '@/stores/useToastStore';
import type { DuesReceiptFile } from '@/types/dues';
import { isPdfReceipt } from '@/utils/dues/duesTransaction';
import { DuesReceiptPageButton } from './DuesReceiptPageButton';

interface DuesReceiptViewerModalProps {
Expand All @@ -20,7 +18,6 @@ function DuesReceiptViewerModal({ open, onOpenChange, receiptFiles }: DuesReceip
const [activeIndex, setActiveIndex] = useState(0);
const receiptCount = receiptFiles.length;
const activeReceipt = receiptFiles[activeIndex];
const isPdf = activeReceipt ? isPdfReceipt(activeReceipt) : false;
const hasMultipleReceipts = receiptCount > 1;

const handlePrevious = () => {
Expand Down Expand Up @@ -64,16 +61,12 @@ function DuesReceiptViewerModal({ open, onOpenChange, receiptFiles }: DuesReceip
/>
)}

{isPdf ? (
<PdfReceiptPreview receipt={activeReceipt} />
) : (
// eslint-disable-next-line @next/next/no-img-element
<img
src={activeReceipt.fileUrl}
alt={`영수증 원본 ${activeIndex + 1}`}
className="max-h-full max-w-full object-contain"
/>
)}
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={activeReceipt.fileUrl}
alt={`영수증 원본 ${activeIndex + 1}`}
className="max-h-full max-w-full object-contain"
/>

{hasMultipleReceipts && (
<DuesReceiptPageButton direction="next" onClick={handleNext} className="right-450" />
Expand All @@ -86,71 +79,4 @@ function DuesReceiptViewerModal({ open, onOpenChange, receiptFiles }: DuesReceip
);
}

function PdfReceiptPreview({ receipt }: { receipt: DuesReceiptFile }) {
const [isDownloading, setIsDownloading] = useState(false);

const handleDownload = async () => {
if (isDownloading) return;

setIsDownloading(true);

try {
const response = await fetch(receipt.fileUrl);
if (!response.ok) throw new Error('PDF 다운로드에 실패했습니다.');

const blob = await response.blob();
const objectUrl = URL.createObjectURL(blob);
const anchor = document.createElement('a');

anchor.href = objectUrl;
anchor.download = receipt.fileName;
document.body.appendChild(anchor);
anchor.click();
anchor.remove();
setTimeout(() => {
URL.revokeObjectURL(objectUrl);
}, 1000);
} catch {
toastError('파일 다운로드에 실패했습니다.');
} finally {
setIsDownloading(false);
}
};

return (
<div className="flex w-full max-w-[360px] flex-col items-center gap-400 text-center">
<div className="bg-container-neutral-alternative flex size-20 items-center justify-center rounded-lg">
<Icon src={PaperclipIcon} size={32} className="text-icon-alternative" />
</div>

<div className="flex w-full flex-col gap-100">
<p className="typo-sub1 truncate text-white">{receipt.fileName}</p>
<p className="typo-body2 text-white/70">
PDF 영수증은 원본 파일로 열거나 다운로드할 수 있어요
</p>
</div>

<div className="flex w-full gap-200">
<a
href={receipt.fileUrl}
target="_blank"
rel="noreferrer"
className="typo-button2 bg-container-neutral text-text-normal hover:bg-container-neutral-interaction flex h-12 flex-1 items-center justify-center rounded-sm transition-colors"
>
새 탭에서 열기
</a>
<button
type="button"
onClick={handleDownload}
disabled={isDownloading}
className="typo-button2 bg-button-primary text-text-inverse hover:bg-button-primary-interaction disabled:bg-button-neutral disabled:text-text-disabled flex h-12 flex-1 cursor-pointer items-center justify-center gap-100 rounded-sm transition-colors disabled:cursor-not-allowed"
>
<Icon src={DownloadIcon} size={18} />
{isDownloading ? '다운로드 중' : '다운로드'}
</button>
</div>
</div>
);
}

export { DuesReceiptViewerModal, type DuesReceiptViewerModalProps };
5 changes: 3 additions & 2 deletions src/components/dues/DuesTransactionDetailModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { DeleteIcon } from '@/assets/icons';
import { Icon } from '@/components/ui';
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
import type { DuesTransaction } from '@/types/dues';
import { getReceiptFiles } from '@/utils/dues/duesTransaction';
import { getReceiptFiles, isPdfReceipt } from '@/utils/dues/duesTransaction';
import { DuesReceiptCard } from './DuesReceiptCard';
import { DuesReceiptViewerModal } from './DuesReceiptViewerModal';
import { DuesTransactionDetailCard } from './DuesTransactionDetailCard';
Expand All @@ -27,6 +27,7 @@ function DuesTransactionDetailModal({
if (!transaction) return null;

const receiptFiles = getReceiptFiles(transaction);
const imageReceiptFiles = receiptFiles.filter((receipt) => !isPdfReceipt(receipt));

return (
<Dialog open={open} onOpenChange={onOpenChange}>
Expand Down Expand Up @@ -61,7 +62,7 @@ function DuesTransactionDetailModal({
key={receiptViewerOpen ? 'receipt-viewer-open' : 'receipt-viewer-closed'}
open={receiptViewerOpen}
onOpenChange={setReceiptViewerOpen}
receiptFiles={receiptFiles}
receiptFiles={imageReceiptFiles}
/>
</DialogContent>
</Dialog>
Expand Down
Loading