diff --git a/app/admin/contacts/page.tsx b/app/admin/contacts/page.tsx index e06bf0e..a89b933 100644 --- a/app/admin/contacts/page.tsx +++ b/app/admin/contacts/page.tsx @@ -1,6 +1,7 @@ 'use client'; import { useEffect, useState } from 'react'; +import { useRouter } from 'next/navigation'; interface Contact { id: string; @@ -29,11 +30,41 @@ const SERVICE_LABELS: Record = { }; export default function AdminContactsPage() { + const router = useRouter(); const [contacts, setContacts] = useState([]); const [loading, setLoading] = useState(true); const [selected, setSelected] = useState(null); const [updating, setUpdating] = useState(null); const [filterStatus, setFilterStatus] = useState('all'); + const [creatingQuote, setCreatingQuote] = useState(false); + + async function createQuote(contact: Contact) { + setCreatingQuote(true); + try { + const title = `${SERVICE_LABELS[contact.service] ?? contact.service ?? '외주 문의'} — ${contact.name ?? ''}`.trim(); + const res = await fetch('/api/admin/quotes', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + title, + contact_request_id: contact.id, + client_name: contact.name ?? '', + client_email: contact.email, + }), + }); + const d = await res.json(); + if (res.ok && d.quote?.id) { + router.push('/admin/quotes/' + d.quote.id); + } else { + alert(d.error || '견적서 생성에 실패했습니다'); + } + } catch (e) { + console.error(e); + alert('견적서 생성 중 오류가 발생했습니다'); + } finally { + setCreatingQuote(false); + } + } useEffect(() => { fetch('/api/admin/contacts') @@ -221,6 +252,19 @@ export default function AdminContactsPage() { 이메일 답장하기 + + {/* 견적서 작성 */} + )} diff --git a/app/admin/quotes/[id]/page.tsx b/app/admin/quotes/[id]/page.tsx index cea560e..ef8db76 100644 --- a/app/admin/quotes/[id]/page.tsx +++ b/app/admin/quotes/[id]/page.tsx @@ -64,6 +64,7 @@ export default function QuoteEditorPage() { const [copied, setCopied] = useState(false); const [milestones, setMilestones] = useState([]); const [mileSaving, setMileSaving] = useState(null); + const [sending, setSending] = useState(false); useEffect(() => { fetch(`/api/admin/quotes/${id}`) @@ -125,6 +126,32 @@ export default function QuoteEditorPage() { setMileSaving(null); } + // ── 고객에게 발송 ─────────────────────── + async function sendToClient() { + if (!form.client_email) return; + if (!confirm("고객에게 견적 메일을 발송하고 상태를 '발송됨'으로 변경합니다.")) return; + setSending(true); + try { + const res = await fetch(`/api/admin/quotes/${id}/send`, { method: 'POST' }); + const d = await res.json(); + if (res.ok && d.success) { + setField('status', 'sent'); + if (d.emailSent === false) { + alert('상태는 변경됐으나 메일 발송에 실패했습니다 — 수동 발송이 필요합니다'); + } else { + alert('발송 완료'); + } + } else { + alert(d.error || '발송에 실패했습니다'); + } + } catch (e) { + console.error(e); + alert('발송 중 오류가 발생했습니다'); + } finally { + setSending(false); + } + } + // ── helpers ──────────────────────────── const setField = (k: keyof QuoteForm, v: unknown) => setForm((f) => ({ ...f, [k]: v })); @@ -255,6 +282,23 @@ export default function QuoteEditorPage() { PDF 저장 )} + {/* 고객에게 발송 */} + + {!form.client_email && ( + 이메일 입력 필요 + )} + {/* 저장 */}