96 lines
5.7 KiB
TypeScript
96 lines
5.7 KiB
TypeScript
import { useState } from 'react'
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import { Headset, Sparkles, Send } from 'lucide-react'
|
|
import { Link, useNavigate } from 'react-router-dom'
|
|
import { getMyCs, createCs, aiCardMessage } from '../api/client'
|
|
import { useShop } from '../store/shop'
|
|
import StatusBadge from '../components/StatusBadge'
|
|
|
|
const CATS = ['DELIVERY', 'PRODUCT', 'PAYMENT', 'REFUND', 'OTHER']
|
|
|
|
export default function Cs() {
|
|
const qc = useQueryClient()
|
|
const nav = useNavigate()
|
|
const { custToken } = useShop()
|
|
const [category, setCategory] = useState('DELIVERY')
|
|
const [orderNo, setOrderNo] = useState('')
|
|
const [subject, setSubject] = useState('')
|
|
const [content, setContent] = useState('')
|
|
const [msg, setMsg] = useState('')
|
|
// AI 카드메시지 도우미
|
|
const [occasion, setOccasion] = useState('Birthday')
|
|
const [tone, setTone] = useState('Warm')
|
|
const [recipient, setRecipient] = useState('')
|
|
const [cardSuggestions, setCardSuggestions] = useState<string[]>([])
|
|
|
|
const { data: tickets } = useQuery({ queryKey: ['cs'], queryFn: getMyCs, enabled: !!custToken })
|
|
|
|
const submit = async (e: React.FormEvent) => {
|
|
e.preventDefault(); setMsg('')
|
|
if (!custToken) { nav('/account'); return }
|
|
try {
|
|
const r = await createCs({ orderNo, category, subject, content })
|
|
setMsg(r?.aiReply ? `AI auto-reply: ${r.aiReply}` : 'Your request has been submitted.')
|
|
setSubject(''); setContent(''); qc.invalidateQueries({ queryKey: ['cs'] })
|
|
} catch { setMsg('Failed to submit your request.') }
|
|
}
|
|
|
|
const genCard = async () => {
|
|
const r = await aiCardMessage(occasion, tone, recipient).catch(() => null)
|
|
setCardSuggestions(r?.messages || [])
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-3xl mx-auto px-4 py-8">
|
|
<div className="flex items-center gap-2 mb-6"><Headset className="text-bloom" size={22} /><h1 className="font-serif text-2xl font-bold text-blush-900">Customer Support (1:1)</h1></div>
|
|
|
|
{/* AI 카드 메시지 작성 도우미 */}
|
|
<div className="bg-petal rounded-2xl p-5 mb-6">
|
|
<div className="flex items-center gap-2 text-bloom2 font-medium text-sm mb-3"><Sparkles size={16} /> AI Card Message Helper</div>
|
|
<div className="grid grid-cols-3 gap-2 mb-3">
|
|
<input value={occasion} onChange={e => setOccasion(e.target.value)} placeholder="Occasion (e.g. Birthday)" className="px-3 py-2 rounded-lg border border-blush-100 text-sm bg-white outline-none" />
|
|
<input value={tone} onChange={e => setTone(e.target.value)} placeholder="Tone" className="px-3 py-2 rounded-lg border border-blush-100 text-sm bg-white outline-none" />
|
|
<input value={recipient} onChange={e => setRecipient(e.target.value)} placeholder="Recipient" className="px-3 py-2 rounded-lg border border-blush-100 text-sm bg-white outline-none" />
|
|
</div>
|
|
<button onClick={genCard} className="bg-bloom text-white text-sm font-semibold px-4 py-2 rounded-full">Suggest Messages</button>
|
|
{!!cardSuggestions.length && (
|
|
<ul className="mt-3 space-y-2">
|
|
{cardSuggestions.map((m, i) => <li key={i} className="bg-white rounded-lg px-3 py-2 text-sm text-gray-700">{m}</li>)}
|
|
</ul>
|
|
)}
|
|
</div>
|
|
|
|
{/* 문의 작성 */}
|
|
<form onSubmit={submit} className="bg-white rounded-2xl border border-blush-100/60 p-5 space-y-3 mb-8">
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<select value={category} onChange={e => setCategory(e.target.value)} className="px-3 py-2 rounded-xl border border-blush-100 text-sm bg-white">
|
|
{CATS.map(c => <option key={c} value={c}>{c}</option>)}
|
|
</select>
|
|
<input value={orderNo} onChange={e => setOrderNo(e.target.value)} placeholder="Order number (optional)" className="px-3 py-2 rounded-xl border border-blush-100 text-sm outline-none focus:border-bloom" />
|
|
</div>
|
|
<input value={subject} onChange={e => setSubject(e.target.value)} required placeholder="Subject" className="w-full px-3 py-2.5 rounded-xl border border-blush-100 text-sm outline-none focus:border-bloom" />
|
|
<textarea value={content} onChange={e => setContent(e.target.value)} rows={4} required placeholder="Tell us how we can help. Our AI will try to answer first." className="w-full px-3 py-2.5 rounded-xl border border-blush-100 text-sm outline-none focus:border-bloom" />
|
|
{msg && <div className="text-sm text-leaf bg-leaf/10 rounded-lg px-3 py-2">{msg}</div>}
|
|
<button className="flex items-center gap-1.5 bg-bloom text-white font-semibold px-6 py-2.5 rounded-full hover:bg-bloom2"><Send size={16} /> Submit Request</button>
|
|
{!custToken && <p className="text-xs text-gray-400">Please log in to submit a request. <Link to="/account" className="text-bloom">Log In</Link></p>}
|
|
</form>
|
|
|
|
<h2 className="font-serif text-lg font-bold mb-3">My Requests</h2>
|
|
<div className="space-y-2">
|
|
{(tickets || []).map((t: any) => (
|
|
<div key={t.id} className="bg-white rounded-2xl border border-blush-100/60 p-4">
|
|
<div className="flex items-center justify-between">
|
|
<span className="font-medium text-sm">{t.subject}</span>
|
|
<StatusBadge status={t.status} />
|
|
</div>
|
|
<p className="text-sm text-gray-600 mt-1">{t.content}</p>
|
|
{t.aiReply && <div className="mt-2 text-xs text-bloom2 bg-petal rounded-lg px-3 py-2"><b>AI Reply:</b> {t.aiReply}</div>}
|
|
{t.itsmSrId && <div className="text-[11px] text-gray-400 mt-1">ITSM SR: {t.itsmSrId}</div>}
|
|
</div>
|
|
))}
|
|
{!(tickets || []).length && <div className="text-gray-400 text-sm py-6 text-center">No requests submitted yet.</div>}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|