From 3eb4dc4bbabb093ca9fb1bef4f45d0b13bd9a139 Mon Sep 17 00:00:00 2001 From: Joseph Gimba <86230531+Joewizy@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:02:11 +0000 Subject: [PATCH 1/2] feat: implement product verification --- frontend/.env.example | 3 ++ frontend/src/app/api/product/[productId].ts | 46 +++++++++++++++++++++ frontend/src/components/Scan.tsx | 20 ++++++++- frontend/src/services/apiService.ts | 13 ++++++ 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 frontend/src/app/api/product/[productId].ts diff --git a/frontend/.env.example b/frontend/.env.example index 2939b25..4c6aa57 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -4,3 +4,6 @@ RPC_URL= DATABASE_URL= +BACKEND_BASE_URL=http://localhost:3000/api + + diff --git a/frontend/src/app/api/product/[productId].ts b/frontend/src/app/api/product/[productId].ts new file mode 100644 index 0000000..e923280 --- /dev/null +++ b/frontend/src/app/api/product/[productId].ts @@ -0,0 +1,46 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; + +type ProductDetails = { + product_id: string; + name: string; + image: string; + manufacturer: string; + manufactureDate: string; + expiryDate: string; +}; + +type ErrorResponse = { + error: string; +}; + +const BASE_URL = process.env.BACKEND_BASE_URL || 'http://localhost:3000'; // Update to match your backend URL + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const { productId } = req.query; + + if (req.method !== 'GET') { + return res.status(405).json({ error: 'Method Not Allowed' }); + } + + if (!productId || typeof productId !== 'string') { + return res.status(400).json({ error: 'Invalid or missing productId' }); + } + + try { + const response = await fetch(`${BASE_URL}/products/${productId}`); + + if (!response.ok) { + const errorBody = await response.json(); + return res.status(response.status).json({ error: errorBody.error || 'Failed to fetch product details' }); + } + + const productDetails: ProductDetails = await response.json(); + return res.status(200).json(productDetails); + } catch (error: any) { + console.error('Error fetching product details:', error.message); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/frontend/src/components/Scan.tsx b/frontend/src/components/Scan.tsx index 7c0c2ff..ee28176 100644 --- a/frontend/src/components/Scan.tsx +++ b/frontend/src/components/Scan.tsx @@ -6,7 +6,7 @@ import { StarIcon2, } from '@/assets/icons'; import { CONTRACT_ADDR, formatDate, formatIpfsHash } from '@/lib/config'; -import { fetchIpfsFile } from '@/services/apiService'; +import { fetchIpfsFile, fetchProductDetails } from '@/services/apiService'; import { useAccount, useReadContract } from '@starknet-react/core'; import React, { useEffect, useMemo, useState } from 'react'; import { useParams } from 'next/navigation'; @@ -22,6 +22,7 @@ type ProductProps = { }; export default function ScanProduct() { + const [productId, setProductId] = useState(""); const [product, setProduct] = useState(); const [contractData, setContractData] = useState<{ product_id: string; @@ -34,13 +35,28 @@ export default function ScanProduct() { const { address } = useAccount(); let payload = params?.product; + useEffect(() => { + const fetchData = async () => { + try { + if (payload) { + const product = await fetchProductDetails(payload.productId); + setProductId(product.productId); + } + } catch (e) { + console.error('Error fetching from Product:', e); + } + }; + + fetchData(); + }, [payload]); + const toggleUserModal = () => { setOpenWallet((prev) => !prev); }; const { data } = useReadContract({ functionName: 'verify', - args: [payload.toString()], + args: [productId], abi, address: CONTRACT_ADDR, watch: true, diff --git a/frontend/src/services/apiService.ts b/frontend/src/services/apiService.ts index fc7afca..2f729bd 100644 --- a/frontend/src/services/apiService.ts +++ b/frontend/src/services/apiService.ts @@ -42,3 +42,16 @@ export const handleFlagProduct = async function ( return await response.json(); }; + +export const fetchProductDetails = async (productId: string) => { + try { + const response = await fetch(`/api/product/${productId}`); + if (!response.ok) { + throw new Error(await response.text()); + } + return await response.json(); + } catch (error) { + console.error('Error fetching product details:', error.message); + throw error; + } +}; From d6431452bb710425e2334afa666a1ecd830b79d1 Mon Sep 17 00:00:00 2001 From: Joseph Gimba <86230531+Joewizy@users.noreply.github.com> Date: Sat, 30 Nov 2024 14:07:16 +0000 Subject: [PATCH 2/2] chore: run prettier fix --- frontend/src/app/api/product/[productId].ts | 16 +++++++++------- frontend/src/components/Scan.tsx | 4 ++-- frontend/src/services/apiService.ts | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/api/product/[productId].ts b/frontend/src/app/api/product/[productId].ts index e923280..f383984 100644 --- a/frontend/src/app/api/product/[productId].ts +++ b/frontend/src/app/api/product/[productId].ts @@ -1,12 +1,12 @@ import type { NextApiRequest, NextApiResponse } from 'next'; type ProductDetails = { - product_id: string; - name: string; - image: string; - manufacturer: string; - manufactureDate: string; - expiryDate: string; + product_id: string; + name: string; + image: string; + manufacturer: string; + manufactureDate: string; + expiryDate: string; }; type ErrorResponse = { @@ -34,7 +34,9 @@ export default async function handler( if (!response.ok) { const errorBody = await response.json(); - return res.status(response.status).json({ error: errorBody.error || 'Failed to fetch product details' }); + return res + .status(response.status) + .json({ error: errorBody.error || 'Failed to fetch product details' }); } const productDetails: ProductDetails = await response.json(); diff --git a/frontend/src/components/Scan.tsx b/frontend/src/components/Scan.tsx index ee28176..ff38ba3 100644 --- a/frontend/src/components/Scan.tsx +++ b/frontend/src/components/Scan.tsx @@ -22,7 +22,7 @@ type ProductProps = { }; export default function ScanProduct() { - const [productId, setProductId] = useState(""); + const [productId, setProductId] = useState(''); const [product, setProduct] = useState(); const [contractData, setContractData] = useState<{ product_id: string; @@ -39,7 +39,7 @@ export default function ScanProduct() { const fetchData = async () => { try { if (payload) { - const product = await fetchProductDetails(payload.productId); + const product = await fetchProductDetails(payload[0]); setProductId(product.productId); } } catch (e) { diff --git a/frontend/src/services/apiService.ts b/frontend/src/services/apiService.ts index 2f729bd..a61b39e 100644 --- a/frontend/src/services/apiService.ts +++ b/frontend/src/services/apiService.ts @@ -51,7 +51,7 @@ export const fetchProductDetails = async (productId: string) => { } return await response.json(); } catch (error) { - console.error('Error fetching product details:', error.message); + console.error('Error fetching product details:', error); throw error; } };