"use client"; import Image from 'next/image' import Link from './LocalizedLink' import { useEffect, useState } from 'react' import { Award, Check, Edit3, ExternalLink, Loader2, LogOut, Mail, MapPin, Package, Phone, Plus, Settings, Trash2, User, type LucideIcon, } from 'lucide-react' import { formatPrice, type CustomerAddress, type Order } from '@homaroma/types' import { API_BASE } from '../lib/api' import { orderStatusBadgeClass, toneClass } from '../lib/theme' import { useAuthStore, type Customer } from '../store/authStore' type ProfileTab = 'orders' | 'addresses' | 'settings' type ProfileTabItem = { tab: ProfileTab label: string icon: LucideIcon count: number | null } type ProfileCustomer = Customer & { points?: number addresses?: CustomerAddress[] } type AddressFormState = { recipientName: string phone: string addressLine: string subdistrict: string district: string province: string zipcode: string isDefault: boolean } const emptyAddressForm: AddressFormState = { recipientName: '', phone: '', addressLine: '', subdistrict: '', district: '', province: '', zipcode: '', isDefault: false, } function statusLabel(status: string) { const labels: Record = { PENDING_PAYMENT: 'รอการชำระเงิน', PAID: 'ชำระเงินสำเร็จ', PROCESSING: 'กำลังจัดเตรียมสินค้า', SHIPPED: 'จัดส่งแล้ว', DELIVERED: 'จัดส่งสำเร็จ', CANCELLED: 'ยกเลิกแล้ว', } return labels[status] ?? status.replaceAll('_', ' ') } function formatAddress(address: CustomerAddress) { return `${address.addressLine} ต.${address.subdistrict} อ.${address.district} จ.${address.province} ${address.zipcode}` } function toAddressForm(address: CustomerAddress): AddressFormState { return { recipientName: address.recipientName, phone: address.phone, addressLine: address.addressLine, subdistrict: address.subdistrict, district: address.district, province: address.province, zipcode: address.zipcode, isDefault: address.isDefault, } } async function readJson(path: string, init?: RequestInit): Promise { const response = await fetch(`${API_BASE}${path}`, init) const data = await response.json() if (!response.ok) { const message = data && typeof data === 'object' && 'error' in data && typeof data.error === 'string' ? data.error : `${path} returned ${response.status}` throw new Error(message) } return data as T } export default function ProfileClient() { const { token, customer, setAuth, logout } = useAuthStore() const [activeTab, setActiveTab] = useState('orders') const [dbCustomer, setDbCustomer] = useState(null) const [orders, setOrders] = useState([]) const [addresses, setAddresses] = useState([]) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [showAddressForm, setShowAddressForm] = useState(false) const [editingAddress, setEditingAddress] = useState(null) const [addressForm, setAddressForm] = useState(emptyAddressForm) const [submittingAddress, setSubmittingAddress] = useState(false) const [settingsForm, setSettingsForm] = useState({ email: '', phone: '' }) const [submittingSettings, setSubmittingSettings] = useState(false) const [settingsSuccess, setSettingsSuccess] = useState(false) const loadProfileData = async (currentCustomer: Customer) => { setLoading(true) setError(null) try { const [profileData, orderData, addressData] = await Promise.all([ readJson(`/profile/${currentCustomer.id}`), readJson(`/orders/customer/${currentCustomer.id}`), readJson(`/profile/${currentCustomer.id}/addresses`), ]) setDbCustomer(profileData) setOrders(Array.isArray(orderData) ? orderData : []) setAddresses(Array.isArray(addressData) ? addressData : []) setSettingsForm({ email: profileData.email ?? '', phone: profileData.phone ?? '', }) } catch (err) { setError(err instanceof Error ? err.message : 'ไม่สามารถโหลดข้อมูลสมาชิกได้') } finally { setLoading(false) } } useEffect(() => { if (!customer) return queueMicrotask(() => { void loadProfileData(customer) }) }, [customer]) const resetAddressForm = () => { setAddressForm(emptyAddressForm) setEditingAddress(null) setShowAddressForm(false) } const updateAddressForm = (key: keyof AddressFormState, value: string | boolean) => { setAddressForm((current) => ({ ...current, [key]: value })) } const handleStartAddress = (address?: CustomerAddress) => { if (address) { setEditingAddress(address) setAddressForm(toAddressForm(address)) } else { setEditingAddress(null) setAddressForm(emptyAddressForm) } setShowAddressForm(true) } const reloadAddresses = async (customerId: number) => { const data = await readJson(`/profile/${customerId}/addresses`) setAddresses(Array.isArray(data) ? data : []) } const handleAddressSubmit = async (event: React.FormEvent) => { event.preventDefault() if (!customer) return setSubmittingAddress(true) setError(null) try { await readJson( editingAddress ? `/profile/${customer.id}/addresses/${editingAddress.id}` : `/profile/${customer.id}/addresses`, { method: editingAddress ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(addressForm), }, ) resetAddressForm() await reloadAddresses(customer.id) } catch (err) { setError(err instanceof Error ? err.message : 'ไม่สามารถบันทึกที่อยู่ได้') } finally { setSubmittingAddress(false) } } const handleDeleteAddress = async (addressId: number) => { if (!customer) return if (!window.confirm('ยืนยันที่จะลบที่อยู่จัดส่งนี้?')) return setError(null) try { await readJson<{ success: boolean }>(`/profile/${customer.id}/addresses/${addressId}`, { method: 'DELETE' }) await reloadAddresses(customer.id) } catch (err) { setError(err instanceof Error ? err.message : 'ไม่สามารถลบที่อยู่ได้') } } const handleSetDefaultAddress = async (addressId: number) => { if (!customer) return setError(null) try { await readJson(`/profile/${customer.id}/addresses/${addressId}/default`, { method: 'PATCH' }) await reloadAddresses(customer.id) } catch (err) { setError(err instanceof Error ? err.message : 'ไม่สามารถตั้งค่าที่อยู่เริ่มต้นได้') } } const handleSettingsSubmit = async (event: React.FormEvent) => { event.preventDefault() if (!customer) return setSubmittingSettings(true) setSettingsSuccess(false) setError(null) try { const updatedCustomer = await readJson(`/profile/${customer.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: settingsForm.email || null, phone: settingsForm.phone || null, }), }) setDbCustomer(updatedCustomer) if (token) setAuth(token, updatedCustomer) setSettingsSuccess(true) setTimeout(() => setSettingsSuccess(false), 3000) } catch (err) { setError(err instanceof Error ? err.message : 'ไม่สามารถบันทึกข้อมูลติดต่อได้') } finally { setSubmittingSettings(false) } } const handleLineLogin = () => { const maybeWindow = window as Window & { liff?: { login: (options?: { redirectUri?: string }) => void } } if (maybeWindow.liff) { maybeWindow.liff.login({ redirectUri: window.location.href }) return } setError('ยังไม่พบ LINE LIFF ในหน้า website นี้ กรุณาเปิดผ่าน LINE OA หรือรอให้ทีมตั้งค่า LIFF สำหรับ website') } if (!customer) { return (

เข้าสู่ระบบสมาชิก

ล็อกอินเพื่อตรวจสอบประวัติคำสั่งซื้อ สะสมคะแนนสมาชิก และบันทึกสมุดที่อยู่จัดส่งสินค้า

{error && (

{error}

)} กลับไปเลือกซื้อสินค้า
) } const displayCustomer = dbCustomer ?? customer const tabs: ProfileTabItem[] = [ { tab: 'orders', label: 'ออเดอร์', icon: Package, count: orders.length }, { tab: 'addresses', label: 'ที่อยู่', icon: MapPin, count: addresses.length }, { tab: 'settings', label: 'ตั้งค่า', icon: Settings, count: null }, ] return (
{error && (
{error}
)}
{displayCustomer.displayName

{displayCustomer.displayName || 'Member'}

{displayCustomer.phone && {displayCustomer.phone}} {displayCustomer.email && {displayCustomer.email}}

คะแนนสะสม

{displayCustomer.points ?? 0} คะแนน

{tabs.map(({ tab, label, icon: Icon, count }) => ( ))}
{loading ? (
) : ( <> {activeTab === 'orders' && (
{orders.length === 0 ? ( ) : ( orders.map((order) => (

เลขออเดอร์ #{order.id.toString().padStart(4, '0')}

สั่งเมื่อ {new Date(order.createdAt).toLocaleDateString('th-TH', { year: 'numeric', month: 'long', day: 'numeric' })}

{statusLabel(order.status)}
{order.items.map((item) => (
{item.productName}

{item.productName}

{item.variantSize ? `${item.variantSize} · ` : ''}x {item.quantity}

{formatPrice(item.priceAtPurchase * item.quantity)}

))}

{order.trackingNo ? <>เลขพัสดุ: {order.trackingNo} : 'ยังไม่มีเลขพัสดุ'}

ยอดรวมสุทธิ: {formatPrice(order.totalAmount)}

)) )}
)} {activeTab === 'addresses' && (

สมุดที่อยู่จัดส่ง

{!showAddressForm && ( )}
{showAddressForm && (

{editingAddress ? 'แก้ไขที่อยู่จัดส่ง' : 'เพิ่มที่อยู่จัดส่งใหม่'}

updateAddressForm('recipientName', value)} required /> updateAddressForm('phone', value)} required />
updateAddressForm('addressLine', value)} required />
updateAddressForm('subdistrict', value)} required /> updateAddressForm('district', value)} required /> updateAddressForm('province', value)} required /> updateAddressForm('zipcode', value)} required />
)} {addresses.length === 0 ? ( ) : (
{addresses.map((address) => (

{address.recipientName}

{address.isDefault && เริ่มต้น}

{address.phone}

{formatAddress(address)}

{!address.isDefault && ( )}
))}
)}
)} {activeTab === 'settings' && (

ข้อมูลติดต่อสมาชิก

{settingsSuccess && (
บันทึกข้อมูลติดต่อสำเร็จแล้ว
)} setSettingsForm((current) => ({ ...current, email }))} type="email" /> setSettingsForm((current) => ({ ...current, phone }))} type="tel" />
)} )}
) } function TextInput({ label, value, onChange, required = false, type = 'text', }: { label: string value: string onChange: (value: string) => void required?: boolean type?: string }) { return ( ) } function EmptyPanel({ icon: Icon, title, description, actionHref, actionText, }: { icon: typeof Package title: string description: string actionHref?: string actionText?: string }) { return (

{title}

{description}

{actionHref && actionText && ( {actionText} )}
) }