diff options
Diffstat (limited to 'src-migrate/pages')
| -rw-r--r-- | src-migrate/pages/shop/cart/cart.module.css | 14 | ||||
| -rw-r--r-- | src-migrate/pages/shop/cart/index.tsx | 15 | ||||
| -rw-r--r-- | src-migrate/pages/shop/product/[slug].tsx | 62 |
3 files changed, 51 insertions, 40 deletions
diff --git a/src-migrate/pages/shop/cart/cart.module.css b/src-migrate/pages/shop/cart/cart.module.css index b756fb15..af5a2abc 100644 --- a/src-migrate/pages/shop/cart/cart.module.css +++ b/src-migrate/pages/shop/cart/cart.module.css @@ -3,7 +3,7 @@ } .content { - @apply flex flex-wrap ; + @apply flex flex-wrap; } .item-wrapper { @@ -33,3 +33,15 @@ .summary-buttons-step-approval { @apply grid grid-cols-1 gap-y-3 mt-6; } + +@media (max-width: 768px) { + .item-wrapper { + /* adjust if your bar is taller/shorter */ + padding-bottom: calc(env(safe-area-inset-bottom) + 9rem); + } + + .summary-wrapper { + @apply fixed inset-x-0 bottom-0 z-50 md:sticky w-full; + } + +}
\ No newline at end of file diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 795dfa72..031aa45b 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -301,9 +301,8 @@ const CartPage: React.FC = () => { <> {/* Sticky Header */} <div - className={`${ - isTop ? 'border-b-[0px]' : 'border-b-[1px]' - } sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`} + className={`${isTop ? 'border-b-[0px]' : 'border-b-[1px]' + } sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`} > <div className='flex items-center justify-between mb-2'> <h1 className={style.title}>Keranjang Belanja</h1> @@ -392,13 +391,17 @@ const CartPage: React.FC = () => { </div> </div> + <div + className="md:hidden" + style={{ height: 'calc(var(--mobile-actions-h) + var(--mobile-total-h))' }} + /> + {/* Cart Summary */} <div - className={`${style['summary-wrapper']} ${ - device.isMobile && (!cart || cart?.product_total === 0) + className={`${style['summary-wrapper']} ${device.isMobile && (!cart || cart?.product_total === 0) ? 'hidden' : '' - }`} + }`} > <div className={style.summary}> {device.isMobile ? ( diff --git a/src-migrate/pages/shop/product/[slug].tsx b/src-migrate/pages/shop/product/[slug].tsx index fc72a6b0..90658544 100644 --- a/src-migrate/pages/shop/product/[slug].tsx +++ b/src-migrate/pages/shop/product/[slug].tsx @@ -18,34 +18,41 @@ type PageProps = { product: IProductDetail } -export const getServerSideProps: GetServerSideProps<PageProps> = (async (context) => { - const { slug } = context.query +export const getServerSideProps: GetServerSideProps<PageProps & { canonicalPath: string }> = async (context) => { + const { slug } = context.query; const cookieString = context.req.headers.cookie; const cookies = cookieString ? cookie.parse(cookieString) : {}; const auth = cookies?.auth ? JSON.parse(cookies.auth) : {}; - const tier = auth?.pricelist || '' + const tier = auth?.pricelist || ''; - const productId = getIdFromSlug(slug as string) + const productId = getIdFromSlug(slug as string); + const product = await getProductById(productId, tier); - const product = await getProductById(productId, tier) + // ❌ produk tidak ada → 404 + if (!product) return { notFound: true }; - if (!product) return { notFound: true } + // ❌ tidak ada variants atau tidak ada yang harga > 0 → 404 + const hasValidVariant = Array.isArray(product.variants) + && product.variants.some(v => (v?.price?.price ?? 0) > 0); + if (!hasValidVariant) return { notFound: true }; + + // Canonical path aman untuk SSR (hindari router.asPath di server) + const canonicalPath = context.resolvedUrl || `/product/${slug}`; + + return { props: { product, canonicalPath } }; +}; - return { - props: { product } - } -}) const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST -const ProductDetailPage: NextPage<PageProps> = ({ product }) => { +const ProductDetailPage: NextPage<PageProps & { canonicalPath: string }> = ({ product, canonicalPath }) => { const router = useRouter(); - const { setProduct } = useProductContext(); - useEffect(() => { - if (product) setProduct(product); - }, [product, setProduct]); + useEffect(() => { if (product) setProduct(product); }, [product, setProduct]); + + const origin = process.env.NEXT_PUBLIC_SELF_HOST || ''; + const url = origin + (canonicalPath?.startsWith('/') ? canonicalPath : `/${canonicalPath}`); return ( <BasicLayout> @@ -53,31 +60,20 @@ const ProductDetailPage: NextPage<PageProps> = ({ product }) => { title={`${product.name} - Indoteknik.com`} description='Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.' openGraph={{ - url: SELF_HOST + router.asPath, - images: [ - { - url: product?.image, - width: 800, - height: 800, - alt: product?.name, - }, - ], + url, + images: [{ url: product?.image, width: 800, height: 800, alt: product?.name }], type: 'product', }} - additionalMetaTags={[ - { - name: 'keywords', - content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}`, - } - ]} - canonical={SELF_HOST + router.asPath} + additionalMetaTags={[{ name: 'keywords', content: `${product?.name}, Harga ${product?.name}, Beli ${product?.name}, Spesifikasi ${product?.name}` }]} + canonical={url} /> <div className='md:container pt-4 md:pt-6'> <ProductDetail product={product} /> </div> </BasicLayout> - ) -} + ); +}; + export default ProductDetailPage
\ No newline at end of file |
