summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src-migrate/modules/product-detail/components/ProductDetail.tsx114
-rw-r--r--src/pages/api/magento-product.ts114
2 files changed, 216 insertions, 12 deletions
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx
index 1bacd2e2..b0950194 100644
--- a/src-migrate/modules/product-detail/components/ProductDetail.tsx
+++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx
@@ -4,7 +4,7 @@ import Link from 'next/link';
import { useRouter } from 'next/router';
import { useEffect, useRef, useState, UIEvent } from 'react';
-// Import komponen Chakra UI yang dibutuhkan
+// Import komponen Chakra UI
import {
Button,
Tabs,
@@ -16,7 +16,10 @@ import {
Tbody,
Tr,
Td,
- Box
+ Box,
+ Spinner,
+ Center,
+ Text
} from '@chakra-ui/react';
import {
@@ -58,6 +61,12 @@ const ProductDetail = ({ product }: Props) => {
const { isDesktop, isMobile } = useDevice();
const router = useRouter();
const [auth, setAuth] = useState<any>(null);
+
+ // State untuk Spesifikasi dari Magento
+ const [specs, setSpecs] = useState<{ label: string; value: string }[]>([]);
+ const [loadingSpecs, setLoadingSpecs] = useState(false);
+ const [errorSpecs, setErrorSpecs] = useState(false);
+
useEffect(() => {
try {
setAuth(getAuth() ?? null);
@@ -74,6 +83,7 @@ const ProductDetail = ({ product }: Props) => {
activeVariantId,
setIsApproval,
isApproval,
+ selectedVariant,
setSelectedVariant,
} = useProductDetail();
@@ -99,12 +109,65 @@ const ProductDetail = ({ product }: Props) => {
if (typeof auth === 'object') {
setIsApproval(auth?.feature?.soApproval);
}
- const selectedVariant =
+ const variantInit =
product?.variants?.find((variant) => variant.is_in_bu) ||
product?.variants?.[0];
- setSelectedVariant(selectedVariant);
+ setSelectedVariant(variantInit);
}, []);
+ // =========================================================================
+ // LOGIC FETCH: MENGGUNAKAN ID VARIANT
+ // =========================================================================
+ useEffect(() => {
+ const fetchMagentoSpecs = async () => {
+ // MENGGUNAKAN ID VARIANT SESUAI REQUEST
+ const skuToFetch = selectedVariant?.id;
+
+ if (!skuToFetch) return;
+
+ setLoadingSpecs(true);
+ setErrorSpecs(false);
+
+ try {
+ console.log("Fetching Magento specs via Proxy for Variant ID:", skuToFetch);
+
+ // Pastikan dikonversi ke string
+ const endpoint = `/api/magento-product?sku=${encodeURIComponent(String(skuToFetch))}`;
+
+ const response = await fetch(endpoint, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ }
+ });
+
+ if (!response.ok) {
+ console.warn(`Spec API status: ${response.status}`);
+ setSpecs([]);
+ return;
+ }
+
+ const data = await response.json();
+
+ if (data.specs && Array.isArray(data.specs)) {
+ setSpecs(data.specs);
+ } else {
+ setSpecs([]);
+ }
+
+ } catch (error) {
+ console.error("Gagal mengambil data spesifikasi:", error);
+ setErrorSpecs(true);
+ setSpecs([]);
+ } finally {
+ setLoadingSpecs(false);
+ }
+ };
+
+ fetchMagentoSpecs();
+ }, [selectedVariant]);
+
+
const allImages = (() => {
const arr: string[] = [];
if (product?.image) arr.push(product.image);
@@ -154,9 +217,6 @@ const ProductDetail = ({ product }: Props) => {
setMainImage(allImages[i] || '');
};
- console.log('detail product render');
- console.log('product: ', product);
-
return (
<>
<div className='relative'>
@@ -192,7 +252,7 @@ const ProductDetail = ({ product }: Props) => {
<div className='md:flex md:flex-wrap'>
{/* ===== Kolom kiri: gambar ===== */}
<div className='md:w-4/12'>
- {/* === MOBILE: Slider swipeable, tanpa thumbnail carousel === */}
+ {/* === MOBILE: Slider swipeable === */}
{isMobile ? (
<div className='relative'>
<div
@@ -233,6 +293,7 @@ const ProductDetail = ({ product }: Props) => {
)}
</div>
+ {/* Dots indicator */}
{allImages.length > 1 && (
<div className='absolute bottom-2 left-0 right-0 flex justify-center gap-2'>
{allImages.map((_, i) => (
@@ -250,7 +311,7 @@ const ProductDetail = ({ product }: Props) => {
</div>
) : (
<>
- {/* === DESKTOP: Tetap seperti sebelumnya === */}
+ {/* === DESKTOP === */}
<ProductImage product={{ ...product, image: mainImage }} />
{allImages.length > 0 && (
<div className='mt-4 overflow-x-auto'>
@@ -414,9 +475,38 @@ const ProductDetail = ({ product }: Props) => {
</div>
</TabPanel>
- {/* PANEL 2: SPESIFIKASI (Sesuai Gambar) */}
- <TabPanel px={0} py={6}>
- <p className="text-gray-500 text-sm">Informasi tambahan belum tersedia.</p>
+ {/* PANEL 2: SPESIFIKASI DARI MAGENTO */}
+ <TabPanel px={0} py={2}>
+ <Box border="1px solid" borderColor="gray.200" borderRadius="sm">
+ {loadingSpecs ? (
+ <Center py={6}>
+ <Spinner color='red.500' />
+ </Center>
+ ) : errorSpecs ? (
+ <Box p={4} color="red.500" fontSize="sm">
+ Gagal memuat data spesifikasi. Silakan coba lagi nanti.
+ </Box>
+ ) : specs.length > 0 ? (
+ <Table variant="simple" size="md">
+ <Tbody>
+ {specs.map((item, index) => (
+ <Tr key={index}>
+ <Td fontWeight="bold" width="30%" verticalAlign="top" borderColor="gray.200">
+ {item.label}
+ </Td>
+ <Td verticalAlign="top" borderColor="gray.200">
+ {item.value}
+ </Td>
+ </Tr>
+ ))}
+ </Tbody>
+ </Table>
+ ) : (
+ <Box p={4} color="gray.500" fontSize="sm">
+ <Text>Spesifikasi teknis belum tersedia untuk varian ID: <b>{selectedVariant?.id}</b></Text>
+ </Box>
+ )}
+ </Box>
</TabPanel>
{/* PANEL 3: DETAIL LAINNYA */}
diff --git a/src/pages/api/magento-product.ts b/src/pages/api/magento-product.ts
new file mode 100644
index 00000000..c906079e
--- /dev/null
+++ b/src/pages/api/magento-product.ts
@@ -0,0 +1,114 @@
+import type { NextApiRequest, NextApiResponse } from 'next';
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse
+) {
+ const { sku } = req.query;
+
+ if (!sku) {
+ return res.status(400).json({ error: 'SKU is required' });
+ }
+
+ // Token Magento
+ const token = 'vxrtcjvztv1icgjzsui45de9kmwlz0lf';
+ const baseUrl = 'https://pimdev.1211.my.id/rest/V1/products';
+
+ try {
+ // 1. Pastikan SKU menjadi string dan hapus spasi kiri/kanan
+ const cleanSku = String(sku).trim();
+
+ // 2. Encode SKU
+ const encodedSku = encodeURIComponent(cleanSku);
+
+ // 3. Bentuk URL Final
+ const finalUrl = `${baseUrl}/${encodedSku}`;
+
+ // --- DEBUGGING LOG ---
+ console.log('Fetching URL:', finalUrl);
+
+ // Request ke Product Endpoint
+ const response = await fetch(finalUrl, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`,
+ },
+ });
+
+ if (!response.ok) {
+ console.error(`Magento Error: ${response.status} ${response.statusText}`);
+ return res.status(response.status).json({
+ error: 'Failed to fetch from Magento',
+ magentoStatus: response.status,
+ checkedUrl: finalUrl
+ });
+ }
+
+ const data = await response.json();
+
+ // =====================================================================
+ // TAMBAHAN: FETCH LABEL ATRIBUT (z_*)
+ // =====================================================================
+
+ let specsWithLabels: any[] = [];
+
+ // Cek apakah ada custom_attributes
+ if (data.custom_attributes) {
+ // Filter atribut yang kodenya dimulai dengan 'z'
+ const zAttributes = data.custom_attributes.filter((attr: any) =>
+ attr.attribute_code.startsWith('z')
+ );
+
+ // Fetch detail label untuk setiap atribut secara paralel
+ specsWithLabels = await Promise.all(
+ zAttributes.map(async (attr: any) => {
+ try {
+ // Endpoint untuk ambil detail atribut (Label): /V1/products/attributes/{attributeCode}
+ const attrUrl = `${baseUrl}/attributes/${attr.attribute_code}`;
+
+ const attrRes = await fetch(attrUrl, {
+ method: 'GET',
+ headers: {
+ 'Authorization': `Bearer ${token}`,
+ 'Content-Type': 'application/json',
+ }
+ });
+
+ if (attrRes.ok) {
+ const attrData = await attrRes.json();
+ // AMBIL NILAI 'default_frontend_label'
+ return {
+ label: attrData.default_frontend_label || attr.attribute_code,
+ value: attr.value
+ };
+ }
+ } catch (err) {
+ console.error(`Failed to fetch label for ${attr.attribute_code}`);
+ }
+
+ // Fallback: Jika gagal ambil label, format manual dari kode
+ const fallbackLabel = attr.attribute_code
+ .substring(1).replace(/_/g, ' ').trim(); // z_size_ml -> size ml
+
+ return {
+ label: fallbackLabel,
+ value: attr.value
+ };
+ })
+ );
+ }
+
+ // Gabungkan data asli dengan data specs yang sudah ada labelnya
+ const responseData = {
+ ...data,
+ specs: specsWithLabels // Frontend tinggal pakai field ini
+ };
+
+ res.status(200).json(responseData);
+
+ } catch (error) {
+ console.error('Proxy Server Error:', error);
+ res.status(500).json({ error: 'Internal Server Error' });
+ }
+} \ No newline at end of file