feat: add frontend/src/components/Compose.tsx
This commit is contained in:
parent
e4f634f3fb
commit
cc8ea14bf6
83
frontend/src/components/Compose.tsx
Normal file
83
frontend/src/components/Compose.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { useState } from 'react'
|
||||||
|
import { mailApi } from '../api/mailApi'
|
||||||
|
import { useMailStore } from '../store/mailStore'
|
||||||
|
|
||||||
|
export default function Compose() {
|
||||||
|
const { replyTo, closeCompose, username } = useMailStore()
|
||||||
|
const [to, setTo] = useState(replyTo?.sender_addr || '')
|
||||||
|
const [cc, setCc] = useState('')
|
||||||
|
const [subject, setSubject] = useState(
|
||||||
|
replyTo ? (replyTo.subject.startsWith('Re:') ? replyTo.subject : `Re: ${replyTo.subject}`) : ''
|
||||||
|
)
|
||||||
|
const [body, setBody] = useState(
|
||||||
|
replyTo
|
||||||
|
? `\n\n---\n${replyTo.date}에 ${replyTo.sender} (${replyTo.sender_addr}) 님이 작성:\n${replyTo.body_text || '(HTML 메일)'}\n`
|
||||||
|
: ''
|
||||||
|
)
|
||||||
|
const [sending, setSending] = useState(false)
|
||||||
|
const [error, setError] = useState('')
|
||||||
|
const [success, setSuccess] = useState(false)
|
||||||
|
const [showCc, setShowCc] = useState(false)
|
||||||
|
|
||||||
|
const send = async () => {
|
||||||
|
if (!to.trim() || !subject.trim()) { setError('받는사람과 제목은 필수입니다'); return }
|
||||||
|
setSending(true); setError('')
|
||||||
|
try {
|
||||||
|
await mailApi.send({ to, cc: cc || undefined, subject, body })
|
||||||
|
setSuccess(true)
|
||||||
|
setTimeout(closeCompose, 1500)
|
||||||
|
} catch (e: any) {
|
||||||
|
setError(e.response?.data?.detail || '발송에 실패했습니다')
|
||||||
|
} finally { setSending(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="compose-overlay" onClick={e => e.target === e.currentTarget && closeCompose()}>
|
||||||
|
<div className="compose-box">
|
||||||
|
<div className="compose-header">
|
||||||
|
<span>✏️ {replyTo ? '답장' : '새 메일'}</span>
|
||||||
|
<button className="compose-close" onClick={closeCompose}>✕</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="compose-fields">
|
||||||
|
<div className="compose-field">
|
||||||
|
<label>보내는 사람</label>
|
||||||
|
<span className="compose-from">{username}</span>
|
||||||
|
</div>
|
||||||
|
<div className="compose-field">
|
||||||
|
<label>받는 사람</label>
|
||||||
|
<input value={to} onChange={e => setTo(e.target.value)} placeholder="수신자 이메일" />
|
||||||
|
<button className="btn-cc-toggle" onClick={() => setShowCc(v => !v)}>참조</button>
|
||||||
|
</div>
|
||||||
|
{showCc && (
|
||||||
|
<div className="compose-field">
|
||||||
|
<label>참조</label>
|
||||||
|
<input value={cc} onChange={e => setCc(e.target.value)} placeholder="참조 이메일" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="compose-field">
|
||||||
|
<label>제목</label>
|
||||||
|
<input value={subject} onChange={e => setSubject(e.target.value)} placeholder="제목 입력" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<textarea
|
||||||
|
className="compose-body"
|
||||||
|
value={body}
|
||||||
|
onChange={e => setBody(e.target.value)}
|
||||||
|
placeholder="내용을 입력하세요..."
|
||||||
|
/>
|
||||||
|
|
||||||
|
{error && <div className="compose-error">⚠️ {error}</div>}
|
||||||
|
{success && <div className="compose-success">✅ 발송 완료!</div>}
|
||||||
|
|
||||||
|
<div className="compose-footer">
|
||||||
|
<button className="btn-send" onClick={send} disabled={sending}>
|
||||||
|
{sending ? '발송 중...' : '📨 보내기'}
|
||||||
|
</button>
|
||||||
|
<button className="btn-cancel" onClick={closeCompose}>취소</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user