feat: add frontend/src/components/MailView.tsx
This commit is contained in:
parent
3cd4ff055a
commit
03211c0260
91
frontend/src/components/MailView.tsx
Normal file
91
frontend/src/components/MailView.tsx
Normal file
@ -0,0 +1,91 @@
|
||||
import DOMPurify from 'dompurify'
|
||||
import { useMailStore } from '../store/mailStore'
|
||||
import { mailApi } from '../api/mailApi'
|
||||
|
||||
export default function MailView() {
|
||||
const { selectedMail, currentFolder, openCompose } = useMailStore()
|
||||
|
||||
if (!selectedMail) return (
|
||||
<div className="mail-view-empty">
|
||||
<div className="empty-icon">✉️</div>
|
||||
<div>메일을 선택하세요</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const m = selectedMail
|
||||
const cleanHtml = m.body_html
|
||||
? DOMPurify.sanitize(m.body_html, { FORBID_TAGS: ['script', 'iframe', 'object'] })
|
||||
: null
|
||||
|
||||
const reply = () => openCompose(m)
|
||||
const forward = () => openCompose({ ...m, subject: `Fw: ${m.subject}`, to: '', uid: '' } as any)
|
||||
|
||||
return (
|
||||
<div className="mail-view">
|
||||
<div className="mail-view-header">
|
||||
<h2 className="mail-subject">{m.subject}</h2>
|
||||
<div className="mail-meta">
|
||||
<div className="mail-from">
|
||||
<span className="meta-label">보낸사람</span>
|
||||
<span>{m.sender} <{m.sender_addr}></span>
|
||||
</div>
|
||||
<div className="mail-to">
|
||||
<span className="meta-label">받는사람</span>
|
||||
<span>{m.to}</span>
|
||||
</div>
|
||||
{m.cc && (
|
||||
<div className="mail-cc">
|
||||
<span className="meta-label">참조</span>
|
||||
<span>{m.cc}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="mail-date-row">
|
||||
<span className="meta-label">날짜</span>
|
||||
<span>{m.date}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mail-actions">
|
||||
<button className="btn-reply" onClick={reply}>↩️ 답장</button>
|
||||
<button className="btn-forward" onClick={forward}>↪️ 전달</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 첨부파일 */}
|
||||
{m.attachments?.length > 0 && (
|
||||
<div className="mail-attachments">
|
||||
<div className="attach-title">📎 첨부파일 ({m.attachments.length}개)</div>
|
||||
<div className="attach-list">
|
||||
{m.attachments.map(a => (
|
||||
<a
|
||||
key={a.part_id}
|
||||
href={mailApi.attachmentUrl(m.uid, a.part_id, currentFolder)}
|
||||
className="attach-item" download={a.filename}
|
||||
>
|
||||
<span className="attach-name">{a.filename}</span>
|
||||
<span className="attach-size">({fmtSize(a.size)})</span>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 본문 */}
|
||||
<div className="mail-body-content">
|
||||
{cleanHtml ? (
|
||||
<div
|
||||
className="mail-html-body"
|
||||
dangerouslySetInnerHTML={{ __html: cleanHtml }}
|
||||
/>
|
||||
) : (
|
||||
<pre className="mail-text-body">{m.body_text}</pre>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function fmtSize(b: number) {
|
||||
if (b < 1024) return `${b}B`
|
||||
if (b < 1024 * 1024) return `${(b / 1024).toFixed(0)}KB`
|
||||
return `${(b / 1024 / 1024).toFixed(1)}MB`
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user