x
This commit is contained in:
1 parent
d13dedd215
commit
dba82b26a8
17 files changed
+427
-71
No files matched your search
@@ -23,6 +23,7 @@ import {
|
||||
import { formatPrice, type CustomerAddress, type Order } from '@homaroma/types'
|
||||
|
||||
import { API_BASE } from '../lib/api'
|
||||
import { useLang } from '../hooks/useLang'
|
||||
import { orderStatusBadgeClass, toneClass } from '../lib/theme'
|
||||
import { useAuthStore, type Customer } from '../store/authStore'
|
||||
|
||||
@@ -107,6 +108,7 @@ async function readJson<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
}
|
||||
|
||||
export default function ProfileClient() {
|
||||
const lang = useLang()
|
||||
const { token, customer, setAuth, logout } = useAuthStore()
|
||||
const [activeTab, setActiveTab] = useState<ProfileTab>('orders')
|
||||
const [dbCustomer, setDbCustomer] = useState<ProfileCustomer | null>(null)
|
||||
@@ -401,18 +403,21 @@ export default function ProfileClient() {
|
||||
</div>
|
||||
|
||||
<div className="mt-4 space-y-3">
|
||||
{order.items.map((item) => (
|
||||
<div key={item.id} className="flex items-center gap-3 text-sm">
|
||||
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-xl bg-surface-page">
|
||||
<Image src={item.productImage || '/images/fallback-product.svg'} alt={item.productName} fill unoptimized className="h-full w-full object-cover" />
|
||||
{order.items.map((item) => {
|
||||
const displayName = (item.translations as any)?.name?.[lang] || item.productName
|
||||
return (
|
||||
<div key={item.id} className="flex items-center gap-3 text-sm">
|
||||
<div className="relative h-11 w-11 shrink-0 overflow-hidden rounded-xl bg-surface-page">
|
||||
<Image src={item.productImage || '/images/fallback-product.svg'} alt={displayName} fill unoptimized className="h-full w-full object-cover" />
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-semibold text-text-main">{displayName}</p>
|
||||
<p className="text-xs text-muted-text">{item.variantName ? `${item.variantName} · ` : ''}x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-brand-primary">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-semibold text-text-main">{item.productName}</p>
|
||||
<p className="text-xs text-muted-text">{item.variantName ? `${item.variantName} · ` : ''}x {item.quantity}</p>
|
||||
</div>
|
||||
<p className="font-bold text-brand-primary">{formatPrice(item.priceAtPurchase * item.quantity)}</p>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex flex-col gap-3 border-t border-line-primary/20 pt-4 text-sm md:flex-row md:items-center md:justify-between">
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import Link from './LocalizedLink'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ChevronRight, Loader2, Package, Search } from 'lucide-react'
|
||||
|
||||
import { API_BASE } from '../lib/api'
|
||||
@@ -31,9 +32,22 @@ function orderLabel(id: number) {
|
||||
return `#${id.toString().padStart(4, '0')}`
|
||||
}
|
||||
|
||||
export default function TrackOrderClient() {
|
||||
interface TrackOrderClientProps {
|
||||
lang?: string
|
||||
}
|
||||
|
||||
export default function TrackOrderClient({ lang: propLang }: TrackOrderClientProps = {}) {
|
||||
const router = useRouter()
|
||||
const lang = useLang()
|
||||
const contextLang = useLang()
|
||||
const lang = propLang || contextLang || 'th'
|
||||
const { t: i18nT } = useTranslation()
|
||||
const t = useCallback(
|
||||
(key: string, options?: Record<string, unknown>) => {
|
||||
return i18nT(key, { lng: lang, ...options })
|
||||
},
|
||||
[i18nT, lang],
|
||||
)
|
||||
|
||||
const [orderId, setOrderId] = useState('')
|
||||
const [phone, setPhone] = useState('')
|
||||
const [localOrders, setLocalOrders] = useState<number[]>([])
|
||||
@@ -61,7 +75,7 @@ export default function TrackOrderClient() {
|
||||
const normalizedOrderId = Number.parseInt(orderId.replace(/[^0-9]/g, ''), 10)
|
||||
|
||||
if (!Number.isFinite(normalizedOrderId) || normalizedOrderId <= 0) {
|
||||
setError('กรุณากรอกหมายเลขคำสั่งซื้อให้ถูกต้อง')
|
||||
setError(t('track.errors.invalidOrderId'))
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
@@ -83,9 +97,9 @@ export default function TrackOrderClient() {
|
||||
return
|
||||
}
|
||||
|
||||
setError('error' in data && data.error ? data.error : 'ไม่สามารถยืนยันข้อมูลคำสั่งซื้อได้')
|
||||
setError('error' in data && data.error ? data.error : t('track.errors.notFound'))
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'เกิดข้อผิดพลาดในการเชื่อมต่อ กรุณาลองใหม่อีกครั้ง')
|
||||
setError(err instanceof Error ? err.message : t('track.errors.networkError'))
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
@@ -99,8 +113,8 @@ export default function TrackOrderClient() {
|
||||
<Search className="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="ui-eyebrow">Manual Search</p>
|
||||
<h2 className="text-xl font-semibold text-text-main">ค้นหาคำสั่งซื้อ</h2>
|
||||
<p className="ui-eyebrow">{t('track.search.eyebrow')}</p>
|
||||
<h2 className="text-xl font-semibold text-text-main">{t('track.search.title')}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +127,7 @@ export default function TrackOrderClient() {
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="orderId" className="ui-form-label">
|
||||
Order Number
|
||||
{t('track.search.orderIdLabel')}
|
||||
</label>
|
||||
<input
|
||||
id="orderId"
|
||||
@@ -121,14 +135,14 @@ export default function TrackOrderClient() {
|
||||
value={orderId}
|
||||
onChange={(event) => setOrderId(event.target.value)}
|
||||
className="ui-form-shell"
|
||||
placeholder="เช่น 1024"
|
||||
placeholder={t('track.search.orderIdPlaceholder')}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label htmlFor="phone" className="ui-form-label">
|
||||
Phone Number
|
||||
{t('track.search.phoneLabel')}
|
||||
</label>
|
||||
<input
|
||||
id="phone"
|
||||
@@ -136,7 +150,7 @@ export default function TrackOrderClient() {
|
||||
value={phone}
|
||||
onChange={(event) => setPhone(event.target.value)}
|
||||
className="ui-form-shell"
|
||||
placeholder="เบอร์โทรที่ใช้ตอนสั่งซื้อ"
|
||||
placeholder={t('track.search.phonePlaceholder')}
|
||||
type="tel"
|
||||
/>
|
||||
</div>
|
||||
@@ -146,7 +160,7 @@ export default function TrackOrderClient() {
|
||||
disabled={loading}
|
||||
className="ui-form-inline-button w-full"
|
||||
>
|
||||
{loading ? <Loader2 className="h-5 w-5 animate-spin" /> : 'Find Order'}
|
||||
{loading ? <Loader2 className="h-5 w-5 animate-spin" /> : t('track.search.submit')}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
@@ -154,13 +168,13 @@ export default function TrackOrderClient() {
|
||||
<section className="space-y-6">
|
||||
<div className="flex items-center gap-3 border-b border-line-primary/20 pb-4">
|
||||
<Package className="h-5 w-5 text-brand-primary" />
|
||||
<h2 className="text-xl font-semibold text-text-main">Recent Device Orders</h2>
|
||||
<h2 className="text-xl font-semibold text-text-main">{t('track.recent.title')}</h2>
|
||||
</div>
|
||||
|
||||
{localOrders.length === 0 ? (
|
||||
<div className="ui-empty-panel p-10 text-center">
|
||||
<p className="text-sm font-semibold text-muted-text">ยังไม่มีประวัติคำสั่งซื้อบนอุปกรณ์นี้</p>
|
||||
<p className="mt-2 text-xs uppercase tracking-[0.18em] text-muted-text/70">ใช้ฟอร์มค้นหาด้านซ้ายเพื่อบันทึกออเดอร์</p>
|
||||
<p className="text-sm font-semibold text-muted-text">{t('track.recent.emptyTitle')}</p>
|
||||
<p className="mt-2 text-xs uppercase tracking-[0.18em] text-muted-text/70">{t('track.recent.emptyDesc')}</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
@@ -171,7 +185,7 @@ export default function TrackOrderClient() {
|
||||
className="ui-card ui-card-hover group flex items-center justify-between p-5"
|
||||
>
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-[0.22em] text-muted-text/70">Order</p>
|
||||
<p className="text-xs font-bold uppercase tracking-[0.22em] text-muted-text/70">{t('track.recent.orderLabel')}</p>
|
||||
<p className="mt-1 font-mono text-2xl font-bold text-text-main">{orderLabel(id)}</p>
|
||||
</div>
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-surface-page text-text-main transition group-hover:bg-brand-primary group-hover:text-surface-card">
|
||||
@@ -185,3 +199,4 @@ export default function TrackOrderClient() {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user