This commit is contained in:
Your Name committed 2026-06-20 18:57:24 -04:00
1 parent f7aa4d099f
commit ab3e53c4da
33 files changed
+290 -69

No files matched your search

@@ -1,7 +1,7 @@
"use client";
import { useState } from 'react'
import Link from 'next/link'
import Link from './LocalizedLink'
import { getCategoryImage, formatPrice, type Product } from '@homaroma/types'
import { useCartStore } from '../store/cartStore'
import { resolveCategoryName, resolveLocalized } from '../lib/localization'
+1 -1
View File
@@ -1,6 +1,6 @@
"use client";
import { X, ShoppingBag, Plus, Minus, Trash2 } from 'lucide-react'
import Link from 'next/link'
import Link from './LocalizedLink'
import { useCartStore } from '../store/cartStore'
import { formatPrice } from '@homaroma/types'
import { useTranslation } from 'react-i18next'
@@ -1,6 +1,6 @@
"use client";
import Link from 'next/link'
import Link from './LocalizedLink'
import { useRouter } from 'next/navigation'
import { useEffect, useMemo, useState } from 'react'
import { AlertCircle, CheckCircle2, Loader2, PackageCheck, ShoppingBag, Ticket, UserRound } from 'lucide-react'
@@ -9,6 +9,8 @@ import { formatPrice, type CartItem, type CustomerAddress } from '@homaroma/type
import { API_BASE } from '../lib/api'
import { useAuthStore, type Customer } from '../store/authStore'
import { useCartStore } from '../store/cartStore'
import { useLang } from '../hooks/useLang'
import { getPathWithLocale } from '../lib/locales'
type StoreSettings = {
shippingFee?: number
@@ -115,6 +117,7 @@ function saveLocalOrder(orderId: number) {
export default function CheckoutClient() {
const router = useRouter()
const lang = useLang()
const { customer } = useAuthStore()
const { items, clearCart, getCartTotal } = useCartStore()
const [hydrated, setHydrated] = useState(false)
@@ -373,7 +376,7 @@ export default function CheckoutClient() {
clearCart()
saveLocalOrder(order.id)
router.push(`/order/${order.id}`)
router.push(getPathWithLocale(`/order/${order.id}`, lang))
} catch (error) {
setSubmitError(error instanceof Error ? error.message : 'ไม่สามารถสร้างคำสั่งซื้อได้')
} finally {
+2 -2
View File
@@ -23,7 +23,7 @@ export default function Footer({
const { t } = useTranslation()
const lang = useLang()
const [mounted, setMounted] = useState(false)
useEffect(() => { setMounted(true) }, [])
useEffect(() => { queueMicrotask(() => setMounted(true)) }, [])
// Before mount: always 'th' to match SSR output → no hydration mismatch
// After mount: real user language → correct content
const effectiveLang = mounted ? lang : 'th'
@@ -32,7 +32,7 @@ export default function Footer({
const translateMenuLabel = (item: MenuItem): string => {
// Use outer `lang` from closure — hooks cannot be called inside nested functions
if (effectiveLang !== 'en' && item.translations && item.translations.length > 0) {
const trans = item.translations.find((tr: any) => tr.langCode === effectiveLang)
const trans = item.translations.find((tr: { langCode: string; label: string }) => tr.langCode === effectiveLang)
if (trans?.label) return trans.label
}
const map: Record<string, string> = {
@@ -24,7 +24,7 @@ export default function LocalizedLink({
// Extract current locale from pathname
const currentLocale = useMemo(() => {
const segments = pathname.split('/').filter(Boolean);
if (segments.length > 0 && SUPPORTED_LOCALES.includes(segments[0] as any)) {
if (segments.length > 0 && (SUPPORTED_LOCALES as readonly string[]).includes(segments[0])) {
return segments[0];
}
return 'th'; // Fallback
+10 -4
View File
@@ -32,6 +32,12 @@ const LANGUAGES = [
{ code: 'ko', label: '한국어', short: 'KO' },
]
function setLanguageCookie(code: string) {
if (typeof document !== 'undefined') {
document.cookie = `homaroma_lang=${code}; path=/; max-age=31536000`
}
}
function createFallbackMenu(categories: Category[]): MegaMenuPayload[] {
const categoryLinks = categories.map((category) => {
// category.name is a multilingual Record<lang,string> when fetched with lang=all
@@ -90,13 +96,13 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
const changeLanguage = (code: string) => {
i18n.changeLanguage(code)
localStorage.setItem('homaroma_lang', code)
document.cookie = `homaroma_lang=${code}; path=/; max-age=31536000`
setLanguageCookie(code)
const currentPathWithoutLang = getPathWithoutLocale(pathname)
const newPath = getPathWithLocale(currentPathWithoutLang, code)
setLangSheetOpen(false)
router.push(newPath)
router.push(newPath + window.location.search)
}
const loginWithLine = () => {
@@ -105,7 +111,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
maybeWindow.liff.login()
return
}
router.push('/profile')
router.push(getPathWithLocale('/profile', lang))
}
@@ -163,7 +169,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
const handleSearch = (e: React.FormEvent) => {
e.preventDefault()
if (searchQuery.trim()) {
router.push(`/search?q=${encodeURIComponent(searchQuery.trim())}`)
router.push(getPathWithLocale(`/search?q=${encodeURIComponent(searchQuery.trim())}`, lang))
setSearchOpen(false)
setMobileOpen(false)
setSearchQuery('')
+1 -1
View File
@@ -1,6 +1,6 @@
"use client"
import Link from 'next/link'
import Link from './LocalizedLink'
import type { Product } from '@homaroma/types'
import { getCategoryImage, formatPrice } from '@homaroma/types'
import { resolveCategoryName, resolveLocalized } from '../lib/localization'
@@ -1,6 +1,6 @@
"use client";
import Link from 'next/link'
import Link from './LocalizedLink'
import { useEffect, useState } from 'react'
import {
Award,
@@ -2,6 +2,8 @@
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { useLang } from '../hooks/useLang'
import { getPathWithLocale } from '../lib/locales'
type ShopCouponBridgeProps = {
coupon?: string
@@ -9,13 +11,14 @@ type ShopCouponBridgeProps = {
export default function ShopCouponBridge({ coupon }: ShopCouponBridgeProps) {
const router = useRouter()
const lang = useLang()
useEffect(() => {
if (!coupon) return
localStorage.setItem('homaroma_active_coupon', coupon)
router.replace('/shop', { scroll: false })
}, [coupon, router])
router.replace(getPathWithLocale('/shop', lang), { scroll: false })
}, [coupon, router, lang])
return null
}
@@ -1,11 +1,13 @@
"use client";
import Link from 'next/link'
import Link from './LocalizedLink'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import { ChevronRight, Loader2, Package, Search } from 'lucide-react'
import { API_BASE } from '../lib/api'
import { useLang } from '../hooks/useLang'
import { getPathWithLocale } from '../lib/locales'
type TrackResponse =
| { success: true; orderId: number }
@@ -30,6 +32,7 @@ function orderLabel(id: number) {
export default function TrackOrderClient() {
const router = useRouter()
const lang = useLang()
const [orderId, setOrderId] = useState('')
const [phone, setPhone] = useState('')
const [localOrders, setLocalOrders] = useState<number[]>([])
@@ -75,7 +78,7 @@ export default function TrackOrderClient() {
if (response.ok && 'success' in data && data.success) {
saveLocalOrder(data.orderId)
router.push(`/order/${data.orderId}`)
router.push(getPathWithLocale(`/order/${data.orderId}`, lang))
return
}
@@ -1,6 +1,6 @@
"use client";
import Link from 'next/link'
import Link from './LocalizedLink'
import { Heart } from 'lucide-react'
import ProductCard from './ProductCard'
@@ -1,6 +1,6 @@
"use client"
import Link from 'next/link'
import Link from '../LocalizedLink'
import { Clock, Mail, MapPin, MessageCircle } from 'lucide-react'
import type { Category, PageSection, Product } from '@homaroma/types'
import { getCategoryImage } from '@homaroma/types'